diff --git a/network.go b/network.go index 4987140402..7414b348a2 100644 --- a/network.go +++ b/network.go @@ -60,10 +60,13 @@ func CreateBridgeIface(config *DaemonConfig) error { var ifaceAddr string if len(config.BridgeIp) != 0 { - _, _, err := net.ParseCIDR(config.BridgeIp) + _, dockerNetwork, err := net.ParseCIDR(config.BridgeIp) if err != nil { return err } + if err := ipallocator.RegisterNetwork(dockerNetwork, nameservers); err != nil { + return err + } ifaceAddr = config.BridgeIp } else { for _, addr := range addrs { @@ -534,6 +537,7 @@ func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) { return manager, nil } + var network *net.IPNet addr, err := getIfaceAddr(config.BridgeIface) if err != nil { // If the iface is not found, try to create it @@ -544,8 +548,13 @@ func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) { if err != nil { return nil, err } + network = addr.(*net.IPNet) + } else { + network = addr.(*net.IPNet) + if err := ipallocator.RegisterExistingNetwork(network); err != nil { + return nil, err + } } - network := addr.(*net.IPNet) // Configure iptables for link support if config.EnableIptables { diff --git a/networkdriver/ipallocator/allocator.go b/networkdriver/ipallocator/allocator.go index 8b7c986461..cca8cdb05a 100644 --- a/networkdriver/ipallocator/allocator.go +++ b/networkdriver/ipallocator/allocator.go @@ -21,12 +21,16 @@ var ( ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") ErrNoAvailableIPs = errors.New("no available ip addresses on network") ErrIPAlreadyAllocated = errors.New("ip already allocated") + ErrNetworkNotRegistered = errors.New("network not registered") lock = sync.Mutex{} allocatedIPs = networkSet{} availableIPS = networkSet{} ) +// RegisterNetwork registers a new network with the allocator +// and validates that it contains a valid ip that does not overlap +// with existing routes and nameservers func RegisterNetwork(network *net.IPNet, nameservers []string) error { lock.Lock() defer lock.Unlock() @@ -47,19 +51,36 @@ func RegisterNetwork(network *net.IPNet, nameservers []string) error { if err := checkNameserverOverlaps(nameservers, network); err != nil { return err } + return RegisterExistingNetwork(network) +} +// RegisterExistingNetwork registers an exising network created +// for use with the allocator but does not perform any validation +func RegisterExistingNetwork(network *net.IPNet) error { n := newIPNet(network) - allocatedIPs[n] = &iPSet{} - availableIPS[n] = &iPSet{} + if _, exists := allocatedIPs[n]; !exists { + allocatedIPs[n] = &iPSet{} + } + if _, exists := availableIPS[n]; !exists { + availableIPS[n] = &iPSet{} + } return nil } +// RequestIP requests an available ip from the given network. It +// 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) { lock.Lock() defer lock.Unlock() + if !networkExists(network) { + return nil, ErrNetworkNotRegistered + } + if ip == nil { next, err := getNextIp(network) if err != nil { @@ -74,18 +95,21 @@ func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) { return ip, nil } +// 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 { lock.Lock() defer lock.Unlock() + if !networkExists(network) { + return ErrNetworkNotRegistered + } + var ( - first, _ = networkRange(network) - base = ipToInt(&first) n = newIPNet(network) existing = allocatedIPs[n] available = availableIPS[n] - i = ipToInt(ip) - pos = i - base + pos = getPosition(network, ip) ) existing.Remove(int(pos)) @@ -94,6 +118,19 @@ func ReleaseIP(network *net.IPNet, ip *net.IP) error { 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 { + var ( + first, _ = networkRange(network) + base = ipToInt(&first) + i = ipToInt(ip) + ) + return i - base +} + +// return an available ip if one is currently available. If not, +// return the next available ip for the nextwork func getNextIp(network *net.IPNet) (*net.IP, error) { var ( n = newIPNet(network) @@ -134,11 +171,18 @@ func getNextIp(network *net.IPNet) (*net.IP, error) { } func registerIP(network *net.IPNet, ip *net.IP) error { - existing := allocatedIPs[newIPNet(network)] - // checking position not ip - if existing.Exists(int(ipToInt(ip))) { + var ( + n = newIPNet(network) + existing = allocatedIPs[n] + available = availableIPS[n] + pos = getPosition(network, ip) + ) + + if existing.Exists(int(pos)) { return ErrIPAlreadyAllocated } + available.Remove(int(pos)) + return nil } @@ -241,3 +285,9 @@ func checkNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error { } return nil } + +func networkExists(network *net.IPNet) bool { + n := newIPNet(network) + _, exists := allocatedIPs[n] + return exists +} diff --git a/networkdriver/ipallocator/allocator_test.go b/networkdriver/ipallocator/allocator_test.go index 2e2d463638..f574dfda70 100644 --- a/networkdriver/ipallocator/allocator_test.go +++ b/networkdriver/ipallocator/allocator_test.go @@ -386,6 +386,8 @@ func TestIPAllocator(t *testing.T) { // 2(u) - 3(u) - 4(u) - 5(u) - 6(u) // ↑ + // Reordered these because the new set will always return the + // lowest ips first and not in the order that they were released assertIPEquals(t, &expectedIPs[2], newIPs[0]) assertIPEquals(t, &expectedIPs[3], newIPs[1]) assertIPEquals(t, &expectedIPs[4], newIPs[2])