diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index d64733eba6..87bd7e3901 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -852,14 +852,6 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn } }() - // Set the sbox's MAC. If specified, use the one configured by user, otherwise use a random one - mac := electMacAddress(epConfig) - err = netlink.LinkSetHardwareAddr(sbox, mac) - if err != nil { - return err - } - endpoint.macAddress = mac - n.Lock() config := n.config n.Unlock() @@ -889,6 +881,14 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn } ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask} + // Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP. + mac := electMacAddress(epConfig, ip4) + err = netlink.LinkSetHardwareAddr(sbox, mac) + if err != nil { + return err + } + endpoint.macAddress = mac + // v6 address for the sandbox side pipe interface ipv6Addr = &net.IPNet{} if config.EnableIPv6 { @@ -1289,9 +1289,34 @@ func parseContainerOptions(cOptions map[string]interface{}) (*containerConfigura } } -func electMacAddress(epConfig *endpointConfiguration) net.HardwareAddr { +// Generate a IEEE802 compliant MAC address from the given IP address. +// +// The generator is guaranteed to be consistent: the same IP will always yield the same +// MAC address. This is to avoid ARP cache issues. +func generateMacAddr(ip net.IP) net.HardwareAddr { + hw := make(net.HardwareAddr, 6) + + // The first byte of the MAC address has to comply with these rules: + // 1. Unicast: Set the least-significant bit to 0. + // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. + // 3. As "small" as possible: The veth address has to be "smaller" than the bridge address. + hw[0] = 0x02 + + // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). + // Since this address is locally administered, we can do whatever we want as long as + // it doesn't conflict with other addresses. + hw[1] = 0x42 + + // Insert the IP address into the last 32 bits of the MAC address. + // This is a simple way to guarantee the address will be consistent and unique. + copy(hw[2:], ip.To4()) + + return hw +} + +func electMacAddress(epConfig *endpointConfiguration, ip net.IP) net.HardwareAddr { if epConfig != nil && epConfig.MacAddress != nil { return epConfig.MacAddress } - return netutils.GenerateRandomMAC() + return generateMacAddr(ip) }