Now IP reuses only after all IPs from network was allocated
Fixes #5729
Docker-DCO-1.1-Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com> (github: LK4D4)
| ... | ... |
@@ -7,9 +7,19 @@ import ( |
| 7 | 7 |
"github.com/dotcloud/docker/pkg/collections" |
| 8 | 8 |
"net" |
| 9 | 9 |
"sync" |
| 10 |
+ "sync/atomic" |
|
| 10 | 11 |
) |
| 11 | 12 |
|
| 12 |
-type networkSet map[string]*collections.OrderedIntSet |
|
| 13 |
+type allocatedMap struct {
|
|
| 14 |
+ *collections.OrderedIntSet |
|
| 15 |
+ last int32 |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func newAllocatedMap() *allocatedMap {
|
|
| 19 |
+ return &allocatedMap{OrderedIntSet: collections.NewOrderedIntSet()}
|
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+type networkSet map[string]*allocatedMap |
|
| 13 | 23 |
|
| 14 | 24 |
var ( |
| 15 | 25 |
ErrNoAvailableIPs = errors.New("no available ip addresses on network")
|
| ... | ... |
@@ -19,7 +29,6 @@ var ( |
| 19 | 19 |
var ( |
| 20 | 20 |
lock = sync.Mutex{}
|
| 21 | 21 |
allocatedIPs = networkSet{}
|
| 22 |
- availableIPS = networkSet{}
|
|
| 23 | 22 |
) |
| 24 | 23 |
|
| 25 | 24 |
// RequestIP requests an available ip from the given network. It |
| ... | ... |
@@ -55,13 +64,11 @@ func ReleaseIP(address *net.IPNet, ip *net.IP) error {
|
| 55 | 55 |
checkAddress(address) |
| 56 | 56 |
|
| 57 | 57 |
var ( |
| 58 |
- existing = allocatedIPs[address.String()] |
|
| 59 |
- available = availableIPS[address.String()] |
|
| 58 |
+ allocated = allocatedIPs[address.String()] |
|
| 60 | 59 |
pos = getPosition(address, ip) |
| 61 | 60 |
) |
| 62 | 61 |
|
| 63 |
- existing.Remove(int(pos)) |
|
| 64 |
- available.Push(int(pos)) |
|
| 62 |
+ allocated.Remove(int(pos)) |
|
| 65 | 63 |
|
| 66 | 64 |
return nil |
| 67 | 65 |
} |
| ... | ... |
@@ -82,29 +89,19 @@ func getPosition(address *net.IPNet, ip *net.IP) int32 {
|
| 82 | 82 |
func getNextIp(address *net.IPNet) (*net.IP, error) {
|
| 83 | 83 |
var ( |
| 84 | 84 |
ownIP = ipToInt(&address.IP) |
| 85 |
- available = availableIPS[address.String()] |
|
| 86 | 85 |
allocated = allocatedIPs[address.String()] |
| 87 | 86 |
first, _ = networkdriver.NetworkRange(address) |
| 88 | 87 |
base = ipToInt(&first) |
| 89 | 88 |
size = int(networkdriver.NetworkSize(address.Mask)) |
| 90 | 89 |
max = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address |
| 91 |
- pos = int32(available.Pop()) |
|
| 90 |
+ pos = atomic.LoadInt32(&allocated.last) |
|
| 92 | 91 |
) |
| 93 | 92 |
|
| 94 |
- // We pop and push the position not the ip |
|
| 95 |
- if pos != 0 {
|
|
| 96 |
- ip := intToIP(int32(base + pos)) |
|
| 97 |
- allocated.Push(int(pos)) |
|
| 98 |
- |
|
| 99 |
- return ip, nil |
|
| 100 |
- } |
|
| 101 |
- |
|
| 102 | 93 |
var ( |
| 103 | 94 |
firstNetIP = address.IP.To4().Mask(address.Mask) |
| 104 | 95 |
firstAsInt = ipToInt(&firstNetIP) + 1 |
| 105 | 96 |
) |
| 106 | 97 |
|
| 107 |
- pos = int32(allocated.PullBack()) |
|
| 108 | 98 |
for i := int32(0); i < max; i++ {
|
| 109 | 99 |
pos = pos%max + 1 |
| 110 | 100 |
next := int32(base + pos) |
| ... | ... |
@@ -116,6 +113,7 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
|
| 116 | 116 |
if !allocated.Exists(int(pos)) {
|
| 117 | 117 |
ip := intToIP(next) |
| 118 | 118 |
allocated.Push(int(pos)) |
| 119 |
+ atomic.StoreInt32(&allocated.last, pos) |
|
| 119 | 120 |
return ip, nil |
| 120 | 121 |
} |
| 121 | 122 |
} |
| ... | ... |
@@ -124,15 +122,14 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
|
| 124 | 124 |
|
| 125 | 125 |
func registerIP(address *net.IPNet, ip *net.IP) error {
|
| 126 | 126 |
var ( |
| 127 |
- existing = allocatedIPs[address.String()] |
|
| 128 |
- available = availableIPS[address.String()] |
|
| 127 |
+ allocated = allocatedIPs[address.String()] |
|
| 129 | 128 |
pos = getPosition(address, ip) |
| 130 | 129 |
) |
| 131 | 130 |
|
| 132 |
- if existing.Exists(int(pos)) {
|
|
| 131 |
+ if allocated.Exists(int(pos)) {
|
|
| 133 | 132 |
return ErrIPAlreadyAllocated |
| 134 | 133 |
} |
| 135 |
- available.Remove(int(pos)) |
|
| 134 |
+ atomic.StoreInt32(&allocated.last, pos) |
|
| 136 | 135 |
|
| 137 | 136 |
return nil |
| 138 | 137 |
} |
| ... | ... |
@@ -153,7 +150,6 @@ func intToIP(n int32) *net.IP {
|
| 153 | 153 |
func checkAddress(address *net.IPNet) {
|
| 154 | 154 |
key := address.String() |
| 155 | 155 |
if _, exists := allocatedIPs[key]; !exists {
|
| 156 |
- allocatedIPs[key] = collections.NewOrderedIntSet() |
|
| 157 |
- availableIPS[key] = collections.NewOrderedIntSet() |
|
| 156 |
+ allocatedIPs[key] = newAllocatedMap() |
|
| 158 | 157 |
} |
| 159 | 158 |
} |
| ... | ... |
@@ -8,7 +8,6 @@ import ( |
| 8 | 8 |
|
| 9 | 9 |
func reset() {
|
| 10 | 10 |
allocatedIPs = networkSet{}
|
| 11 |
- availableIPS = networkSet{}
|
|
| 12 | 11 |
} |
| 13 | 12 |
|
| 14 | 13 |
func TestRequestNewIps(t *testing.T) {
|
| ... | ... |
@@ -18,8 +17,10 @@ func TestRequestNewIps(t *testing.T) {
|
| 18 | 18 |
Mask: []byte{255, 255, 255, 0},
|
| 19 | 19 |
} |
| 20 | 20 |
|
| 21 |
+ var ip *net.IP |
|
| 22 |
+ var err error |
|
| 21 | 23 |
for i := 2; i < 10; i++ {
|
| 22 |
- ip, err := RequestIP(network, nil) |
|
| 24 |
+ ip, err = RequestIP(network, nil) |
|
| 23 | 25 |
if err != nil {
|
| 24 | 26 |
t.Fatal(err) |
| 25 | 27 |
} |
| ... | ... |
@@ -28,6 +29,17 @@ func TestRequestNewIps(t *testing.T) {
|
| 28 | 28 |
t.Fatalf("Expected ip %s got %s", expected, ip.String())
|
| 29 | 29 |
} |
| 30 | 30 |
} |
| 31 |
+ value := intToIP(ipToInt(ip) + 1).String() |
|
| 32 |
+ if err := ReleaseIP(network, ip); err != nil {
|
|
| 33 |
+ t.Fatal(err) |
|
| 34 |
+ } |
|
| 35 |
+ ip, err = RequestIP(network, nil) |
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ t.Fatal(err) |
|
| 38 |
+ } |
|
| 39 |
+ if ip.String() != value {
|
|
| 40 |
+ t.Fatalf("Expected to receive the next ip %s got %s", value, ip.String())
|
|
| 41 |
+ } |
|
| 31 | 42 |
} |
| 32 | 43 |
|
| 33 | 44 |
func TestReleaseIp(t *testing.T) {
|
| ... | ... |
@@ -64,6 +76,17 @@ func TestGetReleasedIp(t *testing.T) {
|
| 64 | 64 |
t.Fatal(err) |
| 65 | 65 |
} |
| 66 | 66 |
|
| 67 |
+ for i := 0; i < 252; i++ {
|
|
| 68 |
+ _, err = RequestIP(network, nil) |
|
| 69 |
+ if err != nil {
|
|
| 70 |
+ t.Fatal(err) |
|
| 71 |
+ } |
|
| 72 |
+ err = ReleaseIP(network, ip) |
|
| 73 |
+ if err != nil {
|
|
| 74 |
+ t.Fatal(err) |
|
| 75 |
+ } |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 67 | 78 |
ip, err = RequestIP(network, nil) |
| 68 | 79 |
if err != nil {
|
| 69 | 80 |
t.Fatal(err) |
| ... | ... |
@@ -185,24 +208,6 @@ func TestIPAllocator(t *testing.T) {
|
| 185 | 185 |
|
| 186 | 186 |
newIPs[i] = ip |
| 187 | 187 |
} |
| 188 |
- // Before loop begin |
|
| 189 |
- // 2(u) - 3(u) - 4(f) - 5(f) - 6(f) |
|
| 190 |
- // ↑ |
|
| 191 |
- |
|
| 192 |
- // After i = 0 |
|
| 193 |
- // 2(u) - 3(u) - 4(f) - 5(u) - 6(f) |
|
| 194 |
- // ↑ |
|
| 195 |
- |
|
| 196 |
- // After i = 1 |
|
| 197 |
- // 2(u) - 3(u) - 4(f) - 5(u) - 6(u) |
|
| 198 |
- // ↑ |
|
| 199 |
- |
|
| 200 |
- // After i = 2 |
|
| 201 |
- // 2(u) - 3(u) - 4(u) - 5(u) - 6(u) |
|
| 202 |
- // ↑ |
|
| 203 |
- |
|
| 204 |
- // Reordered these because the new set will always return the |
|
| 205 |
- // lowest ips first and not in the order that they were released |
|
| 206 | 188 |
assertIPEquals(t, &expectedIPs[2], newIPs[0]) |
| 207 | 189 |
assertIPEquals(t, &expectedIPs[3], newIPs[1]) |
| 208 | 190 |
assertIPEquals(t, &expectedIPs[4], newIPs[2]) |
| ... | ... |
@@ -234,6 +239,86 @@ func TestAllocateFirstIP(t *testing.T) {
|
| 234 | 234 |
} |
| 235 | 235 |
} |
| 236 | 236 |
|
| 237 |
+func TestAllocateAllIps(t *testing.T) {
|
|
| 238 |
+ defer reset() |
|
| 239 |
+ network := &net.IPNet{
|
|
| 240 |
+ IP: []byte{192, 168, 0, 1},
|
|
| 241 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 242 |
+ } |
|
| 243 |
+ |
|
| 244 |
+ var ( |
|
| 245 |
+ current, first *net.IP |
|
| 246 |
+ err error |
|
| 247 |
+ isFirst = true |
|
| 248 |
+ ) |
|
| 249 |
+ |
|
| 250 |
+ for err == nil {
|
|
| 251 |
+ current, err = RequestIP(network, nil) |
|
| 252 |
+ if isFirst {
|
|
| 253 |
+ first = current |
|
| 254 |
+ isFirst = false |
|
| 255 |
+ } |
|
| 256 |
+ } |
|
| 257 |
+ |
|
| 258 |
+ if err != ErrNoAvailableIPs {
|
|
| 259 |
+ t.Fatal(err) |
|
| 260 |
+ } |
|
| 261 |
+ |
|
| 262 |
+ if _, err := RequestIP(network, nil); err != ErrNoAvailableIPs {
|
|
| 263 |
+ t.Fatal(err) |
|
| 264 |
+ } |
|
| 265 |
+ |
|
| 266 |
+ if err := ReleaseIP(network, first); err != nil {
|
|
| 267 |
+ t.Fatal(err) |
|
| 268 |
+ } |
|
| 269 |
+ |
|
| 270 |
+ again, err := RequestIP(network, nil) |
|
| 271 |
+ if err != nil {
|
|
| 272 |
+ t.Fatal(err) |
|
| 273 |
+ } |
|
| 274 |
+ |
|
| 275 |
+ assertIPEquals(t, first, again) |
|
| 276 |
+} |
|
| 277 |
+ |
|
| 278 |
+func TestAllocateDifferentSubnets(t *testing.T) {
|
|
| 279 |
+ defer reset() |
|
| 280 |
+ network1 := &net.IPNet{
|
|
| 281 |
+ IP: []byte{192, 168, 0, 1},
|
|
| 282 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 283 |
+ } |
|
| 284 |
+ network2 := &net.IPNet{
|
|
| 285 |
+ IP: []byte{127, 0, 0, 1},
|
|
| 286 |
+ Mask: []byte{255, 255, 255, 0},
|
|
| 287 |
+ } |
|
| 288 |
+ expectedIPs := []net.IP{
|
|
| 289 |
+ 0: net.IPv4(192, 168, 0, 2), |
|
| 290 |
+ 1: net.IPv4(192, 168, 0, 3), |
|
| 291 |
+ 2: net.IPv4(127, 0, 0, 2), |
|
| 292 |
+ 3: net.IPv4(127, 0, 0, 3), |
|
| 293 |
+ } |
|
| 294 |
+ |
|
| 295 |
+ ip11, err := RequestIP(network1, nil) |
|
| 296 |
+ if err != nil {
|
|
| 297 |
+ t.Fatal(err) |
|
| 298 |
+ } |
|
| 299 |
+ ip12, err := RequestIP(network1, nil) |
|
| 300 |
+ if err != nil {
|
|
| 301 |
+ t.Fatal(err) |
|
| 302 |
+ } |
|
| 303 |
+ ip21, err := RequestIP(network2, nil) |
|
| 304 |
+ if err != nil {
|
|
| 305 |
+ t.Fatal(err) |
|
| 306 |
+ } |
|
| 307 |
+ ip22, err := RequestIP(network2, nil) |
|
| 308 |
+ if err != nil {
|
|
| 309 |
+ t.Fatal(err) |
|
| 310 |
+ } |
|
| 311 |
+ assertIPEquals(t, &expectedIPs[0], ip11) |
|
| 312 |
+ assertIPEquals(t, &expectedIPs[1], ip12) |
|
| 313 |
+ assertIPEquals(t, &expectedIPs[2], ip21) |
|
| 314 |
+ assertIPEquals(t, &expectedIPs[3], ip22) |
|
| 315 |
+} |
|
| 316 |
+ |
|
| 237 | 317 |
func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) {
|
| 238 | 318 |
if !ip1.Equal(*ip2) {
|
| 239 | 319 |
t.Fatalf("Expected IP %s, got %s", ip1, ip2)
|