diff --git a/plugins/main/host-device/README.md b/plugins/main/host-device/README.md new file mode 100644 index 00000000..1e0af84a --- /dev/null +++ b/plugins/main/host-device/README.md @@ -0,0 +1,21 @@ +# host-device +Move an already-existing device in to a container. + +This simple plugin will move the requested device from the host's network namespace +to the container's. Nothing else will be done - no IPAM, no addresses. + +The device can be specified with any one of three properties: +* `device`: The device name, e.g. `eth0`, `can0` +* `hwaddr`: A MAC address +* `kernelpath`: The kernel device kobj, e.g. `/sys/devices/pci0000:00/0000:00:1f.6` + +For this plugin, `CNI_IFNAME` will be ignored. Upon DEL, the device will be moved back. + +A sample configuration might look like: + +```json +{ + "cniVersion": "0.3.1", + "device": "enp0s1" +} +``` diff --git a/plugins/main/host-device/host-device.go b/plugins/main/host-device/host-device.go index 954b7778..8f3d482a 100644 --- a/plugins/main/host-device/host-device.go +++ b/plugins/main/host-device/host-device.go @@ -25,6 +25,7 @@ import ( "strings" "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ns" @@ -32,6 +33,7 @@ import ( ) type NetConf struct { + types.NetConf Device string `json:"device"` // Device-Name, something like eth0 or can0 etc. HWAddr string `json:"hwaddr"` // MAC Address of target network interface KernelPath string `json:"kernelpath"` // Kernelpath of the device @@ -65,8 +67,12 @@ func cmdAdd(args *skel.CmdArgs) error { return fmt.Errorf("failed to open netns %q: %v", args.Netns, err) } defer containerNs.Close() - defer (¤t.Result{}).Print() - return addLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs) + + dev, err := moveLinkIn(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs) + if err != nil { + return fmt.Errorf("failed to move link %v", err) + } + return printLink(dev, cfg.CNIVersion, containerNs) } func cmdDel(args *skel.CmdArgs) error { @@ -80,35 +86,58 @@ func cmdDel(args *skel.CmdArgs) error { } defer containerNs.Close() defer fmt.Println(`{}`) - return removeLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs) + return moveLinkOut(cfg.Device, cfg.HWAddr, cfg.KernelPath, containerNs) } -func addLink(device, hwAddr, kernelPath string, containerNs ns.NetNS) error { +func moveLinkIn(device, hwAddr, kernelPath string, containerNs ns.NetNS) (netlink.Link, error) { dev, err := getLink(device, hwAddr, kernelPath) if err != nil { - return err + return nil, err + } + if err := netlink.LinkSetNsFd(dev, int(containerNs.Fd())); err != nil { + return nil, err } - return netlink.LinkSetNsFd(dev, int(containerNs.Fd())) -} -func removeLink(device, hwAddr, kernelPath string, containerNs ns.NetNS) error { - var dev netlink.Link - err := containerNs.Do(func(_ ns.NetNS) error { - d, err := getLink(device, hwAddr, kernelPath) + if err := containerNs.Do(func(_ ns.NetNS) error { + dev, err = netlink.LinkByName(dev.Attrs().Name) if err != nil { return err } - dev = d return nil - }) - if err != nil { - return err + }); err != nil { + return nil, err } + + return dev, nil +} + +func moveLinkOut(device, hwAddr, kernelPath string, containerNs ns.NetNS) error { defaultNs, err := ns.GetCurrentNS() if err != nil { return err } - return netlink.LinkSetNsFd(dev, int(defaultNs.Fd())) + + return containerNs.Do(func(_ ns.NetNS) error { + dev, err := getLink(device, hwAddr, kernelPath) + if err != nil { + return err + } + return netlink.LinkSetNsFd(dev, int(defaultNs.Fd())) + }) +} + +func printLink(dev netlink.Link, cniVersion string, containerNs ns.NetNS) error { + result := current.Result{ + CNIVersion: current.ImplementedSpecVersion, + Interfaces: []*current.Interface{ + { + Name: dev.Attrs().Name, + Mac: dev.Attrs().HardwareAddr.String(), + Sandbox: containerNs.Path(), + }, + }, + } + return types.PrintResult(&result, cniVersion) } func getLink(devname, hwaddr, kernelpath string) (netlink.Link, error) { diff --git a/plugins/main/host-device/host-device_test.go b/plugins/main/host-device/host-device_test.go index 128ad73c..7f74fc1c 100644 --- a/plugins/main/host-device/host-device_test.go +++ b/plugins/main/host-device/host-device_test.go @@ -19,6 +19,8 @@ import ( "math/rand" "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" . "github.com/onsi/ginkgo" @@ -44,6 +46,8 @@ var _ = Describe("base functionality", func() { It("Works with a valid config", func() { + var origLink netlink.Link + // prepare ifname in original namespace err := originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -53,9 +57,9 @@ var _ = Describe("base functionality", func() { }, }) Expect(err).NotTo(HaveOccurred()) - link, err := netlink.LinkByName(ifname) + origLink, err = netlink.LinkByName(ifname) Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(link) + err = netlink.LinkSetUp(origLink) Expect(err).NotTo(HaveOccurred()) return nil }) @@ -77,13 +81,26 @@ var _ = Describe("base functionality", func() { IfName: ifname, StdinData: []byte(conf), } + var resI types.Result err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() - _, _, err := testutils.CmdAddWithResult(targetNS.Path(), ifname, []byte(conf), func() error { return cmdAdd(args) }) + var err error + resI, _, err = testutils.CmdAddWithResult(targetNS.Path(), ifname, []byte(conf), func() error { return cmdAdd(args) }) return err }) Expect(err).NotTo(HaveOccurred()) + // check that the result was sane + res, err := current.NewResultFromResult(resI) + Expect(err).NotTo(HaveOccurred()) + Expect(res.Interfaces).To(Equal([]*current.Interface{ + { + Name: ifname, + Mac: origLink.Attrs().HardwareAddr.String(), + Sandbox: targetNS.Path(), + }, + })) + // assert that dummy0 is now in the target namespace err = targetNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -102,6 +119,19 @@ var _ = Describe("base functionality", func() { return nil }) Expect(err).NotTo(HaveOccurred()) + + // Check that deleting the device moves it back + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithResult(targetNS.Path(), ifname, func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + _, err := netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) It("fails an invalid config", func() {