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:
parent
8a0733ce69
commit
88e21c6a75
6 changed files with 54 additions and 1 deletions
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue