2021-08-23 15:14:53 +02:00
|
|
|
//go:build linux
|
2021-05-25 23:48:54 +00:00
|
|
|
// +build linux
|
|
|
|
|
2015-02-22 17:24:22 -08:00
|
|
|
package bridge
|
|
|
|
|
|
|
|
import (
|
2015-07-30 06:02:23 -07:00
|
|
|
"fmt"
|
2020-04-03 16:23:18 -07:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2015-07-30 06:02:23 -07:00
|
|
|
|
2021-04-06 00:24:47 +00:00
|
|
|
"github.com/docker/docker/libnetwork/netutils"
|
2017-07-26 14:18:31 -07:00
|
|
|
"github.com/sirupsen/logrus"
|
2015-02-22 17:24:22 -08:00
|
|
|
"github.com/vishvananda/netlink"
|
|
|
|
)
|
|
|
|
|
2015-02-22 21:11:12 -08:00
|
|
|
// SetupDevice create a new bridge interface/
|
2015-05-22 10:56:36 -07:00
|
|
|
func setupDevice(config *networkConfiguration, i *bridgeInterface) error {
|
2015-02-22 17:24:22 -08:00
|
|
|
// We only attempt to create the bridge when the requested device name is
|
|
|
|
// the default one.
|
2015-09-25 09:02:18 -07:00
|
|
|
if config.BridgeName != DefaultBridgeName && config.DefaultBridge {
|
2015-04-17 02:47:12 +00:00
|
|
|
return NonDefaultBridgeExistError(config.BridgeName)
|
2015-02-22 17:24:22 -08:00
|
|
|
}
|
|
|
|
|
2015-03-04 13:25:43 -08:00
|
|
|
// Set the bridgeInterface netlink.Bridge.
|
2015-02-22 17:58:52 -08:00
|
|
|
i.Link = &netlink.Bridge{
|
2015-02-22 17:24:22 -08:00
|
|
|
LinkAttrs: netlink.LinkAttrs{
|
2015-04-15 05:25:42 +00:00
|
|
|
Name: config.BridgeName,
|
2015-02-22 17:24:22 -08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-08-31 20:37:01 +02:00
|
|
|
// Set the bridge's MAC address. Requires kernel version 3.3 or up.
|
|
|
|
hwAddr := netutils.GenerateRandomMAC()
|
|
|
|
i.Link.Attrs().HardwareAddr = hwAddr
|
|
|
|
logrus.Debugf("Setting bridge mac address to %s", hwAddr)
|
bridge: Fix hwaddr set race between us and udev
systemd and udev in their default configuration attempt to set a
persistent MAC address for network interfaces that don't have one
already [systemd-def-link]. We set the address only after creating the
interface, so there is a race between us and udev. There are several
outcomes (that actually occur, this race is very much not a theoretical
one):
* We set the address before udev gets to the networking rules, so udev
sees `/sys/devices/virtual/net/docker0/addr_assign_type = 3`
(NET_ADDR_SET). This means there's no need to assign a different
address and everything is fine.
* udev reads `/sys/devices/virtual/net/docker0/addr_assign_type` before
we set the address, gets `1` (NET_ADDR_RANDOM), and proceeds to
generate and set a persistent address.
Old versions of udev (pre-v242, i.e. without [udev-patch]) would then
fail to generate an address, spit out "Could not generate persistent
MAC address for docker0: No such file or directory" (see [udev-issue],
and everything would be probably fine as well.
Current version of udev (with [udev-patch]) will generate an address
just fine and then race us setting it. As udev does more work than we,
the most probable outcome is that udev will overwrite the address we
set and possibly cause some trouble later on.
On a clean Debian Buster (from Vagrant) VM with systemd/udev 242 from
Debian Experimental, `docker network create net1` up to `net7` resulted
in 3 bridges having a 02:42: address and 4 bridges having a seemingly
random (actually generated from interface name) address. With systemd
241, the result would be all bridges having a 02:42:, but some "Could
not generate persistent MAC address for" messages in the log.
The fix is to revert the MAC address setting fix from 6901ea51dc9f99b9,
as it is no longer necessary with current netlink [netlink-addr-add],
and set the address atomically when creating the bridge interface, not
after that.
[systemd-def-link]: https://github.com/systemd/systemd/blob/a166cd3aacdbfd4df196bb4ca9f43cff19cf9fec/network/99-default.link
[udev-patch]: https://github.com/systemd/systemd/commit/6d36464065601f79a352367cf099be8907d8f9aa
[udev-issue]: https://github.com/systemd/systemd/issues/3374
[netlink-addr-add]: https://github.com/vishvananda/netlink/commit/7d9b424492b5319e5993c5d6e8bef48e583aabd6
...
Do note that a similar race happens when creating veth devices as well.
I wasn't able to reproduce getting a wrong (non-02:42:) address,
possibly because the address is set by docker later, maybe only after
the interface is moved to another network namespace (but I'm just
guessing here). Still, different timings result in various error
messages being logged ("link_config: could not get ethtool features for
vethd9c938e" and the like) depending on when the interface disappears
from the primary network namespace. I'm not sure how to fix this and I
don't intend to dig deeper into this.
Signed-off-by: Tomas Janousek <tomi@nomi.cz>
2019-05-19 18:22:39 +02:00
|
|
|
|
2020-08-31 20:37:01 +02:00
|
|
|
if err := i.nlh.LinkAdd(i.Link); err != nil {
|
2015-07-30 06:02:23 -07:00
|
|
|
logrus.Debugf("Failed to create bridge %s via netlink. Trying ioctl", config.BridgeName)
|
2020-08-31 20:37:01 +02:00
|
|
|
return ioctlCreateBridge(config.BridgeName, hwAddr.String())
|
2015-07-30 06:02:23 -07:00
|
|
|
}
|
|
|
|
|
2020-08-31 20:37:01 +02:00
|
|
|
return nil
|
2015-02-22 17:24:22 -08:00
|
|
|
}
|
|
|
|
|
2020-04-03 16:23:18 -07:00
|
|
|
func setupDefaultSysctl(config *networkConfiguration, i *bridgeInterface) error {
|
|
|
|
// Disable IPv6 router advertisements originating on the bridge
|
|
|
|
sysPath := filepath.Join("/proc/sys/net/ipv6/conf/", config.BridgeName, "accept_ra")
|
|
|
|
if _, err := os.Stat(sysPath); err != nil {
|
|
|
|
logrus.
|
|
|
|
WithField("bridge", config.BridgeName).
|
|
|
|
WithField("syspath", sysPath).
|
|
|
|
Info("failed to read ipv6 net.ipv6.conf.<bridge>.accept_ra")
|
|
|
|
return nil
|
|
|
|
}
|
2021-08-24 18:10:50 +08:00
|
|
|
if err := os.WriteFile(sysPath, []byte{'0', '\n'}, 0644); err != nil {
|
2020-06-05 14:01:18 +02:00
|
|
|
logrus.WithError(err).Warn("unable to disable IPv6 router advertisement")
|
2020-04-03 16:23:18 -07:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-22 21:11:12 -08:00
|
|
|
// SetupDeviceUp ups the given bridge interface.
|
2015-05-22 10:56:36 -07:00
|
|
|
func setupDeviceUp(config *networkConfiguration, i *bridgeInterface) error {
|
2016-05-16 11:51:40 -07:00
|
|
|
err := i.nlh.LinkSetUp(i.Link)
|
2015-02-24 11:19:00 -08:00
|
|
|
if err != nil {
|
2016-05-16 11:51:40 -07:00
|
|
|
return fmt.Errorf("Failed to set link up for %s: %v", config.BridgeName, err)
|
2015-02-24 11:19:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to update the bridge interface to refresh the flags status,
|
|
|
|
// ignoring any failure to do so.
|
2016-05-16 11:51:40 -07:00
|
|
|
if lnk, err := i.nlh.LinkByName(config.BridgeName); err == nil {
|
2015-02-24 11:19:00 -08:00
|
|
|
i.Link = lnk
|
2016-05-16 11:51:40 -07:00
|
|
|
} else {
|
|
|
|
logrus.Warnf("Failed to retrieve link for interface (%s): %v", config.BridgeName, err)
|
2015-02-24 11:19:00 -08:00
|
|
|
}
|
|
|
|
return nil
|
2015-02-22 17:24:22 -08:00
|
|
|
}
|