瀏覽代碼

Prefer Netlink calls over ioctl

As seen in https://github.com/docker/docker/issues/14738 there is
general instability in the later kernels under race conditions when ioctl
calls are used in parallel with netlink calls for various operations.
(We are yet to narrow down to the exact root-cause on the kernel).

For those older kernels which doesnt support some of the netlink APIs,
we can fallback to using ioctl calls. Hence bringing back the original
code that used netlink (https://github.com/docker/libnetwork/pull/349).

Also, there was an existing bug in bridge creation using netlink which
was setting bridge mac during bridge creation. That operation is not
supported in the netlink library (and doesnt throw an error either).
Included a fix for that condition by setting the bridge mac after
creating the bridge.

Signed-off-by: Madhu Venugopal <madhu@docker.com>
Madhu Venugopal 10 年之前
父節點
當前提交
6901ea51dc
共有 2 個文件被更改,包括 31 次插入7 次删除
  1. 15 6
      libnetwork/drivers/bridge/bridge.go
  2. 16 1
      libnetwork/drivers/bridge/setup_device.go

+ 15 - 6
libnetwork/drivers/bridge/bridge.go

@@ -763,17 +763,26 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
 }
 }
 
 
 func addToBridge(ifaceName, bridgeName string) error {
 func addToBridge(ifaceName, bridgeName string) error {
-	iface, err := net.InterfaceByName(ifaceName)
+	link, err := netlink.LinkByName(ifaceName)
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("could not find interface %s: %v", ifaceName, err)
 		return fmt.Errorf("could not find interface %s: %v", ifaceName, err)
 	}
 	}
+	if err = netlink.LinkSetMaster(link,
+		&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: bridgeName}}); err != nil {
+		logrus.Debugf("Failed to add %s to bridge via netlink.Trying ioctl: %v", ifaceName, err)
+		iface, err := net.InterfaceByName(ifaceName)
+		if err != nil {
+			return fmt.Errorf("could not find network interface %s: %v", ifaceName, err)
+		}
 
 
-	master, err := net.InterfaceByName(bridgeName)
-	if err != nil {
-		return fmt.Errorf("could not find bridge %s: %v", bridgeName, err)
-	}
+		master, err := net.InterfaceByName(bridgeName)
+		if err != nil {
+			return fmt.Errorf("could not find bridge %s: %v", bridgeName, err)
+		}
 
 
-	return ioctlAddToBridge(iface, master)
+		return ioctlAddToBridge(iface, master)
+	}
+	return nil
 }
 }
 
 
 func setHairpinMode(link netlink.Link, enable bool) error {
 func setHairpinMode(link netlink.Link, enable bool) error {

+ 16 - 1
libnetwork/drivers/bridge/setup_device.go

@@ -1,8 +1,11 @@
 package bridge
 package bridge
 
 
 import (
 import (
+	"fmt"
+
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/pkg/parsers/kernel"
 	"github.com/docker/docker/pkg/parsers/kernel"
+	"github.com/docker/libnetwork/netutils"
 	"github.com/vishvananda/netlink"
 	"github.com/vishvananda/netlink"
 )
 )
 
 
@@ -32,7 +35,19 @@ func setupDevice(config *networkConfiguration, i *bridgeInterface) error {
 		setMac = kv.Kernel > 3 || (kv.Kernel == 3 && kv.Major >= 3)
 		setMac = kv.Kernel > 3 || (kv.Kernel == 3 && kv.Major >= 3)
 	}
 	}
 
 
-	return ioctlCreateBridge(config.BridgeName, setMac)
+	if err = netlink.LinkAdd(i.Link); err != nil {
+		logrus.Debugf("Failed to create bridge %s via netlink. Trying ioctl", config.BridgeName)
+		return ioctlCreateBridge(config.BridgeName, setMac)
+	}
+
+	if setMac {
+		hwAddr := netutils.GenerateRandomMAC()
+		if err = netlink.LinkSetHardwareAddr(i.Link, hwAddr); err != nil {
+			return fmt.Errorf("failed to set bridge mac-address %s : %s", hwAddr, err.Error())
+		}
+		logrus.Debugf("Setting bridge mac address to %s", hwAddr)
+	}
+	return err
 }
 }
 
 
 // SetupDeviceUp ups the given bridge interface.
 // SetupDeviceUp ups the given bridge interface.