func cmdAdd(args *skel.CmdArgs) error { tuningConf := TuningConf{} if err := json.Unmarshal(args.StdinData, &tuningConf); err != nil { return fmt.Errorf("failed to load netconf: %v", err) } // The directory /proc/sys/net is per network namespace. Enter in the // network namespace before writing on it. err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error { for key, value := range tuningConf.SysCtl { fileName := filepath.Join("/proc/sys", strings.Replace(key, ".", "/", -1)) fileName = filepath.Clean(fileName) // Refuse to modify sysctl parameters that don't belong // to the network subsystem. if !strings.HasPrefix(fileName, "/proc/sys/net/") { return fmt.Errorf("invalid net sysctl key: %q", key) } content := []byte(value) err := ioutil.WriteFile(fileName, content, 0644) if err != nil { return err } } return nil }) if err != nil { return err } result := types.Result{} return result.Print() }
func setupVeth(netns string, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool) error { var hostVethName string err := ns.WithNetNSPath(netns, false, func(hostNS *os.File) error { // create the veth pair in the container and move host end into host netns hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS) if err != nil { return err } hostVethName = hostVeth.Attrs().Name return nil }) if err != nil { return err } // need to lookup hostVeth again as its index has changed during ns move hostVeth, err := netlink.LinkByName(hostVethName) if err != nil { return fmt.Errorf("failed to lookup %q: %v", hostVethName, err) } // connect host veth end to the bridge if err = netlink.LinkSetMaster(hostVeth, br); err != nil { return fmt.Errorf("failed to connect %q to bridge %v: %v", hostVethName, br.Attrs().Name, err) } // set hairpin mode if err = netlink.LinkSetHairpin(hostVeth, hairpinMode); err != nil { return fmt.Errorf("failed to setup hairpin mode for %v: %v", hostVethName, err) } return nil }
func cmdDel(args *skel.CmdArgs) error { n, err := loadNetConf(args.StdinData) if err != nil { return err } if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil { return err } var ipn *net.IPNet err = ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error { var err error ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4) return err }) if err != nil { return err } if n.IPMasq { chain := utils.FormatChainName(n.Name, args.ContainerID) comment := utils.FormatComment(n.Name, args.ContainerID) if err = ip.TeardownIPMasq(ipn, chain, comment); err != nil { return err } } return nil }
func cmdDel(args *skel.CmdArgs) error { conf := NetConf{} if err := json.Unmarshal(args.StdinData, &conf); err != nil { return fmt.Errorf("failed to load netconf: %v", err) } if err := ipam.ExecDel(conf.IPAM.Type, args.StdinData); err != nil { return err } if args.Netns == "" { return nil } var ipn *net.IPNet err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error { var err error ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4) return err }) if err != nil { return err } if conf.IPMasq { chain := utils.FormatChainName(conf.Name, args.ContainerID) comment := utils.FormatComment(conf.Name, args.ContainerID) if err = ip.TeardownIPMasq(ipn, chain, comment); err != nil { return err } } return nil }
func cmdDel(args *skel.CmdArgs) error { c, err := cnc.NewDefaultClient() if err != nil { return fmt.Errorf("error while starting cilium-client: %s", err) } ep, err := c.EndpointGetByDockerID(args.ContainerID) if err != nil { return fmt.Errorf("error while retrieving endpoint from cilium daemon: %s", err) } if ep == nil { return fmt.Errorf("endpoint with container ID %s not found", args.ContainerID) } ipv6addr := ep.IPv6.IP() if err = c.ReleaseIP(ipam.CNIIPAMType, ipam.IPAMReq{IP: &ipv6addr}); err != nil { log.Warningf("failed to release allocated IPv6 of container ID %q: %s", args.ContainerID, err) } ipv4addr := ep.IPv4.IP() if ep.IPv4 != nil { if err = c.ReleaseIP(ipam.CNIIPAMType, ipam.IPAMReq{IP: &ipv4addr}); err != nil { log.Warningf("failed to release allocated IPv4 of container ID %q: %s", args.ContainerID, err) } } if err := c.EndpointLeave(ep.ID); err != nil { log.Warningf("leaving the endpoint failed: %s\n", err) } return ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error { return plugins.DelLinkByName(args.IfName) }) }
func cmdDel(args *skel.CmdArgs) error { n, err := loadConf(args.StdinData) if err != nil { return err } err = ipam.ExecDel(n.IPAM.Type, args.StdinData) if err != nil { return err } return ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error { return ip.DelLinkByName(args.IfName) }) }
// Adds a macvlan interface to a container for use with the egress router feature func addMacvlan(netns string) error { var defIface netlink.Link var err error // Find interface with the default route routes, err := netlink.RouteList(nil, netlink.FAMILY_V4) if err != nil { return fmt.Errorf("failed to read routes: %v", err) } for _, r := range routes { if r.Dst == nil { defIface, err = netlink.LinkByIndex(r.LinkIndex) if err != nil { return fmt.Errorf("failed to get default route interface: %v", err) } } } if defIface == nil { return fmt.Errorf("failed to find default route interface") } return ns.WithNetNSPath(netns, func(ns.NetNS) error { err := netlink.LinkAdd(&netlink.Macvlan{ LinkAttrs: netlink.LinkAttrs{ MTU: defIface.Attrs().MTU, Name: "macvlan0", ParentIndex: defIface.Attrs().Index, }, Mode: netlink.MACVLAN_MODE_PRIVATE, }) if err != nil { return fmt.Errorf("failed to create macvlan interface: %v", err) } l, err := netlink.LinkByName("macvlan0") if err != nil { return fmt.Errorf("failed to find macvlan interface: %v", err) } err = netlink.LinkSetUp(l) if err != nil { return fmt.Errorf("failed to set macvlan interface up: %v", err) } return nil }) }
func cmdDel(args *skel.CmdArgs) error { n, err := loadConf(args.StdinData) if err != nil { return err } err = ipam.ExecDel(n.IPAM.Type, args.StdinData) if err != nil { return err } if args.Netns == "" { return nil } return ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error { return ip.DelLinkByName(args.IfName) }) }
func cmdDel(args *skel.CmdArgs) error { args.IfName = "lo" // ignore config, this only works for loopback err := ns.WithNetNSPath(args.Netns, func(ns.NetNS) error { link, err := netlink.LinkByName(args.IfName) if err != nil { return err // not tested } err = netlink.LinkSetDown(link) if err != nil { return err // not tested } return nil }) if err != nil { return err // not tested } return nil }
// AcquireLease gets an DHCP lease and then maintains it in the background // by periodically renewing it. The acquired lease can be released by // calling DHCPLease.Stop() func AcquireLease(clientID, netns, ifName string) (*DHCPLease, error) { errCh := make(chan error, 1) l := &DHCPLease{ clientID: clientID, stop: make(chan struct{}), } log.Printf("%v: acquiring lease", clientID) l.wg.Add(1) go func() { errCh <- ns.WithNetNSPath(netns, func(_ ns.NetNS) error { defer l.wg.Done() link, err := netlink.LinkByName(ifName) if err != nil { return fmt.Errorf("error looking up %q: %v", ifName, err) } l.link = link if err = l.acquire(); err != nil { return err } log.Printf("%v: lease acquired, expiration is %v", l.clientID, l.expireTime) errCh <- nil l.maintain() return nil }) }() if err := <-errCh; err != nil { return nil, err } return l, nil }
func cmdAdd(args *skel.CmdArgs) error { args.IfName = "lo" // ignore config, this only works for loopback err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error { link, err := netlink.LinkByName(args.IfName) if err != nil { return err // not tested } err = netlink.LinkSetUp(link) if err != nil { return err // not tested } return nil }) if err != nil { return err // not tested } result := types.Result{} return result.Print() }
func cmdDel(args *skel.CmdArgs) error { conf := NetConf{} if err := json.Unmarshal(args.StdinData, &conf); err != nil { return fmt.Errorf("failed to load netconf: %v", err) } ConfigureLogging(conf.LogLevel) workload, orchestrator, err := GetIdentifiers(args) if err != nil { return err } logger := CreateContextLogger(workload) // Allow the hostname to be overridden by the network config if conf.Hostname != "" { hostname = conf.Hostname } logger.WithFields(log.Fields{ "Workload": workload, "Orchestrator": orchestrator, "Node": hostname, }).Info("Extracted identifiers") // Always try to release the address. Don't deal with any errors till the endpoints are cleaned up. fmt.Fprintf(os.Stderr, "Calico CNI releasing IP address\n") logger.WithFields(log.Fields{"paths": os.Getenv("CNI_PATH"), "type": conf.IPAM.Type}).Debug("Looking for IPAM plugin in paths") ipamErr := ipam.ExecDel(conf.IPAM.Type, args.StdinData) if ipamErr != nil { logger.Error(ipamErr) } calicoClient, err := CreateClient(conf) if err != nil { return err } if err := calicoClient.WorkloadEndpoints().Delete(api.WorkloadEndpointMetadata{ Name: args.IfName, Node: hostname, Orchestrator: orchestrator, Workload: workload}); err != nil { return err } // Only try to delete the device if a namespace was passed in. if args.Netns != "" { fmt.Fprintf(os.Stderr, "Calico CNI deleting device in netns %s\n", args.Netns) err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error { _, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4) return err }) if err != nil { return err } } // Return the IPAM error if there was one. The IPAM error will be lost if there was also an error in cleaning up // the device or endpoint, but crucially, the user will know the overall operation failed. return ipamErr }
func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string, error) { // The IPAM result will be something like IP=192.168.3.5/24, GW=192.168.3.1. // What we want is really a point-to-point link but veth does not support IFF_POINTOPONT. // Next best thing would be to let it ARP but set interface to 192.168.3.5/32 and // add a route like "192.168.3.0/24 via 192.168.3.1 dev $ifName". // Unfortunately that won't work as the GW will be outside the interface's subnet. // Our solution is to configure the interface with 192.168.3.5/24, then delete the // "192.168.3.0/24 dev $ifName" route that was automatically added. Then we add // "192.168.3.1/32 dev $ifName" and "192.168.3.0/24 via 192.168.3.1 dev $ifName". // In other words we force all traffic to ARP via the gateway except for GW itself. var hostVethName string err := ns.WithNetNSPath(netns, func(hostNS ns.NetNS) error { hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS) if err != nil { return err } hostNS.Do(func(_ ns.NetNS) error { hostVethName = hostVeth.Attrs().Name if err := ip.SetHWAddrByIP(hostVethName, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { return fmt.Errorf("failed to set hardware addr by IP: %v", err) } return nil }) if err = ipam.ConfigureIface(ifName, pr); err != nil { return err } contVeth, err := netlink.LinkByName(ifName) if err != nil { return fmt.Errorf("failed to look up %q: %v", ifName, err) } if err := ip.SetHWAddrByIP(contVeth.Attrs().Name, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil { return fmt.Errorf("failed to set hardware addr by IP: %v", err) } // Delete the route that was automatically added route := netlink.Route{ LinkIndex: contVeth.Attrs().Index, Dst: &net.IPNet{ IP: pr.IP4.IP.IP.Mask(pr.IP4.IP.Mask), Mask: pr.IP4.IP.Mask, }, Scope: netlink.SCOPE_NOWHERE, } if err := netlink.RouteDel(&route); err != nil { return fmt.Errorf("failed to delete route %v: %v", route, err) } for _, r := range []netlink.Route{ netlink.Route{ LinkIndex: contVeth.Attrs().Index, Dst: &net.IPNet{ IP: pr.IP4.Gateway, Mask: net.CIDRMask(32, 32), }, Scope: netlink.SCOPE_LINK, Src: pr.IP4.IP.IP, }, netlink.Route{ LinkIndex: contVeth.Attrs().Index, Dst: &net.IPNet{ IP: pr.IP4.IP.IP.Mask(pr.IP4.IP.Mask), Mask: pr.IP4.IP.Mask, }, Scope: netlink.SCOPE_UNIVERSE, Gw: pr.IP4.Gateway, Src: pr.IP4.IP.IP, }, } { if err := netlink.RouteAdd(&r); err != nil { return fmt.Errorf("failed to add route %v: %v", r, err) } } return nil }) return hostVethName, err }
// DoNetworking performs the networking for the given config and IPAM result func DoNetworking(args *skel.CmdArgs, conf NetConf, res *types.Result, logger *log.Entry, desiredVethName string) (hostVethName, contVethMAC string, err error) { // Select the first 11 characters of the containerID for the host veth. hostVethName = "cali" + args.ContainerID[:min(11, len(args.ContainerID))] contVethName := args.IfName // If a desired veth name was passed in, use that instead. if desiredVethName != "" { hostVethName = desiredVethName } err = ns.WithNetNSPath(args.Netns, func(hostNS ns.NetNS) error { veth := &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{ Name: contVethName, Flags: net.FlagUp, MTU: conf.MTU, }, PeerName: hostVethName, } if err := netlink.LinkAdd(veth); err != nil { logger.Errorf("Error adding veth %+v: %s", veth, err) return err } hostVeth, err := netlink.LinkByName(hostVethName) if err != nil { err = fmt.Errorf("failed to lookup %q: %v", hostVethName, err) return err } contVeth, err := netlink.LinkByName(contVethName) if err != nil { err = fmt.Errorf("failed to lookup %q: %v", contVethName, err) return err } // Fetch the MAC from the container Veth. This is needed by Calico. contVethMAC = contVeth.Attrs().HardwareAddr.String() logger.WithField("MAC", contVethMAC).Debug("Found MAC for container veth") // At this point, the virtual ethernet pair has been created, and both ends have the right names. // Both ends of the veth are still in the container's network namespace. // Before returning, create the routes inside the namespace, first for IPv4 then IPv6. if res.IP4 != nil { // Add a connected route to a dummy next hop so that a default route can be set gw := net.IPv4(169, 254, 1, 1) gwNet := &net.IPNet{IP: gw, Mask: net.CIDRMask(32, 32)} if err = netlink.RouteAdd(&netlink.Route{ LinkIndex: contVeth.Attrs().Index, Scope: netlink.SCOPE_LINK, Dst: gwNet}); err != nil { return fmt.Errorf("failed to add route %v", err) } if err = ip.AddDefaultRoute(gw, contVeth); err != nil { return fmt.Errorf("failed to add route %v", err) } if err = netlink.AddrAdd(contVeth, &netlink.Addr{IPNet: &res.IP4.IP}); err != nil { return fmt.Errorf("failed to add IP addr to %q: %v", contVethName, err) } } // Handle IPv6 routes if res.IP6 != nil { // No need to add a dummy next hop route as the host veth device will already have an IPv6 // link local address that can be used as a next hop. // Just fetch the address of the host end of the veth and use it as the next hop. var hostIPv6Addr net.IP if err := hostNS.Do(func(_ ns.NetNS) error { addresses, err := netlink.AddrList(hostVeth, netlink.FAMILY_V6) if err != nil { logger.Errorf("Error listing IPv6 addresses: %s", err) return err } if len(addresses) < 1 { // If the hostVeth doesn't have an IPv6 address then this host probably doesn't // support IPv6. Since a IPv6 address has been allocated that can't be used, // return an error. return fmt.Errorf("Failed to get IPv6 addresses for container veth") } hostIPv6Addr = addresses[0].IP return nil }); err != nil { logger.Errorf("Error getting IPv6 address: %s", err) return err } _, defNet, _ := net.ParseCIDR("::/0") if err = ip.AddRoute(defNet, hostIPv6Addr, contVeth); err != nil { return fmt.Errorf("failed to add default gateway to %v %v", hostIPv6Addr, err) } if err = netlink.AddrAdd(contVeth, &netlink.Addr{IPNet: &res.IP6.IP}); err != nil { return fmt.Errorf("failed to add IP addr to %q: %v", contVeth, err) } } // Now that the everything has been successfully set up in the container, move the "host" end of the // veth into the host namespace. if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil { return fmt.Errorf("failed to move veth to host netns: %v", err) } return nil }) if err != nil { logger.Errorf("Error creating veth: %s", err) return "", "", err } // Moving a veth between namespaces always leaves it in the "DOWN" state. Set it back to "UP" now that we're // back in the host namespace. hostVeth, err := netlink.LinkByName(hostVethName) if err != nil { return "", "", fmt.Errorf("failed to lookup %q: %v", hostVethName, err) } if err = netlink.LinkSetUp(hostVeth); err != nil { return "", "", fmt.Errorf("failed to set %q up: %v", hostVethName, err) } return hostVethName, contVethMAC, err }
// Set up all networking (host/container veth, OVS flows, IPAM, loopback, etc) func (m *podManager) setup(req *cniserver.PodRequest) (*cnitypes.Result, *kubehostport.RunningPod, error) { podConfig, pod, err := m.getPodConfig(req) if err != nil { return nil, nil, err } ipamResult, err := m.ipamAdd(req.Netns, req.ContainerId) if err != nil { // TODO: Remove this hack once we've figured out how to retrieve the netns // of an exited container. Currently, restarting docker will leak a bunch of // ips. This will exhaust available ip space unless we cleanup old ips. At the // same time we don't want to try GC'ing them periodically as that could lead // to a performance regression in starting pods. So on each setup failure, try // GC on the assumption that the kubelet is going to retry pod creation, and // when it does, there will be ips. m.ipamGarbageCollection() return nil, nil, fmt.Errorf("failed to run IPAM for %v: %v", req.ContainerId, err) } podIP := ipamResult.IP4.IP.IP // Release any IPAM allocations and hostports if the setup failed var success bool defer func() { if !success { m.ipamDel(req.ContainerId) if err := m.hostportHandler.SyncHostports(TUN, m.getRunningPods()); err != nil { glog.Warningf("failed syncing hostports: %v", err) } } }() // Open any hostports the pod wants newPod := &kubehostport.RunningPod{Pod: pod, IP: podIP} if err := m.hostportHandler.OpenPodHostportsAndSync(newPod, TUN, m.getRunningPods()); err != nil { return nil, nil, err } var hostVeth, contVeth netlink.Link err = ns.WithNetNSPath(req.Netns, func(hostNS ns.NetNS) error { hostVeth, contVeth, err = ip.SetupVeth(podInterfaceName, int(m.mtu), hostNS) if err != nil { return fmt.Errorf("failed to create container veth: %v", err) } // refetch to get hardware address and other properties contVeth, err = netlink.LinkByIndex(contVeth.Attrs().Index) if err != nil { return fmt.Errorf("failed to fetch container veth: %v", err) } // Clear out gateway to prevent ConfigureIface from adding the cluster // subnet via the gateway ipamResult.IP4.Gateway = nil if err = ipam.ConfigureIface(podInterfaceName, ipamResult); err != nil { return fmt.Errorf("failed to configure container IPAM: %v", err) } lo, err := netlink.LinkByName("lo") if err == nil { err = netlink.LinkSetUp(lo) } if err != nil { return fmt.Errorf("failed to configure container loopback: %v", err) } return nil }) if err != nil { return nil, nil, err } if podConfig.wantMacvlan { if err := addMacvlan(req.Netns); err != nil { return nil, nil, err } } contVethMac := contVeth.Attrs().HardwareAddr.String() vnidStr := vnidToString(podConfig.vnid) out, err := exec.Command(sdnScript, setUpCmd, hostVeth.Attrs().Name, contVethMac, podIP.String(), vnidStr, podConfig.ingressBandwidth, podConfig.egressBandwidth).CombinedOutput() glog.V(5).Infof("SetUpPod network plugin output: %s, %v", string(out), err) if isScriptError(err) { return nil, nil, fmt.Errorf("error running network setup script:\nhostVethName %s, contVethMac %s, podIP %s, podConfig %#v\n %s", hostVeth.Attrs().Name, contVethMac, podIP.String(), podConfig, getScriptError(out)) } else if err != nil { return nil, nil, err } success = true return ipamResult, newPod, nil }
}) Context("when given a network namespace", func() { It("sets the lo device to UP", func() { command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD")) session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) Eventually(session).Should(gbytes.Say(`{.*}`)) Eventually(session).Should(gexec.Exit(0)) var lo *net.Interface err = ns.WithNetNSPath(networkNS, true, func(hostNS *os.File) error { var err error lo, err = net.InterfaceByName("lo") return err }) Expect(err).NotTo(HaveOccurred()) Expect(lo.Flags & net.FlagUp).To(Equal(net.FlagUp)) }) It("sets the lo device to DOWN", func() { command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL")) session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Expect(err).NotTo(HaveOccurred()) Eventually(session).Should(gbytes.Say(``)) Eventually(session).Should(gexec.Exit(0))
func cmdAdd(args *skel.CmdArgs) error { n, err := loadNetConf(args.StdinData) if err != nil { return err } br, err := setupBridge(n) if err != nil { return err } if err = setupVeth(args.Netns, br, args.IfName, n.MTU, n.HairpinMode); err != nil { return err } // run the IPAM plugin and get back the config to apply result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData) if err != nil { return err } if result.IP4 == nil { return errors.New("IPAM plugin returned missing IPv4 config") } if result.IP4.Gateway == nil && n.IsGW { result.IP4.Gateway = calcGatewayIP(&result.IP4.IP) } err = ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error { return ipam.ConfigureIface(args.IfName, result) }) if err != nil { return err } if n.IsGW { gwn := &net.IPNet{ IP: result.IP4.Gateway, Mask: result.IP4.IP.Mask, } if err = ensureBridgeAddr(br, gwn); err != nil { return err } if err := ip.EnableIP4Forward(); err != nil { return fmt.Errorf("failed to enable forwarding: %v", err) } } if n.IPMasq { chain := utils.FormatChainName(n.Name, args.ContainerID) comment := utils.FormatComment(n.Name, args.ContainerID) if err = ip.SetupIPMasq(ip.Network(&result.IP4.IP), chain, comment); err != nil { return err } } result.DNS = n.DNS return result.Print() }
// Set up all networking (host/container veth, OVS flows, IPAM, loopback, etc) func (m *podManager) setup(req *cniserver.PodRequest) (*cnitypes.Result, *kubehostport.RunningPod, error) { podConfig, pod, err := m.getPodConfig(req) if err != nil { return nil, nil, err } ipamResult, err := m.runIPAM(req.Netns, cniserver.CNI_ADD, req.ContainerId) if err != nil { return nil, nil, fmt.Errorf("failed to run IPAM for %v: %v", req.ContainerId, err) } podIP := ipamResult.IP4.IP.IP // Release any IPAM allocations and hostports if the setup failed var success bool defer func() { if !success { m.runIPAM(req.Netns, cniserver.CNI_DEL, req.ContainerId) if err := m.hostportHandler.SyncHostports(TUN, m.getRunningPods()); err != nil { glog.Warningf("failed syncing hostports: %v", err) } } }() // Open any hostports the pod wants newPod := &kubehostport.RunningPod{Pod: pod, IP: podIP} if err := m.hostportHandler.OpenPodHostportsAndSync(newPod, TUN, m.getRunningPods()); err != nil { return nil, nil, err } var hostVeth, contVeth netlink.Link err = ns.WithNetNSPath(req.Netns, func(hostNS ns.NetNS) error { hostVeth, contVeth, err = ip.SetupVeth(podInterfaceName, int(m.mtu), hostNS) if err != nil { return fmt.Errorf("failed to create container veth: %v", err) } // refetch to get hardware address and other properties contVeth, err = netlink.LinkByIndex(contVeth.Attrs().Index) if err != nil { return fmt.Errorf("failed to fetch container veth: %v", err) } // Clear out gateway to prevent ConfigureIface from adding the cluster // subnet via the gateway ipamResult.IP4.Gateway = nil if err = ipam.ConfigureIface(podInterfaceName, ipamResult); err != nil { return fmt.Errorf("failed to configure container IPAM: %v", err) } lo, err := netlink.LinkByName("lo") if err == nil { err = netlink.LinkSetUp(lo) } if err != nil { return fmt.Errorf("failed to configure container loopback: %v", err) } return nil }) if err != nil { return nil, nil, err } if podConfig.wantMacvlan { if err := addMacvlan(req.Netns); err != nil { return nil, nil, err } } contVethMac := contVeth.Attrs().HardwareAddr.String() vnidStr := vnidToString(podConfig.vnid) out, err := exec.Command(sdnScript, setUpCmd, hostVeth.Attrs().Name, contVethMac, podIP.String(), vnidStr, podConfig.ingressBandwidth, podConfig.egressBandwidth).CombinedOutput() glog.V(5).Infof("SetUpPod network plugin output: %s, %v", string(out), err) if isScriptError(err) { return nil, nil, fmt.Errorf("error running network setup script:\nhostVethName %s, contVethMac %s, podIP %s, podConfig %#v\n %s", hostVeth.Attrs().Name, contVethMac, podIP.String(), podConfig, getScriptError(out)) } else if err != nil { return nil, nil, err } success = true return ipamResult, newPod, nil }