tap: allow for a tap device to be created as a bridge port

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>
This commit is contained in:
Miguel Duarte Barroso 2023-02-17 16:47:44 +01:00
parent 38f18d26ec
commit edab9efdea
2 changed files with 128 additions and 0 deletions

View File

@ -47,6 +47,7 @@ type NetConf struct {
Owner *uint32 `json:"owner,omitempty"` Owner *uint32 `json:"owner,omitempty"`
Group *uint32 `json:"group,omitempty"` Group *uint32 `json:"group,omitempty"`
SelinuxContext string `json:"selinuxContext,omitempty"` SelinuxContext string `json:"selinuxContext,omitempty"`
Bridge string `json:"bridge,omitempty"`
Args *struct{} `json:"args,omitempty"` Args *struct{} `json:"args,omitempty"`
RuntimeConfig struct { RuntimeConfig struct {
Mac string `json:"mac,omitempty"` 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) 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) err = netlink.LinkSetUp(link)
if err != nil { if err != nil {
return fmt.Errorf("failed to set tap interface up: %v", err) return fmt.Errorf("failed to set tap interface up: %v", err)

View File

@ -314,5 +314,120 @@ var _ = Describe("Add, check, remove tap plugin", func() {
Expect(err).NotTo(HaveOccurred()) 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())
})
} }
}) })