Skip to content

Commit cdd1542

Browse files
committed
bridge: always pin the bridge MAC address
The ensureAddr function pins the bridge's MAC address so it doesn't change as ports come and go. The pinning happens after the gateway address is set, so if a run set the address but then failed before pinning, subsequent runs never reattempted to pin the MAC, leaving behind a bridge with a floating MAC. Always pin the MAC, even if the gateway address is already present, regardless of the outcomes of previous attempts. Signed-off-by: Tom Wieczorek <twieczorek@mirantis.com>
1 parent 33cc6bd commit cdd1542

2 files changed

Lines changed: 74 additions & 4 deletions

File tree

plugins/main/bridge/bridge.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,13 @@ func ensureAddr(br netlink.Link, family int, ipn *net.IPNet, forceAddress bool)
271271
}
272272

273273
ipnStr := ipn.String()
274+
addrFound := false
274275
for _, a := range addrs {
275276

276277
// string comp is actually easiest for doing IPNet comps
277278
if a.IPNet.String() == ipnStr {
278-
return nil
279+
addrFound = true
280+
break
279281
}
280282

281283
// Multiple IPv6 addresses are allowed on the bridge if the
@@ -293,9 +295,11 @@ func ensureAddr(br netlink.Link, family int, ipn *net.IPNet, forceAddress bool)
293295
}
294296
}
295297

296-
addr := &netlink.Addr{IPNet: ipn, Label: ""}
297-
if err := netlink.AddrAdd(br, addr); err != nil && err != syscall.EEXIST {
298-
return fmt.Errorf("could not add IP address %s to %q: %v", ipnStr, br.Attrs().Name, err)
298+
if !addrFound {
299+
addr := &netlink.Addr{IPNet: ipn, Label: ""}
300+
if err := netlink.AddrAdd(br, addr); err != nil && err != syscall.EEXIST {
301+
return fmt.Errorf("could not add IP address %s to %q: %v", ipnStr, br.Attrs().Name, err)
302+
}
299303
}
300304

301305
// Set the bridge's MAC to itself. Otherwise, the bridge will take the

plugins/main/bridge/bridge_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"net"
2222
"os"
2323
"strings"
24+
"sync"
2425

2526
"github.com/coreos/go-iptables/iptables"
2627
"github.com/networkplumbing/go-nft/nft"
@@ -407,6 +408,29 @@ func (tc testCase) expectedCIDRs() ([]*net.IPNet, []*net.IPNet) {
407408
return cidrsV4, cidrsV6
408409
}
409410

411+
// Attaches a veth port with an optional fixed MAC address to a bridge in the
412+
// current network namespace. To be cleaned up via the returned function.
413+
func attachVethPort(br netlink.Link, mac string) (func() error, error) {
414+
linkAttrs := netlink.NewLinkAttrs()
415+
linkAttrs.Name = "testport0"
416+
if mac != "" {
417+
if hwAddr, err := net.ParseMAC(mac); err != nil {
418+
return nil, err
419+
} else {
420+
linkAttrs.HardwareAddr = hwAddr
421+
}
422+
}
423+
veth := &netlink.Veth{LinkAttrs: linkAttrs, PeerName: "testport1"}
424+
if err := netlink.LinkAdd(veth); err != nil {
425+
return nil, err
426+
}
427+
if err := netlink.LinkSetMaster(veth, br); err != nil {
428+
return nil, err
429+
}
430+
431+
return sync.OnceValue(func() error { return netlink.LinkDel(veth) }), nil
432+
}
433+
410434
// delBridgeAddrs() deletes addresses from the bridge
411435
func delBridgeAddrs(testNS ns.NetNS) {
412436
err := testNS.Do(func(ns.NetNS) error {
@@ -2377,6 +2401,48 @@ var _ = Describe("bridge Operations", func() {
23772401
})
23782402
Expect(err).NotTo(HaveOccurred())
23792403
})
2404+
2405+
It(fmt.Sprintf("[%s] (%d) keeps a stable MAC even if the gateway address already exists", ver, i), func() {
2406+
err := originalNS.Do(func(ns.NetNS) error {
2407+
defer GinkgoRecover()
2408+
2409+
tc.cniVersion = ver
2410+
br, _, err := setupBridge(tc.netConf())
2411+
Expect(err).NotTo(HaveOccurred())
2412+
link, err := netlinksafe.LinkByName(BRNAME)
2413+
Expect(err).NotTo(HaveOccurred())
2414+
originalMAC := link.Attrs().HardwareAddr
2415+
2416+
// Pre-add the gateway address the plugin is going to
2417+
// configure, as left behind by an ADD that failed midway.
2418+
_, subnet, err := net.ParseCIDR(tc.subnet)
2419+
Expect(err).NotTo(HaveOccurred())
2420+
gwIP := calcGatewayIP(subnet)
2421+
err = netlink.AddrAdd(br, &netlink.Addr{
2422+
IPNet: &net.IPNet{IP: gwIP, Mask: subnet.Mask},
2423+
})
2424+
Expect(err).NotTo(HaveOccurred())
2425+
2426+
cmdAddDelTest(originalNS, targetNS, tc, dataDir)
2427+
2428+
// The MAC must have been pinned nevertheless: it neither
2429+
// got zeroed when the container veth was removed, nor
2430+
// does it change with port churn.
2431+
link, err = netlinksafe.LinkByName(BRNAME)
2432+
Expect(err).NotTo(HaveOccurred())
2433+
Expect(link.Attrs().HardwareAddr).To(Equal(originalMAC))
2434+
2435+
cleanupVethPort, err := attachVethPort(br, "02:00:00:00:00:01")
2436+
Expect(err).NotTo(HaveOccurred())
2437+
defer func() { Expect(cleanupVethPort()).To(Succeed()) }()
2438+
2439+
link, err = netlinksafe.LinkByName(BRNAME)
2440+
Expect(err).NotTo(HaveOccurred())
2441+
Expect(link.Attrs().HardwareAddr).To(Equal(originalMAC))
2442+
return nil
2443+
})
2444+
Expect(err).NotTo(HaveOccurred())
2445+
})
23802446
}
23812447

23822448
It(fmt.Sprintf("[%s] uses an explicit MAC addresses for the container iface (from CNI_ARGS)", ver), func() {

0 commit comments

Comments
 (0)