package cni import ( "fmt" "net" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" "github.com/guildhouse-co/kedge/internal/topology" ) // programPodRoutes programs routes inside the pod's network namespace for // both overlay and underlay destinations. func programPodRoutes(podNetnsPath string, ifName string, overlay, underlay []SubnetRoute) error { ns, err := netns.GetFromPath(podNetnsPath) 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) } allRoutes := append(overlay, underlay...) for _, sr := range allRoutes { _, dst, err := net.ParseCIDR(sr.Dst) if err != nil { return fmt.Errorf("invalid CIDR %s: %w", sr.Dst, err) } route := &netlink.Route{ LinkIndex: link.Attrs().Index, Dst: dst, } if err := netlink.RouteAdd(route); err != nil { return fmt.Errorf("failed to add route %s: %w", sr.Dst, err) } } return nil }) } // verifyRoutes checks that expected routes exist in the pod's network namespace. func verifyRoutes(podNetnsPath string, overlay, underlay []SubnetRoute) error { ns, err := netns.GetFromPath(podNetnsPath) if err != nil { return fmt.Errorf("failed to get pod netns: %w", err) } defer ns.Close() return inNamespace(ns, func() error { routes, err := netlink.RouteList(nil, netlink.FAMILY_V4) if err != nil { return fmt.Errorf("failed to list routes: %w", err) } routeMap := make(map[string]bool) for _, r := range routes { if r.Dst != nil { routeMap[r.Dst.String()] = true } } allExpected := append(overlay, underlay...) for _, sr := range allExpected { if !routeMap[sr.Dst] { return fmt.Errorf("missing route: %s", sr.Dst) } } return nil }) } // loadMeshTopology reads the mesh topology file written by the DaemonSet. func loadMeshTopology(meshConfigPath string) (*topology.MeshTopology, error) { if meshConfigPath == "" { meshConfigPath = "/etc/kedge/mesh.json" } return topology.LoadFromFile(meshConfigPath) }