Skip to content

netkit: dae0 veth + /run/netns/daens left behind after clean SIGTERM stop (fast exit skips netns teardown) #1084

Closed

Description

@itoywh

Problem

In netkit mode, systemctl stop dae (clean SIGTERM) leaves the dae0 interface and the named netns /run/netns/daens behind. dae exits cleanly, but does not tear down its own netkit interface / netns. This leaks kernel state after every stop, and can break a subsequent systemctl restart dae in edge cases (peer interface stuck in a half-dead netns).

Environment

  • dae: current main (identical code path on the kdae fork)
  • OS: Debian (LXC container on Proxmox VE), kernel 7.0.14-12-pve
  • Mode: netkit (auto-selected on kernel >= 6.7)

Reproduction

  1. Start dae in netkit mode.
  2. systemctl stop dae (SIGTERM).
  3. Inspect leftover state:
# ip link show dae0
18: dae0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP> ... link-netns daens   # still UP

# ls /run/netns/daens
/run/netns/daens                                                        # still present
  1. journal confirms a clean exit (not SIGKILL, no timeout):
Aug 21 01:09:45 debian systemd[1]: dae.service: Deactivated successfully.
Aug 21 01:09:45 debian systemd[1]: Stopped dae.service - dae Service.

dae received SIGTERM and exited normally, yet dae0 and /run/netns/daens were not torn down.

Root cause

cmd/run.go shutdownAfterSignalWithHandoff():

if fastExit {
    log.Infoln("[Shutdown] Fast exit enabled; skipping in-process netns and control-plane teardown. Residual kernel state will be purged on next startup.")
    return nil        // returns BEFORE netns.Close()
}

if netns != nil {
    if e := netns.Close(); e != nil {   // would delete dae0 + /run/netns/daens
        log.Warnf("close dae netns: %v", e)
    }
}

fastExit is set to true in the SIGTERM/SIGINT/SIGQUIT handler, so every normal systemctl stop skips netns.Close(). That Close() (in control/netns_utils.go) is the only place calling DeleteLink(HostVethName) + DeleteNamedNetns(NsName) (i.e. dae0 + /run/netns/daens).

The residue is normally purged on the next startup (tryCreateNetkit deletes an existing dae0, setupNetns deletes an existing /run/netns/daens), which is why restart often still works. But a stop should not leave a live netkit interface + named netns behind.

Proposed fix

Tear down the netns unconditionally, before the fastExit early return. netns teardown is cheap (delete a link, delete a named netns, close two fds); only the expensive control-plane teardown (aborting connections, closing dialers) should be skipped on fast exit:

// Always tear down the netns first — it is cheap and leaves kernel
// state (dae0 + /run/netns/daens) behind if skipped.
if netns != nil {
    if e := netns.Close(); e != nil {
        log.Warnf("close dae netns: %v", e)
    }
}

if fastExit {
    log.Infoln("[Shutdown] Fast exit enabled; skipping in-process control-plane teardown.")
    return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions