Skip to content

Commit edab9ef

Browse files
committed
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 <[email protected]>
1 parent 38f18d2 commit edab9ef

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

plugins/main/tap/tap.go

+13
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type NetConf struct {
4747
Owner *uint32 `json:"owner,omitempty"`
4848
Group *uint32 `json:"group,omitempty"`
4949
SelinuxContext string `json:"selinuxContext,omitempty"`
50+
Bridge string `json:"bridge,omitempty"`
5051
Args *struct{} `json:"args,omitempty"`
5152
RuntimeConfig struct {
5253
Mac string `json:"mac,omitempty"`
@@ -216,6 +217,18 @@ func createTap(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interface
216217
return fmt.Errorf("failed to refetch tap %q: %v", ifName, err)
217218
}
218219

220+
if conf.Bridge != "" {
221+
bridge, err := netlink.LinkByName(conf.Bridge)
222+
if err != nil {
223+
return fmt.Errorf("failed to get bridge %s: %v", conf.Bridge, err)
224+
}
225+
226+
tapDev := link
227+
if err := netlink.LinkSetMaster(tapDev, bridge); err != nil {
228+
return fmt.Errorf("failed to set tap %s as a port of bridge %s: %v", tap.Name, conf.Bridge, err)
229+
}
230+
}
231+
219232
err = netlink.LinkSetUp(link)
220233
if err != nil {
221234
return fmt.Errorf("failed to set tap interface up: %v", err)

plugins/main/tap/tap_test.go

+115
Original file line numberDiff line numberDiff line change
@@ -314,5 +314,120 @@ var _ = Describe("Add, check, remove tap plugin", func() {
314314
Expect(err).NotTo(HaveOccurred())
315315
})
316316

317+
It(fmt.Sprintf("[%s] add, check and remove a tap device as a bridge port", ver), func() {
318+
const bridgeName = "br1"
319+
conf := fmt.Sprintf(`{
320+
"cniVersion": "%s",
321+
"name": "tapTest",
322+
"type": "tap",
323+
"owner": 0,
324+
"group": 0,
325+
"bridge": %q,
326+
"ipam": {
327+
"type": "host-local",
328+
"subnet": "10.1.2.0/24",
329+
"dataDir": "%s"
330+
}
331+
}`, ver, bridgeName, dataDir)
332+
333+
args := &skel.CmdArgs{
334+
ContainerID: "dummy",
335+
Netns: targetNS.Path(),
336+
IfName: IFNAME,
337+
StdinData: []byte(conf),
338+
}
339+
340+
t := newTesterByVersion(ver)
341+
342+
var bridge netlink.Link
343+
var result types.Result
344+
var macAddress string
345+
var err error
346+
347+
Expect(
348+
targetNS.Do(func(ns.NetNS) error {
349+
if err := netlink.LinkAdd(&netlink.Bridge{
350+
LinkAttrs: netlink.LinkAttrs{
351+
Name: bridgeName,
352+
},
353+
}); err != nil {
354+
return err
355+
}
356+
bridge, err = netlink.LinkByName(bridgeName)
357+
if err != nil {
358+
return err
359+
}
360+
return nil
361+
}),
362+
).To(Succeed())
363+
364+
err = originalNS.Do(func(ns.NetNS) error {
365+
defer GinkgoRecover()
366+
367+
result, _, err = testutils.CmdAddWithArgs(args, func() error {
368+
return cmdAdd(args)
369+
})
370+
Expect(err).NotTo(HaveOccurred())
371+
macAddress = t.verifyResult(result, IFNAME)
372+
return nil
373+
})
374+
Expect(err).NotTo(HaveOccurred())
375+
376+
By("Make sure the tap link exists in the target namespace")
377+
err = targetNS.Do(func(ns.NetNS) error {
378+
defer GinkgoRecover()
379+
380+
link, err := netlink.LinkByName(IFNAME)
381+
Expect(err).NotTo(HaveOccurred())
382+
Expect(link.Attrs().Name).To(Equal(IFNAME))
383+
Expect(link.Type()).To(Equal(TYPETAP))
384+
Expect(link.Attrs().MasterIndex).To(Equal(bridge.Attrs().Index))
385+
386+
if macAddress != "" {
387+
hwaddr, err := net.ParseMAC(macAddress)
388+
Expect(err).NotTo(HaveOccurred())
389+
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
390+
}
391+
addrs, err := netlink.AddrList(link, syscall.AF_INET)
392+
Expect(err).NotTo(HaveOccurred())
393+
Expect(addrs).To(HaveLen(1))
394+
return nil
395+
})
396+
Expect(err).NotTo(HaveOccurred())
397+
398+
By("Running cmdDel")
399+
args.StdinData = []byte(conf)
400+
err = originalNS.Do(func(ns.NetNS) error {
401+
defer GinkgoRecover()
402+
403+
err = testutils.CmdDelWithArgs(args, func() error {
404+
return cmdDel(args)
405+
})
406+
Expect(err).NotTo(HaveOccurred())
407+
return nil
408+
})
409+
Expect(err).NotTo(HaveOccurred())
410+
411+
err = targetNS.Do(func(ns.NetNS) error {
412+
defer GinkgoRecover()
413+
414+
link, err := netlink.LinkByName(IFNAME)
415+
Expect(err).To(HaveOccurred())
416+
Expect(link).To(BeNil())
417+
return nil
418+
})
419+
Expect(err).NotTo(HaveOccurred())
420+
421+
By("Running cmdDel more than once without error")
422+
err = originalNS.Do(func(ns.NetNS) error {
423+
defer GinkgoRecover()
424+
err = testutils.CmdDelWithArgs(args, func() error {
425+
return cmdDel(args)
426+
})
427+
Expect(err).NotTo(HaveOccurred())
428+
return nil
429+
})
430+
Expect(err).NotTo(HaveOccurred())
431+
})
317432
}
318433
})

0 commit comments

Comments
 (0)