func setupVeth(netns string, br *netlink.Bridge, ifName string, mtu int) 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) } 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) } 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 conf.IPMasq { h := sha512.Sum512([]byte(args.ContainerID)) chain := fmt.Sprintf("CNI-%s-%x", conf.Name, h[:8]) if err = ip.TeardownIPMasq(ipn, chain); err != nil { return err } } return ipam.ExecDel(conf.IPAM.Type, args.StdinData) }
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) } 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 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 ipam.ExecDel(conf.IPAM.Type, args.StdinData) }
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); 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 := "CNI-" + n.Name if err = ip.SetupIPMasq(ip.Network(&result.IP4.IP), chain); err != nil { return err } } return result.Print() }
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, false, func(hostNS *os.File) 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 cmdDel(args *skel.CmdArgs) error { n, err := loadConf(args.StdinData) if err != nil { return err } err = plugin.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) }) }
func setupContainerVeth(netns, ifName string, mtu int, pr *plugin.Result) (string, error) { var hostVethName string err := ns.WithNetNSPath(netns, false, func(hostNS *os.File) error { hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS) if err != nil { return err } err = plugin.ConfigureIface(ifName, pr) if err != nil { return err } hostVethName = hostVeth.Attrs().Name return nil }) return hostVethName, err }
func cmdDel(args *skel.CmdArgs) error { args.IfName = "lo" // ignore config, this only works for loopback err := ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) 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, true, func(_ *os.File) 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 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, false, func(hostNS *os.File) error { hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS) if err != nil { return err } 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) } // 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) } } hostVethName = hostVeth.Attrs().Name return nil }) return hostVethName, err }
}) 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 (n *namespace) Execute(callback func(*os.File) error) error { return ns.WithNetNSPath(n.path, false, func(f *os.File) error { return callback(f) }) }