Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
| ... | ... |
@@ -19,6 +19,7 @@ type iPNet struct {
|
| 19 | 19 |
var ( |
| 20 | 20 |
ErrNetworkAlreadyAllocated = errors.New("requested network overlaps with existing network")
|
| 21 | 21 |
ErrNetworkAlreadyRegisterd = errors.New("requested network is already registered")
|
| 22 |
+ ErrNoAvailableIps = errors.New("no available ips on network")
|
|
| 22 | 23 |
lock = sync.Mutex{}
|
| 23 | 24 |
allocatedIPs = networkSet{}
|
| 24 | 25 |
availableIPS = networkSet{}
|
| ... | ... |
@@ -40,109 +41,87 @@ func RegisterNetwork(network *net.IPNet) error {
|
| 40 | 40 |
if err := checkExistingNetworkOverlaps(network); err != nil {
|
| 41 | 41 |
return err |
| 42 | 42 |
} |
| 43 |
- |
|
| 44 | 43 |
allocatedIPs[newIPNet(network)] = iPSet{}
|
| 45 | 44 |
|
| 46 | 45 |
return nil |
| 47 | 46 |
} |
| 48 | 47 |
|
| 49 |
-func RequestIP(ip *net.IPAddr) (*net.IPAddr, error) {
|
|
| 48 |
+func RequestIP(network *net.IPNet, ip *net.IPAddr) (*net.IPAddr, error) {
|
|
| 50 | 49 |
lock.Lock() |
| 51 | 50 |
defer lock.Unlock() |
| 52 | 51 |
|
| 53 | 52 |
if ip == nil {
|
| 54 |
- next, err := getNextIp() |
|
| 53 |
+ next, err := getNextIp(network) |
|
| 55 | 54 |
if err != nil {
|
| 56 | 55 |
return nil, err |
| 57 | 56 |
} |
| 58 | 57 |
return next, nil |
| 59 | 58 |
} |
| 60 | 59 |
|
| 61 |
- if err := validateIP(ip); err != nil {
|
|
| 60 |
+ if err := validateIP(network, ip); err != nil {
|
|
| 62 | 61 |
return nil, err |
| 63 | 62 |
} |
| 64 | 63 |
return ip, nil |
| 65 | 64 |
} |
| 66 | 65 |
|
| 67 |
-func ReleaseIP(ip *net.IPAddr) error {
|
|
| 66 |
+func ReleaseIP(network *net.IPNet, ip *net.IPAddr) error {
|
|
| 68 | 67 |
lock.Lock() |
| 69 | 68 |
defer lock.Unlock() |
| 70 | 69 |
|
| 70 |
+ n := newIPNet(network) |
|
| 71 |
+ existing := allocatedIPs[n] |
|
| 72 |
+ |
|
| 73 |
+ delete(existing, ip.String()) |
|
| 74 |
+ availableIPS[n][ip.String()] = struct{}{}
|
|
| 75 |
+ |
|
| 76 |
+ return nil |
|
| 71 | 77 |
} |
| 72 | 78 |
|
| 73 |
-func getNextIp(network iPNet) (net.IPAddr, error) {
|
|
| 79 |
+func getNextIp(network *net.IPNet) (net.IPAddr, error) {
|
|
| 74 | 80 |
if available, exists := availableIPS[network]; exists {
|
| 81 |
+ return nil, nil |
|
| 75 | 82 |
} |
| 76 | 83 |
|
| 77 | 84 |
var ( |
| 78 |
- netNetwork = newNetIPNet(network) |
|
| 79 |
- firstIP, _ = networkRange(netNetwork) |
|
| 85 |
+ firstIP, _ = networkRange(network) |
|
| 80 | 86 |
ipNum = ipToInt(firstIP) |
| 81 |
- ownIP = ipToInt(netNetwork.IP) |
|
| 82 |
- size = networkSize(netNetwork.Mask) |
|
| 83 |
- |
|
| 84 |
- pos = int32(1) |
|
| 85 |
- max = size - 2 // -1 for the broadcast address, -1 for the gateway address |
|
| 87 |
+ ownIP = ipToInt(network.IP) |
|
| 88 |
+ size = networkSize(network.Mask) |
|
| 89 |
+ n = newIPNet(network) |
|
| 90 |
+ allocated = allocatedIPs[n] |
|
| 91 |
+ |
|
| 92 |
+ pos = int32(1) |
|
| 93 |
+ max = size - 2 // -1 for the broadcast address, -1 for the gateway address |
|
| 94 |
+ ip *net.IP |
|
| 95 |
+ newNum int32 |
|
| 96 |
+ inUse bool |
|
| 86 | 97 |
) |
| 87 | 98 |
|
| 88 |
- for {
|
|
| 89 |
- var ( |
|
| 90 |
- newNum int32 |
|
| 91 |
- inUse bool |
|
| 92 |
- ) |
|
| 93 |
- |
|
| 94 |
- // Find first unused IP, give up after one whole round |
|
| 95 |
- for attempt := int32(0); attempt < max; attempt++ {
|
|
| 96 |
- newNum = ipNum + pos |
|
| 97 |
- |
|
| 98 |
- pos = pos%max + 1 |
|
| 99 |
- |
|
| 100 |
- // The network's IP is never okay to use |
|
| 101 |
- if newNum == ownIP {
|
|
| 102 |
- continue |
|
| 103 |
- } |
|
| 104 |
- |
|
| 105 |
- if _, inUse = alloc.inUse[newNum]; !inUse {
|
|
| 106 |
- // We found an unused IP |
|
| 107 |
- break |
|
| 108 |
- } |
|
| 99 |
+ // Find first unused IP, give up after one whole round |
|
| 100 |
+ for attempt := int32(0); attempt < max; attempt++ {
|
|
| 101 |
+ newNum = ipNum + pos |
|
| 102 |
+ pos = pos%max + 1 |
|
| 103 |
+ // The network's IP is never okay to use |
|
| 104 |
+ if newNum == ownIP {
|
|
| 105 |
+ continue |
|
| 109 | 106 |
} |
| 110 | 107 |
|
| 111 |
- ip := allocatedIP{ip: intToIP(newNum)}
|
|
| 112 |
- if inUse {
|
|
| 113 |
- ip.err = errors.New("No unallocated IP available")
|
|
| 108 |
+ ip = intToIP(newNum) |
|
| 109 |
+ if _, inUse = allocated[ip.String()]; !inUse {
|
|
| 110 |
+ // We found an unused IP |
|
| 111 |
+ break |
|
| 114 | 112 |
} |
| 113 |
+ } |
|
| 115 | 114 |
|
| 116 |
- select {
|
|
| 117 |
- case quit := <-alloc.quit: |
|
| 118 |
- if quit {
|
|
| 119 |
- return |
|
| 120 |
- } |
|
| 121 |
- case alloc.queueAlloc <- ip: |
|
| 122 |
- alloc.inUse[newNum] = struct{}{}
|
|
| 123 |
- case released := <-alloc.queueReleased: |
|
| 124 |
- r := ipToInt(released) |
|
| 125 |
- delete(alloc.inUse, r) |
|
| 126 |
- |
|
| 127 |
- if inUse {
|
|
| 128 |
- // If we couldn't allocate a new IP, the released one |
|
| 129 |
- // will be the only free one now, so instantly use it |
|
| 130 |
- // next time |
|
| 131 |
- pos = r - ipNum |
|
| 132 |
- } else {
|
|
| 133 |
- // Use same IP as last time |
|
| 134 |
- if pos == 1 {
|
|
| 135 |
- pos = max |
|
| 136 |
- } else {
|
|
| 137 |
- pos-- |
|
| 138 |
- } |
|
| 139 |
- } |
|
| 140 |
- } |
|
| 115 |
+ if ip == nil {
|
|
| 116 |
+ return nil, ErrNoAvailableIps |
|
| 141 | 117 |
} |
| 118 |
+ allocated[ip.String()] = struct{}{}
|
|
| 142 | 119 |
|
| 120 |
+ return ip, nil |
|
| 143 | 121 |
} |
| 144 | 122 |
|
| 145 |
-func validateIP(ip *net.IPAddr) error {
|
|
| 123 |
+func validateIP(network *net.IPNet, ip *net.IPAddr) error {
|
|
| 146 | 124 |
|
| 147 | 125 |
} |
| 148 | 126 |
|