diff options
Diffstat (limited to 'internal/namespace/pinning.go')
| -rw-r--r-- | internal/namespace/pinning.go | 30 |
1 files changed, 22 insertions, 8 deletions
diff --git a/internal/namespace/pinning.go b/internal/namespace/pinning.go index a522f17..e257187 100644 --- a/internal/namespace/pinning.go +++ b/internal/namespace/pinning.go @@ -9,9 +9,11 @@ import ( "strconv" "git.theodohertyfamily.com/tools/wg-wrap/internal/paths" + "golang.org/x/sys/unix" ) -// PinNamespace touches the namespace path to indicate it is pinned/active. +// PinNamespace binds the current network namespace to the profile's namespace path. +// This prevents the kernel from destroying the namespace when all processes exit. func PinNamespace(pm *paths.PathManager, profile string) error { nsPath := GetProfileNamespacePath(pm, profile) profilesDir := filepath.Dir(nsPath) @@ -19,15 +21,21 @@ func PinNamespace(pm *paths.PathManager, profile string) error { return fmt.Errorf("failed to create profiles directory: %w", err) } - // We write a placeholder file to indicate the profile namespace is pinned. - if err := os.WriteFile(nsPath, []byte("active"), 0644); err != nil { + // 1. Create an empty file to serve as the mount point + if err := os.WriteFile(nsPath, []byte(""), 0644); err != nil { return fmt.Errorf("failed to create namespace pin file: %w", err) } + + // 2. Bind-mount the current network namespace to the file. + // This increments the kernel's reference count for the namespace. + if err := unix.Mount("/proc/self/ns/net", nsPath, "", unix.MS_BIND, ""); err != nil { + return fmt.Errorf("failed to bind-mount network namespace: %w", err) + } + return nil } -// UnpinNamespace removes the pinned namespace file from the filesystem. -// This allows the namespace to be destroyed once the last process exits. +// UnpinNamespace unmounts and removes the pinned namespace file. func UnpinNamespace(pm *paths.PathManager, profile string) error { nsPath := GetProfileNamespacePath(pm, profile) @@ -35,13 +43,19 @@ func UnpinNamespace(pm *paths.PathManager, profile string) error { return nil } - pidsDir := GetPidsDirPath(pm, profile) + // 1. Unmount the namespace first. + // If this is the last reference to the namespace, the kernel will destroy it. + if err := unix.Unmount(nsPath, 0); err != nil { + return fmt.Errorf("failed to unmount namespace %s: %w", nsPath, err) + } - // Unlink the namespace file + // 2. Remove the mount point file. if err := os.Remove(nsPath); err != nil { - return fmt.Errorf("failed to unpin namespace %s: %w", nsPath, err) + return fmt.Errorf("failed to remove pin file %s: %w", nsPath, err) } + pidsDir := GetPidsDirPath(pm, profile) + // Try to remove pids directory and empty parent directories _ = os.Remove(pidsDir) _ = os.Remove(filepath.Dir(pidsDir)) |
