diff --git a/daemon/container.go b/daemon/container.go index a95815ca73..0d97d726a1 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -215,6 +215,7 @@ func populateCommand(c *Container, env []string) error { Bridge: network.Bridge, IPAddress: network.IPAddress, IPPrefixLen: network.IPPrefixLen, + MacAddress: network.MacAddress, } } case "container": @@ -504,6 +505,7 @@ func (container *Container) allocateNetwork() error { container.NetworkSettings.Bridge = env.Get("Bridge") container.NetworkSettings.IPAddress = env.Get("IP") container.NetworkSettings.IPPrefixLen = env.GetInt("IPPrefixLen") + container.NetworkSettings.MacAddress = env.Get("MacAddress") container.NetworkSettings.Gateway = env.Get("Gateway") return nil diff --git a/daemon/execdriver/driver.go b/daemon/execdriver/driver.go index 0be2d50dc4..22e4c4647c 100644 --- a/daemon/execdriver/driver.go +++ b/daemon/execdriver/driver.go @@ -65,8 +65,9 @@ type Network struct { type NetworkInterface struct { Gateway string `json:"gateway"` IPAddress string `json:"ip"` - Bridge string `json:"bridge"` IPPrefixLen int `json:"ip_prefix_len"` + MacAddress string `json:"mac_address"` + Bridge string `json:"bridge"` } type Resources struct { diff --git a/daemon/execdriver/native/create.go b/daemon/execdriver/native/create.go index 7ba69efece..492247e492 100644 --- a/daemon/execdriver/native/create.go +++ b/daemon/execdriver/native/create.go @@ -95,6 +95,7 @@ func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Com vethNetwork := libcontainer.Network{ Mtu: c.Network.Mtu, Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen), + MacAddress: c.Network.Interface.MacAddress, Gateway: c.Network.Interface.Gateway, Type: "veth", Bridge: c.Network.Interface.Bridge, diff --git a/daemon/network_settings.go b/daemon/network_settings.go index bf28ca1b50..69c15be3db 100644 --- a/daemon/network_settings.go +++ b/daemon/network_settings.go @@ -11,6 +11,7 @@ type PortMapping map[string]string // Deprecated type NetworkSettings struct { IPAddress string IPPrefixLen int + MacAddress string Gateway string Bridge string PortMapping map[string]PortMapping // Deprecated diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index 3135f1d42e..e05a2c21a5 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -326,10 +326,36 @@ func createBridgeIface(name string) error { return netlink.CreateBridge(name, setBridgeMacAddr) } +// 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 +} + // Allocate a network interface func Allocate(job *engine.Job) engine.Status { var ( ip net.IP + mac net.HardwareAddr err error id = job.Args[0] requestedIP = net.ParseIP(job.Getenv("RequestedIP")) @@ -344,10 +370,16 @@ func Allocate(job *engine.Job) engine.Status { return job.Error(err) } + // If no explicit mac address was given, generate a random one. + if mac, err = net.ParseMAC(job.Getenv("RequestedMac")); err != nil { + mac = generateMacAddr(ip) + } + out := engine.Env{} out.Set("IP", ip.String()) out.Set("Mask", bridgeNetwork.Mask.String()) out.Set("Gateway", bridgeNetwork.IP.String()) + out.Set("MacAddress", mac.String()) out.Set("Bridge", bridgeIface) size, _ := bridgeNetwork.Mask.Size() diff --git a/daemon/networkdriver/bridge/driver_test.go b/daemon/networkdriver/bridge/driver_test.go index 883b3a6d21..1bda2f4372 100644 --- a/daemon/networkdriver/bridge/driver_test.go +++ b/daemon/networkdriver/bridge/driver_test.go @@ -102,3 +102,19 @@ func TestHostnameFormatChecking(t *testing.T) { t.Fatal("Failed to check invalid HostIP") } } + +func TestMacAddrGeneration(t *testing.T) { + ip := net.ParseIP("192.168.0.1") + mac := generateMacAddr(ip).String() + + // Should be consistent. + if generateMacAddr(ip).String() != mac { + t.Fatal("Inconsistent MAC address") + } + + // Should be unique. + ip2 := net.ParseIP("192.168.0.2") + if generateMacAddr(ip2).String() == mac { + t.Fatal("Non-unique MAC address") + } +}