package cni import ( "fmt" "net" "runtime" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" ) // createVethPair creates a veth pair with one end in the host namespace // and the other moved into the pod's network namespace. // Returns (hostVethName, podVethName, error). func createVethPair(podNetns string, ifName string) (string, string, error) { hostVethName := "kedge_h_" + ifName[:min(5, len(ifName))] veth := &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{Name: hostVethName}, PeerName: ifName, } if err := netlink.LinkAdd(veth); err != nil { return "", "", fmt.Errorf("failed to create veth pair: %w", err) } // Get a handle to the pod's network namespace. podNS, err := netns.GetFromPath(podNetns) if err != nil { netlink.LinkDel(veth) return "", "", fmt.Errorf("failed to get pod netns: %w", err) } defer podNS.Close() // Move the peer end into the pod namespace. peer, err := netlink.LinkByName(ifName) if err != nil { netlink.LinkDel(veth) return "", "", fmt.Errorf("failed to find peer veth: %w", err) } if err := netlink.LinkSetNsFd(peer, int(podNS)); err != nil { netlink.LinkDel(veth) return "", "", fmt.Errorf("failed to move peer to pod netns: %w", err) } // Bring up the host-side veth. hostVeth, err := netlink.LinkByName(hostVethName) if err != nil { return "", "", fmt.Errorf("failed to find host veth: %w", err) } if err := netlink.LinkSetUp(hostVeth); err != nil { return "", "", fmt.Errorf("failed to bring up host veth: %w", err) } // Bring up the pod-side veth inside the namespace. if err := inNamespace(podNS, func() error { link, err := netlink.LinkByName(ifName) if err != nil { return err } return netlink.LinkSetUp(link) }); err != nil { return "", "", fmt.Errorf("failed to bring up pod veth: %w", err) } return hostVethName, ifName, nil } // teardownInterface removes the named interface from the pod's network namespace. func teardownInterface(podNetns string, ifName string) error { ns, err := netns.GetFromPath(podNetns) if err != nil { // Namespace may already be gone during cleanup. return nil } defer ns.Close() return inNamespace(ns, func() error { link, err := netlink.LinkByName(ifName) if err != nil { return nil // Already gone. } return netlink.LinkDel(link) }) } // verifyInterface checks that the named interface exists in the pod's network namespace. func verifyInterface(podNetns string, ifName string) error { ns, err := netns.GetFromPath(podNetns) if err != nil { return fmt.Errorf("failed to get pod netns: %w", err) } defer ns.Close() return inNamespace(ns, func() error { link, err := netlink.LinkByName(ifName) if err != nil { return fmt.Errorf("interface %s not found: %w", ifName, err) } if link.Attrs().Flags&net.FlagUp == 0 { return fmt.Errorf("interface %s is down", ifName) } return nil }) } // inNamespace executes fn inside the given network namespace, restoring // the original namespace afterward. func inNamespace(ns netns.NsHandle, fn func() error) error { runtime.LockOSThread() defer runtime.UnlockOSThread() origNS, err := netns.Get() if err != nil { return fmt.Errorf("failed to get current netns: %w", err) } defer origNS.Close() if err := netns.Set(ns); err != nil { return fmt.Errorf("failed to set netns: %w", err) } defer netns.Set(origNS) //nolint:errcheck return fn() }