diff --git a/plugins/main/tap/tap.go b/plugins/main/tap/tap.go index 760fb4f42..5e2c03fac 100644 --- a/plugins/main/tap/tap.go +++ b/plugins/main/tap/tap.go @@ -47,6 +47,7 @@ type NetConf struct { Owner *uint32 `json:"owner,omitempty"` Group *uint32 `json:"group,omitempty"` SelinuxContext string `json:"selinuxContext,omitempty"` + Bridge string `json:"bridge,omitempty"` Args *struct{} `json:"args,omitempty"` RuntimeConfig struct { Mac string `json:"mac,omitempty"` @@ -216,6 +217,18 @@ func createTap(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interface return fmt.Errorf("failed to refetch tap %q: %v", ifName, err) } + if conf.Bridge != "" { + bridge, err := netlink.LinkByName(conf.Bridge) + if err != nil { + return fmt.Errorf("failed to get bridge %s: %v", conf.Bridge, err) + } + + tapDev := link + if err := netlink.LinkSetMaster(tapDev, bridge); err != nil { + return fmt.Errorf("failed to set tap %s as a port of bridge %s: %v", tap.Name, conf.Bridge, err) + } + } + err = netlink.LinkSetUp(link) if err != nil { return fmt.Errorf("failed to set tap interface up: %v", err) diff --git a/plugins/main/tap/tap_test.go b/plugins/main/tap/tap_test.go index 771f6afa8..c39a25ee6 100644 --- a/plugins/main/tap/tap_test.go +++ b/plugins/main/tap/tap_test.go @@ -314,5 +314,120 @@ var _ = Describe("Add, check, remove tap plugin", func() { Expect(err).NotTo(HaveOccurred()) }) + It(fmt.Sprintf("[%s] add, check and remove a tap device as a bridge port", ver), func() { + const bridgeName = "br1" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "tapTest", + "type": "tap", + "owner": 0, + "group": 0, + "bridge": %q, + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, bridgeName, dataDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + t := newTesterByVersion(ver) + + var bridge netlink.Link + var result types.Result + var macAddress string + var err error + + Expect( + targetNS.Do(func(ns.NetNS) error { + if err := netlink.LinkAdd(&netlink.Bridge{ + LinkAttrs: netlink.LinkAttrs{ + Name: bridgeName, + }, + }); err != nil { + return err + } + bridge, err = netlink.LinkByName(bridgeName) + if err != nil { + return err + } + return nil + }), + ).To(Succeed()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + result, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + macAddress = t.verifyResult(result, IFNAME) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + By("Make sure the tap link exists in the target namespace") + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + Expect(link.Type()).To(Equal(TYPETAP)) + Expect(link.Attrs().MasterIndex).To(Equal(bridge.Attrs().Index)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(addrs).To(HaveLen(1)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + By("Running cmdDel") + args.StdinData = []byte(conf) + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + By("Running cmdDel more than once without error") + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) } })