host-device: respect CNI_IFNAME/args.IfName

On ADD save the host device's name into its IFLA_ALIAS property and
rename the device to the requested CNI_IFNAME inside the container
to conform to the CNI specification.  On DEL rename the device to
the original name and move it back into the host namespace.
This commit is contained in:
Dan Williams 2018-01-17 10:27:54 -06:00
parent 03e316b07b
commit ffc591e242
2 changed files with 54 additions and 26 deletions

View File

@ -68,15 +68,20 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
defer containerNs.Close() defer containerNs.Close()
dev, err := moveLinkIn(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs) hostDev, err := getLink(cfg.Device, cfg.HWAddr, cfg.KernelPath)
if err != nil {
return fmt.Errorf("failed to find host device: %v", err)
}
contDev, err := moveLinkIn(hostDev, containerNs, args.IfName)
if err != nil { if err != nil {
return fmt.Errorf("failed to move link %v", err) return fmt.Errorf("failed to move link %v", err)
} }
return printLink(dev, cfg.CNIVersion, containerNs) return printLink(contDev, cfg.CNIVersion, containerNs)
} }
func cmdDel(args *skel.CmdArgs) error { func cmdDel(args *skel.CmdArgs) error {
cfg, err := loadConf(args.StdinData) _, err := loadConf(args.StdinData)
if err != nil { if err != nil {
return err return err
} }
@ -85,44 +90,67 @@ func cmdDel(args *skel.CmdArgs) error {
return fmt.Errorf("failed to open netns %q: %v", args.Netns, err) return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
} }
defer containerNs.Close() defer containerNs.Close()
defer fmt.Println(`{}`)
return moveLinkOut(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs) if err := moveLinkOut(containerNs, args.IfName); err != nil {
return err
}
return nil
} }
func moveLinkIn(device, hwAddr, kernelPath string, containerNs ns.NetNS) (netlink.Link, error) { func moveLinkIn(hostDev netlink.Link, containerNs ns.NetNS, ifName string) (netlink.Link, error) {
dev, err := getLink(device, hwAddr, kernelPath) if err := netlink.LinkSetNsFd(hostDev, int(containerNs.Fd())); err != nil {
if err != nil {
return nil, err
}
if err := netlink.LinkSetNsFd(dev, int(containerNs.Fd())); err != nil {
return nil, err return nil, err
} }
var contDev netlink.Link
if err := containerNs.Do(func(_ ns.NetNS) error { if err := containerNs.Do(func(_ ns.NetNS) error {
dev, err = netlink.LinkByName(dev.Attrs().Name) var err error
contDev, err = netlink.LinkByName(hostDev.Attrs().Name)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to find %q: %v", hostDev.Attrs().Name, err)
}
// Save host device name into the container device's alias property
if err := netlink.LinkSetAlias(contDev, hostDev.Attrs().Name); err != nil {
return fmt.Errorf("failed to set alias to %q: %v", hostDev.Attrs().Name, err)
}
// Rename container device to respect args.IfName
if err := netlink.LinkSetName(contDev, ifName); err != nil {
return fmt.Errorf("failed to rename device %q to %q: %v", hostDev.Attrs().Name, ifName, err)
}
// Retrieve link again to get up-to-date name and attributes
contDev, err = netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to find %q: %v", ifName, err)
} }
return nil return nil
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
return dev, nil return contDev, nil
} }
func moveLinkOut(device, hwAddr, kernelPath string, containerNs ns.NetNS) error { func moveLinkOut(containerNs ns.NetNS, ifName string) error {
defaultNs, err := ns.GetCurrentNS() defaultNs, err := ns.GetCurrentNS()
if err != nil { if err != nil {
return err return err
} }
defer defaultNs.Close()
return containerNs.Do(func(_ ns.NetNS) error { return containerNs.Do(func(_ ns.NetNS) error {
dev, err := getLink(device, hwAddr, kernelPath) dev, err := netlink.LinkByName(ifName)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to find %q: %v", ifName, err)
} }
return netlink.LinkSetNsFd(dev, int(defaultNs.Fd())) // Rename device to it's original name
if err := netlink.LinkSetName(dev, dev.Attrs().Alias); err != nil {
return fmt.Errorf("failed to restore %q to original name %q: %v", ifName, dev.Attrs().Alias, err)
}
if err := netlink.LinkSetNsFd(dev, int(defaultNs.Fd())); err != nil {
return fmt.Errorf("failed to move %q to host netns: %v", dev.Attrs().Alias, err)
}
return nil
}) })
} }

View File

@ -45,7 +45,6 @@ var _ = Describe("base functionality", func() {
}) })
It("Works with a valid config", func() { It("Works with a valid config", func() {
var origLink netlink.Link var origLink netlink.Link
// prepare ifname in original namespace // prepare ifname in original namespace
@ -69,6 +68,7 @@ var _ = Describe("base functionality", func() {
targetNS, err := ns.NewNS() targetNS, err := ns.NewNS()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
CNI_IFNAME := "eth0"
conf := fmt.Sprintf(`{ conf := fmt.Sprintf(`{
"cniVersion": "0.3.0", "cniVersion": "0.3.0",
"name": "cni-plugin-host-device-test", "name": "cni-plugin-host-device-test",
@ -78,14 +78,14 @@ var _ = Describe("base functionality", func() {
args := &skel.CmdArgs{ args := &skel.CmdArgs{
ContainerID: "dummy", ContainerID: "dummy",
Netns: targetNS.Path(), Netns: targetNS.Path(),
IfName: ifname, IfName: CNI_IFNAME,
StdinData: []byte(conf), StdinData: []byte(conf),
} }
var resI types.Result var resI types.Result
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
var err error var err error
resI, _, err = testutils.CmdAddWithResult(targetNS.Path(), ifname, []byte(conf), func() error { return cmdAdd(args) }) resI, _, err = testutils.CmdAddWithResult(targetNS.Path(), CNI_IFNAME, []byte(conf), func() error { return cmdAdd(args) })
return err return err
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -95,7 +95,7 @@ var _ = Describe("base functionality", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(res.Interfaces).To(Equal([]*current.Interface{ Expect(res.Interfaces).To(Equal([]*current.Interface{
{ {
Name: ifname, Name: CNI_IFNAME,
Mac: origLink.Attrs().HardwareAddr.String(), Mac: origLink.Attrs().HardwareAddr.String(),
Sandbox: targetNS.Path(), Sandbox: targetNS.Path(),
}, },
@ -104,9 +104,9 @@ var _ = Describe("base functionality", func() {
// assert that dummy0 is now in the target namespace // assert that dummy0 is now in the target namespace
err = targetNS.Do(func(ns.NetNS) error { err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
link, err := netlink.LinkByName(ifname) link, err := netlink.LinkByName(CNI_IFNAME)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(ifname)) Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr))
return nil return nil
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -120,10 +120,10 @@ var _ = Describe("base functionality", func() {
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
// Check that deleting the device moves it back // Check that deleting the device moves it back and restores the name
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
err = testutils.CmdDelWithResult(targetNS.Path(), ifname, func() error { return cmdDel(args) }) err = testutils.CmdDelWithResult(targetNS.Path(), CNI_IFNAME, func() error { return cmdDel(args) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
_, err := netlink.LinkByName(ifname) _, err := netlink.LinkByName(ifname)