Skip to content

Commit 7b93279

Browse files
committed
bridge: Enable disabling bridge interface
The new `disableContainerInterface` parameter is added to the bridge plugin to enable setting the container interface state down. When the parameter is enabled, the container interface (veth peer that is placed at the container ns) remain down (i.e: disabled). The bridge and host peer interfaces state are not affected by the parameter. Since IPAM logic involve various configurations including waiting for addresses to be realized and setting the interface state UP, the new parameter cannot work with IPAM. In case both IPAM and DisableContainerInterface parameters are set, the bridge plugin will raise an error. Signed-off-by: Or Mergi <[email protected]>
1 parent b6a0e0b commit 7b93279

File tree

2 files changed

+101
-44
lines changed

2 files changed

+101
-44
lines changed

plugins/main/bridge/bridge.go

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,20 @@ const defaultBrName = "cni0"
4747

4848
type NetConf struct {
4949
types.NetConf
50-
BrName string `json:"bridge"`
51-
IsGW bool `json:"isGateway"`
52-
IsDefaultGW bool `json:"isDefaultGateway"`
53-
ForceAddress bool `json:"forceAddress"`
54-
IPMasq bool `json:"ipMasq"`
55-
MTU int `json:"mtu"`
56-
HairpinMode bool `json:"hairpinMode"`
57-
PromiscMode bool `json:"promiscMode"`
58-
Vlan int `json:"vlan"`
59-
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
60-
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
61-
MacSpoofChk bool `json:"macspoofchk,omitempty"`
62-
EnableDad bool `json:"enabledad,omitempty"`
50+
BrName string `json:"bridge"`
51+
IsGW bool `json:"isGateway"`
52+
IsDefaultGW bool `json:"isDefaultGateway"`
53+
ForceAddress bool `json:"forceAddress"`
54+
IPMasq bool `json:"ipMasq"`
55+
MTU int `json:"mtu"`
56+
HairpinMode bool `json:"hairpinMode"`
57+
PromiscMode bool `json:"promiscMode"`
58+
Vlan int `json:"vlan"`
59+
VlanTrunk []*VlanTrunk `json:"vlanTrunk,omitempty"`
60+
PreserveDefaultVlan bool `json:"preserveDefaultVlan"`
61+
MacSpoofChk bool `json:"macspoofchk,omitempty"`
62+
EnableDad bool `json:"enabledad,omitempty"`
63+
DisableContainerInterface bool `json:"disableContainerInterface,omitempty"`
6364

6465
Args struct {
6566
Cni BridgeArgs `json:"cni,omitempty"`
@@ -530,6 +531,10 @@ func cmdAdd(args *skel.CmdArgs) error {
530531

531532
isLayer3 := n.IPAM.Type != ""
532533

534+
if isLayer3 && n.DisableContainerInterface {
535+
return fmt.Errorf("cannot use IPAM when DisableContainerInterface flag is set")
536+
}
537+
533538
if n.IsDefaultGW {
534539
n.IsGW = true
535540
}
@@ -677,38 +682,46 @@ func cmdAdd(args *skel.CmdArgs) error {
677682
}
678683
}
679684
} else {
680-
if err := netns.Do(func(_ ns.NetNS) error {
681-
link, err := netlink.LinkByName(args.IfName)
682-
if err != nil {
683-
return fmt.Errorf("failed to retrieve link: %v", err)
684-
}
685-
// If layer 2 we still need to set the container veth to up
686-
if err = netlink.LinkSetUp(link); err != nil {
687-
return fmt.Errorf("failed to set %q up: %v", args.IfName, err)
685+
if !n.DisableContainerInterface {
686+
if err := netns.Do(func(_ ns.NetNS) error {
687+
link, err := netlink.LinkByName(args.IfName)
688+
if err != nil {
689+
return fmt.Errorf("failed to retrieve link: %v", err)
690+
}
691+
692+
// If layer 2 we still need to set the container veth to up
693+
if err = netlink.LinkSetUp(link); err != nil {
694+
return fmt.Errorf("failed to set %q up: %v", args.IfName, err)
695+
}
696+
return nil
697+
}); err != nil {
698+
return err
688699
}
689-
return nil
690-
}); err != nil {
691-
return err
692700
}
693701
}
694702

695-
var hostVeth netlink.Link
703+
hostVeth, err := netlink.LinkByName(hostInterface.Name)
704+
if err != nil {
705+
return err
706+
}
696707

697-
// check bridge port state
698-
retries := []int{0, 50, 500, 1000, 1000}
699-
for idx, sleep := range retries {
700-
time.Sleep(time.Duration(sleep) * time.Millisecond)
708+
if !n.DisableContainerInterface {
709+
// check bridge port state
710+
retries := []int{0, 50, 500, 1000, 1000}
711+
for idx, sleep := range retries {
712+
time.Sleep(time.Duration(sleep) * time.Millisecond)
701713

702-
hostVeth, err = netlink.LinkByName(hostInterface.Name)
703-
if err != nil {
704-
return err
705-
}
706-
if hostVeth.Attrs().OperState == netlink.OperUp {
707-
break
708-
}
714+
hostVeth, err = netlink.LinkByName(hostInterface.Name)
715+
if err != nil {
716+
return err
717+
}
718+
if hostVeth.Attrs().OperState == netlink.OperUp {
719+
break
720+
}
709721

710-
if idx == len(retries)-1 {
711-
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
722+
if idx == len(retries)-1 {
723+
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
724+
}
712725
}
713726
}
714727

plugins/main/bridge/bridge_test.go

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,12 @@ type testCase struct {
7878
removeDefaultVlan bool
7979
ipMasq bool
8080
macspoofchk bool
81-
AddErr020 string
82-
DelErr020 string
83-
AddErr010 string
84-
DelErr010 string
81+
disableContIface bool
82+
83+
AddErr020 string
84+
DelErr020 string
85+
AddErr010 string
86+
DelErr010 string
8587

8688
envArgs string // CNI_ARGS
8789
runtimeConfig struct {
@@ -154,6 +156,9 @@ const (
154156
netDefault = `,
155157
"isDefaultGateway": true`
156158

159+
disableContainerInterface = `,
160+
"disableContainerInterface": true`
161+
157162
ipamStartStr = `,
158163
"ipam": {
159164
"type": "host-local"`
@@ -248,6 +253,10 @@ func (tc testCase) netConfJSON(dataDir string) string {
248253
conf += fmt.Sprintf(macspoofchkFormat, tc.macspoofchk)
249254
}
250255

256+
if tc.disableContIface {
257+
conf += disableContainerInterface
258+
}
259+
251260
if !tc.isLayer2 {
252261
conf += netDefault
253262
if tc.subnet != "" || tc.ranges != nil {
@@ -677,14 +686,16 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
677686
Expect(err).NotTo(HaveOccurred())
678687
Expect(link.Attrs().Name).To(Equal(IFNAME))
679688
Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{}))
689+
assertContainerInterfaceLinkState(&tc, link)
680690

681691
expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs()
682692
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
683693
Expect(err).NotTo(HaveOccurred())
684694
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
685695
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
686-
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
687696
Expect(err).NotTo(HaveOccurred())
697+
assertIPv6Addresses(&tc, addrs, expCIDRsV6)
698+
688699
// Ignore link local address which may or may not be
689700
// ready when we read addresses.
690701
var foundAddrs int
@@ -728,6 +739,15 @@ func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result,
728739
return result, nil
729740
}
730741

742+
func assertContainerInterfaceLinkState(tc *testCase, link netlink.Link) {
743+
linkState := int(link.Attrs().OperState)
744+
if tc.disableContIface {
745+
Expect(linkState).ToNot(Equal(netlink.OperUp))
746+
} else {
747+
Expect(linkState).To(Equal(netlink.OperUp))
748+
}
749+
}
750+
731751
func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, _ string) {
732752
// Generate network config and command arguments
733753
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
@@ -1008,8 +1028,9 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
10081028
Expect(err).NotTo(HaveOccurred())
10091029
Expect(addrs).To(HaveLen(len(expCIDRsV4)))
10101030
addrs, err = netlink.AddrList(link, netlink.FAMILY_V6)
1011-
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
10121031
Expect(err).NotTo(HaveOccurred())
1032+
assertIPv6Addresses(&tc, addrs, expCIDRsV6)
1033+
10131034
// Ignore link local address which may or may not be
10141035
// ready when we read addresses.
10151036
var foundAddrs int
@@ -1053,6 +1074,14 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result,
10531074
return result, nil
10541075
}
10551076

1077+
func assertIPv6Addresses(tc *testCase, addrs []netlink.Addr, expCIDRsV6 []*net.IPNet) {
1078+
if tc.disableContIface {
1079+
Expect(addrs).To(BeEmpty())
1080+
} else {
1081+
Expect(addrs).To(HaveLen(len(expCIDRsV6) + 1)) // add one for the link-local
1082+
}
1083+
}
1084+
10561085
func (tester *testerV04x) cmdCheckTest(tc testCase, conf *Net, _ string) {
10571086
// Generate network config and command arguments
10581087
tester.args = tc.createCheckCmdArgs(tester.targetNS, conf)
@@ -2461,6 +2490,21 @@ var _ = Describe("bridge Operations", func() {
24612490
return nil
24622491
})).To(Succeed())
24632492
})
2493+
2494+
It(fmt.Sprintf("[%s] set the container veth pair state down", ver), func() {
2495+
Expect(originalNS.Do(func(ns.NetNS) error {
2496+
defer GinkgoRecover()
2497+
tc := testCase{
2498+
cniVersion: ver,
2499+
disableContIface: true,
2500+
isLayer2: true,
2501+
AddErr020: "cannot convert: no valid IP addresses",
2502+
AddErr010: "cannot convert: no valid IP addresses",
2503+
}
2504+
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
2505+
return nil
2506+
})).To(Succeed())
2507+
})
24642508
}
24652509

24662510
It("check vlan id when loading net conf", func() {

0 commit comments

Comments
 (0)