Skip to content

Commit

Permalink
tap: allow for a tap device to be created as a bridge port
Browse files Browse the repository at this point in the history
This extends the tap plugin API enabling the user to instruct the CNI
plugin the created tap device must be set as a port of an *existing*
linux bridge on the pod network namespace.

This is helpful for KubeVirt, allowing network connectivity to be
extended from the pod's interface into the Virtual Machine running
inside the pod.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
  • Loading branch information
maiqueb committed Feb 17, 2023
1 parent fb92605 commit bd8eaed
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 0 deletions.
17 changes: 17 additions & 0 deletions plugins/main/tap/tap.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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 {
Expand Down Expand Up @@ -212,6 +213,22 @@ func createTap(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interface
}
tap.Name = ifName

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, err := netlink.LinkByName(tap.Name)
if err != nil {
return fmt.Errorf("failed to get tap device %s: %v", tap.Name, err)
}

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)
}
}

// Re-fetch link to get all properties/attributes
link, err := netlink.LinkByName(ifName)
if err != nil {
Expand Down
115 changes: 115 additions & 0 deletions plugins/main/tap/tap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,5 +315,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(len(addrs)).To(Equal(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())
})
}
})

0 comments on commit bd8eaed

Please sign in to comment.