diff --git a/daemon/networkdriver/bridge/driver.go b/daemon/networkdriver/bridge/driver.go index 06cf37e79..c4b10fe07 100644 --- a/daemon/networkdriver/bridge/driver.go +++ b/daemon/networkdriver/bridge/driver.go @@ -317,14 +317,14 @@ func createBridgeIface(name string) error { // Allocate a network interface func Allocate(job *engine.Job) engine.Status { var ( - ip *net.IP + ip net.IP err error id = job.Args[0] requestedIP = net.ParseIP(job.Getenv("RequestedIP")) ) if requestedIP != nil { - ip, err = ipallocator.RequestIP(bridgeNetwork, &requestedIP) + ip, err = ipallocator.RequestIP(bridgeNetwork, requestedIP) } else { ip, err = ipallocator.RequestIP(bridgeNetwork, nil) } @@ -342,7 +342,7 @@ func Allocate(job *engine.Job) engine.Status { out.SetInt("IPPrefixLen", size) currentInterfaces.Set(id, &networkInterface{ - IP: *ip, + IP: ip, }) out.WriteTo(job.Stdout) @@ -367,7 +367,7 @@ func Release(job *engine.Job) engine.Status { } } - if err := ipallocator.ReleaseIP(bridgeNetwork, &containerInterface.IP); err != nil { + if err := ipallocator.ReleaseIP(bridgeNetwork, containerInterface.IP); err != nil { log.Infof("Unable to release ip %s", err) } return engine.StatusOK diff --git a/daemon/networkdriver/ipallocator/allocator.go b/daemon/networkdriver/ipallocator/allocator.go index 1bf8e1da9..7abb3ffdf 100644 --- a/daemon/networkdriver/ipallocator/allocator.go +++ b/daemon/networkdriver/ipallocator/allocator.go @@ -3,19 +3,30 @@ package ipallocator import ( "encoding/binary" "errors" - "github.com/docker/docker/daemon/networkdriver" "net" "sync" + + "github.com/dotcloud/docker/daemon/networkdriver" ) // allocatedMap is thread-unsafe set of allocated IP type allocatedMap struct { - p map[int32]struct{} - last int32 + p map[uint32]struct{} + last uint32 + begin uint32 + end uint32 } -func newAllocatedMap() *allocatedMap { - return &allocatedMap{p: make(map[int32]struct{})} +func newAllocatedMap(network *net.IPNet) *allocatedMap { + firstIP, lastIP := networkdriver.NetworkRange(network) + begin := ipToInt(firstIP) + 2 + end := ipToInt(lastIP) - 1 + return &allocatedMap{ + p: make(map[uint32]struct{}), + begin: begin, // - network + end: end, // - broadcast + last: begin - 1, // so first allocated will be begin + } } type networkSet map[string]*allocatedMap @@ -34,43 +45,36 @@ var ( // will return the next available ip if the ip provided is nil. If the // ip provided is not nil it will validate that the provided ip is available // for use or return an error -func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { +func RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) { lock.Lock() defer lock.Unlock() key := network.String() allocated, ok := allocatedIPs[key] if !ok { - allocated = newAllocatedMap() + allocated = newAllocatedMap(network) allocatedIPs[key] = allocated } if ip == nil { - return allocated.getNextIP(network) + return allocated.getNextIP() } - return allocated.checkIP(network, ip) + return allocated.checkIP(ip) } // ReleaseIP adds the provided ip back into the pool of // available ips to be returned for use. -func ReleaseIP(network *net.IPNet, ip *net.IP) error { +func ReleaseIP(network *net.IPNet, ip net.IP) error { lock.Lock() defer lock.Unlock() if allocated, exists := allocatedIPs[network.String()]; exists { - pos := getPosition(network, ip) + pos := ipToInt(ip) delete(allocated.p, pos) } return nil } -// convert the ip into the position in the subnet. Only -// position are saved in the set -func getPosition(network *net.IPNet, ip *net.IP) int32 { - first, _ := networkdriver.NetworkRange(network) - return ipToInt(ip) - ipToInt(&first) -} - -func (allocated *allocatedMap) checkIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { - pos := getPosition(network, ip) +func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) { + pos := ipToInt(ip) if _, ok := allocated.p[pos]; ok { return nil, ErrIPAlreadyAllocated } @@ -81,47 +85,30 @@ func (allocated *allocatedMap) checkIP(network *net.IPNet, ip *net.IP) (*net.IP, // return an available ip if one is currently available. If not, // return the next available ip for the nextwork -func (allocated *allocatedMap) getNextIP(network *net.IPNet) (*net.IP, error) { - var ( - ownIP = ipToInt(&network.IP) - first, _ = networkdriver.NetworkRange(network) - base = ipToInt(&first) - size = int(networkdriver.NetworkSize(network.Mask)) - max = int32(size - 2) // size -1 for the broadcast network, -1 for the gateway network - pos = allocated.last - ) - - var ( - firstNetIP = network.IP.To4().Mask(network.Mask) - firstAsInt = ipToInt(&firstNetIP) + 1 - ) - - for i := int32(0); i < max; i++ { - pos = pos%max + 1 - next := int32(base + pos) - - if next == ownIP || next == firstAsInt { - continue +func (allocated *allocatedMap) getNextIP() (net.IP, error) { + for pos := allocated.last + 1; pos != allocated.last; pos++ { + if pos > allocated.end { + pos = allocated.begin } if _, ok := allocated.p[pos]; ok { continue } allocated.p[pos] = struct{}{} allocated.last = pos - return intToIP(next), nil + return intToIP(pos), nil } return nil, ErrNoAvailableIPs } // Converts a 4 bytes IP into a 32 bit integer -func ipToInt(ip *net.IP) int32 { - return int32(binary.BigEndian.Uint32(ip.To4())) +func ipToInt(ip net.IP) uint32 { + return binary.BigEndian.Uint32(ip.To4()) } // Converts 32 bit integer into a 4 bytes IP address -func intToIP(n int32) *net.IP { +func intToIP(n uint32) net.IP { b := make([]byte, 4) - binary.BigEndian.PutUint32(b, uint32(n)) + binary.BigEndian.PutUint32(b, n) ip := net.IP(b) - return &ip + return ip } diff --git a/daemon/networkdriver/ipallocator/allocator_test.go b/daemon/networkdriver/ipallocator/allocator_test.go index 6897a0a44..57575bc7b 100644 --- a/daemon/networkdriver/ipallocator/allocator_test.go +++ b/daemon/networkdriver/ipallocator/allocator_test.go @@ -17,7 +17,7 @@ func TestRequestNewIps(t *testing.T) { Mask: []byte{255, 255, 255, 0}, } - var ip *net.IP + var ip net.IP var err error for i := 2; i < 10; i++ { ip, err = RequestIP(network, nil) @@ -106,19 +106,19 @@ func TestRequesetSpecificIp(t *testing.T) { ip := net.ParseIP("192.168.1.5") - if _, err := RequestIP(network, &ip); err != nil { + if _, err := RequestIP(network, ip); err != nil { t.Fatal(err) } } func TestConversion(t *testing.T) { ip := net.ParseIP("127.0.0.1") - i := ipToInt(&ip) + i := ipToInt(ip) if i == 0 { t.Fatal("converted to zero") } conv := intToIP(i) - if !ip.Equal(*conv) { + if !ip.Equal(conv) { t.Error(conv.String()) } } @@ -146,7 +146,7 @@ func TestIPAllocator(t *testing.T) { t.Fatal(err) } - assertIPEquals(t, &expectedIPs[i], ip) + assertIPEquals(t, expectedIPs[i], ip) } // Before loop begin // 2(f) - 3(f) - 4(f) - 5(f) - 6(f) @@ -179,19 +179,19 @@ func TestIPAllocator(t *testing.T) { } // Release some IPs in non-sequential order - if err := ReleaseIP(network, &expectedIPs[3]); err != nil { + if err := ReleaseIP(network, expectedIPs[3]); err != nil { t.Fatal(err) } // 2(u) - 3(u) - 4(u) - 5(f) - 6(u) // ↑ - if err := ReleaseIP(network, &expectedIPs[2]); err != nil { + if err := ReleaseIP(network, expectedIPs[2]); err != nil { t.Fatal(err) } // 2(u) - 3(u) - 4(f) - 5(f) - 6(u) // ↑ - if err := ReleaseIP(network, &expectedIPs[4]); err != nil { + if err := ReleaseIP(network, expectedIPs[4]); err != nil { t.Fatal(err) } // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) @@ -199,7 +199,7 @@ func TestIPAllocator(t *testing.T) { // Make sure that IPs are reused in sequential order, starting // with the first released IP - newIPs := make([]*net.IP, 3) + newIPs := make([]net.IP, 3) for i := 0; i < 3; i++ { ip, err := RequestIP(network, nil) if err != nil { @@ -208,9 +208,9 @@ func TestIPAllocator(t *testing.T) { newIPs[i] = ip } - assertIPEquals(t, &expectedIPs[2], newIPs[0]) - assertIPEquals(t, &expectedIPs[3], newIPs[1]) - assertIPEquals(t, &expectedIPs[4], newIPs[2]) + assertIPEquals(t, expectedIPs[2], newIPs[0]) + assertIPEquals(t, expectedIPs[3], newIPs[1]) + assertIPEquals(t, expectedIPs[4], newIPs[2]) _, err = RequestIP(network, nil) if err == nil { @@ -226,7 +226,7 @@ func TestAllocateFirstIP(t *testing.T) { } firstIP := network.IP.To4().Mask(network.Mask) - first := ipToInt(&firstIP) + 1 + first := ipToInt(firstIP) + 1 ip, err := RequestIP(network, nil) if err != nil { @@ -247,7 +247,7 @@ func TestAllocateAllIps(t *testing.T) { } var ( - current, first *net.IP + current, first net.IP err error isFirst = true ) @@ -313,14 +313,14 @@ func TestAllocateDifferentSubnets(t *testing.T) { if err != nil { t.Fatal(err) } - assertIPEquals(t, &expectedIPs[0], ip11) - assertIPEquals(t, &expectedIPs[1], ip12) - assertIPEquals(t, &expectedIPs[2], ip21) - assertIPEquals(t, &expectedIPs[3], ip22) + assertIPEquals(t, expectedIPs[0], ip11) + assertIPEquals(t, expectedIPs[1], ip12) + assertIPEquals(t, expectedIPs[2], ip21) + assertIPEquals(t, expectedIPs[3], ip22) } -func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) { - if !ip1.Equal(*ip2) { +func assertIPEquals(t *testing.T, ip1, ip2 net.IP) { + if !ip1.Equal(ip2) { t.Fatalf("Expected IP %s, got %s", ip1, ip2) } }