package main import ( "encoding/json" "fmt" "net" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" ) const ( PluginName = "mycelium-cni" MyceliumInterface = "mycelium" ) type NetConf struct { types.NetConf MyceliumInterface string `json:"myceliumInterface,omitempty"` } func main() { skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, "mycelium CNI plugin") } func cmdAdd(args *skel.CmdArgs) error { conf, err := parseConfig(args.StdinData) if err != nil { return err } // Get container network namespace containerNS, err := netns.GetFromPath(args.Netns) if err != nil { return fmt.Errorf("failed to open container netns %q: %v", args.Netns, err) } defer containerNS.Close() // Get Mycelium interface and its IPv6 prefix myceliumIP, err := getMyceliumIP(conf.MyceliumInterface) if err != nil { return fmt.Errorf("failed to get Mycelium IP: %v", err) } // Create veth pair hostVethName := fmt.Sprintf("veth-%s", args.ContainerID[:8]) containerVethName := "eth0" hostVeth, containerVeth, err := createVethPair(hostVethName, containerVethName) if err != nil { return fmt.Errorf("failed to create veth pair: %v", err) } // Move container veth to container namespace if err := netlink.LinkSetNsFd(containerVeth, int(containerNS)); err != nil { return fmt.Errorf("failed to move veth to container netns: %v", err) } // Configure container interface containerIP := generateContainerIP(myceliumIP, args.ContainerID) if err := configureContainerInterface(containerNS, containerVethName, containerIP, hostVethName); err != nil { return fmt.Errorf("failed to configure container interface: %v", err) } // Configure host interface and routing if err := configureHostInterface(hostVeth, containerIP); err != nil { return fmt.Errorf("failed to configure host interface: %v", err) } // Build result result := ¤t.Result{ CNIVersion: current.ImplementedSpecVersion, Interfaces: []*current.Interface{ { Name: hostVethName, }, { Name: containerVethName, Sandbox: args.Netns, }, }, IPs: []*current.IPConfig{ { Interface: current.Int(1), // container interface Address: net.IPNet{IP: containerIP, Mask: net.CIDRMask(64, 128)}, }, }, } return types.PrintResult(result, conf.CNIVersion) } func cmdCheck(args *skel.CmdArgs) error { // Basic connectivity check could be implemented here return nil } func cmdDel(args *skel.CmdArgs) error { // Clean up veth pair (host side will be automatically removed) hostVethName := fmt.Sprintf("veth-%s", args.ContainerID[:8]) link, err := netlink.LinkByName(hostVethName) if err != nil { // Interface might already be gone, which is fine return nil } return netlink.LinkDel(link) } func parseConfig(stdin []byte) (*NetConf, error) { conf := &NetConf{ MyceliumInterface: MyceliumInterface, } if err := json.Unmarshal(stdin, conf); err != nil { return nil, fmt.Errorf("failed to parse network configuration: %v", err) } return conf, nil } func getMyceliumIP(interfaceName string) (net.IP, error) { link, err := netlink.LinkByName(interfaceName) if err != nil { return nil, fmt.Errorf("failed to find interface %s: %v", interfaceName, err) } addrs, err := netlink.AddrList(link, netlink.FAMILY_V6) if err != nil { return nil, fmt.Errorf("failed to get addresses for %s: %v", interfaceName, err) } for _, addr := range addrs { if addr.IP.IsGlobalUnicast() { // Return the /64 prefix return addr.IP.Mask(net.CIDRMask(64, 128)), nil } } return nil, fmt.Errorf("no global IPv6 address found on %s", interfaceName) } func generateContainerIP(myceliumPrefix net.IP, containerID string) net.IP { // Generate a container IP within the /64 prefix // Using simple approach: prefix + ::1 (could be made more sophisticated) containerIP := make(net.IP, len(myceliumPrefix)) copy(containerIP, myceliumPrefix) containerIP[15] = 1 // Set last byte to 1 return containerIP } func createVethPair(hostName, containerName string) (netlink.Link, netlink.Link, error) { veth := &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{Name: hostName}, PeerName: containerName, } if err := netlink.LinkAdd(veth); err != nil { return nil, nil, err } hostVeth, err := netlink.LinkByName(hostName) if err != nil { return nil, nil, err } containerVeth, err := netlink.LinkByName(containerName) if err != nil { return nil, nil, err } // Bring up host side if err := netlink.LinkSetUp(hostVeth); err != nil { return nil, nil, err } return hostVeth, containerVeth, nil } func configureContainerInterface(containerNS netns.NsHandle, ifName string, containerIP net.IP, hostVethName string) error { // Switch to container namespace originalNS, err := netns.Get() if err != nil { return err } defer originalNS.Close() defer netns.Set(originalNS) if err := netns.Set(containerNS); err != nil { return err } // Get the interface link, err := netlink.LinkByName(ifName) if err != nil { return err } // Bring interface up if err := netlink.LinkSetUp(link); err != nil { return err } // Add IP address addr := &netlink.Addr{ IPNet: &net.IPNet{ IP: containerIP, Mask: net.CIDRMask(64, 128), }, } if err := netlink.AddrAdd(link, addr); err != nil { return err } // Switch to main namespace to get host veth address if err := netns.Set(originalNS); err != nil { return err } // Get host veth link-local address hostVeth, err := netlink.LinkByName(hostVethName) if err != nil { return err } hostAddrs, err := netlink.AddrList(hostVeth, netlink.FAMILY_V6) if err != nil { return err } var hostLLAddr net.IP for _, addr := range hostAddrs { if addr.IP.IsLinkLocalUnicast() { hostLLAddr = addr.IP break } } // Switch back to container namespace to add route if err := netns.Set(containerNS); err != nil { return err } if hostLLAddr != nil { // Add route to Mycelium network via host veth route := &netlink.Route{ Dst: &net.IPNet{ IP: net.ParseIP("400::"), Mask: net.CIDRMask(7, 128), }, Gw: hostLLAddr, LinkIndex: link.Attrs().Index, } if err := netlink.RouteAdd(route); err != nil { return err } } return nil } func configureHostInterface(hostVeth netlink.Link, containerIP net.IP) error { // Add route to container IP via host veth route := &netlink.Route{ Dst: &net.IPNet{ IP: containerIP, Mask: net.CIDRMask(128, 128), }, LinkIndex: hostVeth.Attrs().Index, } return netlink.RouteAdd(route) }