Support for consistent MAC address.

Right now, MAC addresses are randomly generated by the kernel when
creating the veth interfaces.

This causes different issues related to ARP, such as #4581, #5737 and #8269.

This change adds support for consistent MAC addresses, guaranteeing that
an IP address will always end up with the same MAC address, no matter
what.

Since IP addresses are already guaranteed to be unique by the
IPAllocator, MAC addresses will inherit this property as well for free.

Consistent mac addresses is also a requirement for stable networking (#8297)
since re-using the same IP address on a different MAC address triggers the ARP
issue.

Finally, this change makes the MAC address accessible through docker
inspect, which fixes #4033.

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi 2014-10-02 16:46:06 -07:00
parent 8a0733ce69
commit 88e21c6a75
6 changed files with 54 additions and 1 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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,

View file

@ -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

View file

@ -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()

View file

@ -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")
}
}