diff --git a/network.go b/network.go index 1139f6ad37..473df0ffdb 100644 --- a/network.go +++ b/network.go @@ -1,7 +1,6 @@ package docker import ( - "encoding/binary" "errors" "fmt" "github.com/dotcloud/docker/pkg/iptables" @@ -25,61 +24,6 @@ const ( siocBRADDBR = 0x89a0 ) -// Calculates the first and last IP addresses in an IPNet -func networkRange(network *net.IPNet) (net.IP, net.IP) { - netIP := network.IP.To4() - firstIP := netIP.Mask(network.Mask) - lastIP := net.IPv4(0, 0, 0, 0).To4() - for i := 0; i < len(lastIP); i++ { - lastIP[i] = netIP[i] | ^network.Mask[i] - } - return firstIP, lastIP -} - -// Detects overlap between one IPNet and another -func networkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { - firstIP, _ := networkRange(netX) - if netY.Contains(firstIP) { - return true - } - firstIP, _ = networkRange(netY) - if netX.Contains(firstIP) { - return true - } - return false -} - -// Converts a 4 bytes IP into a 32 bit integer -func ipToInt(ip net.IP) int32 { - return int32(binary.BigEndian.Uint32(ip.To4())) -} - -// Converts 32 bit integer into a 4 bytes IP address -func intToIP(n int32) net.IP { - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, uint32(n)) - return net.IP(b) -} - -// Given a netmask, calculates the number of available hosts -func networkSize(mask net.IPMask) int32 { - m := net.IPv4Mask(0, 0, 0, 0) - for i := 0; i < net.IPv4len; i++ { - m[i] = ^mask[i] - } - - return int32(binary.BigEndian.Uint32(m)) + 1 -} - -func checkRouteOverlaps(networks []netlink.Route, dockerNetwork *net.IPNet) error { - for _, network := range networks { - if network.IPNet != nil && networkOverlaps(dockerNetwork, network.IPNet) { - return fmt.Errorf("Network %s is already routed: '%s'", dockerNetwork, network) - } - } - return nil -} - func checkNameserverOverlaps(nameservers []string, dockerNetwork *net.IPNet) error { if len(nameservers) > 0 { for _, ns := range nameservers { @@ -142,10 +86,7 @@ func CreateBridgeIface(config *DaemonConfig) error { if err != nil { return err } - routes, err := netlink.NetworkGetRoutes() - if err != nil { - return err - } + // TODO: @crosbymichael register route if err := checkRouteOverlaps(routes, dockerNetwork); err == nil { if err := checkNameserverOverlaps(nameservers, dockerNetwork); err == nil { ifaceAddr = addr @@ -441,67 +382,6 @@ type allocatedIP struct { } func (alloc *IPAllocator) run() { - firstIP, _ := networkRange(alloc.network) - ipNum := ipToInt(firstIP) - ownIP := ipToInt(alloc.network.IP) - size := networkSize(alloc.network.Mask) - - pos := int32(1) - max := size - 2 // -1 for the broadcast address, -1 for the gateway address - for { - var ( - newNum int32 - inUse bool - ) - - // Find first unused IP, give up after one whole round - for attempt := int32(0); attempt < max; attempt++ { - newNum = ipNum + pos - - pos = pos%max + 1 - - // The network's IP is never okay to use - if newNum == ownIP { - continue - } - - if _, inUse = alloc.inUse[newNum]; !inUse { - // We found an unused IP - break - } - } - - ip := allocatedIP{ip: intToIP(newNum)} - if inUse { - ip.err = errors.New("No unallocated IP available") - } - - select { - case quit := <-alloc.quit: - if quit { - return - } - case alloc.queueAlloc <- ip: - alloc.inUse[newNum] = struct{}{} - case released := <-alloc.queueReleased: - r := ipToInt(released) - delete(alloc.inUse, r) - - if inUse { - // If we couldn't allocate a new IP, the released one - // will be the only free one now, so instantly use it - // next time - pos = r - ipNum - } else { - // Use same IP as last time - if pos == 1 { - pos = max - } else { - pos-- - } - } - } - } } func (alloc *IPAllocator) Acquire() (net.IP, error) { diff --git a/networkdriver/portallocator/allocator.go b/networkdriver/portallocator/allocator.go new file mode 100644 index 0000000000..5d8e812309 --- /dev/null +++ b/networkdriver/portallocator/allocator.go @@ -0,0 +1,229 @@ +package ipallocator + +import ( + "encoding/binary" + "errors" + "github.com/dotcloud/docker/pkg/netlink" + "net" + "sync" +) + +type networkSet map[iPNet]iPSet +type iPSet map[string]struct{} + +type iPNet struct { + IP string + Mask string +} + +var ( + ErrNetworkAlreadyAllocated = errors.New("requested network overlaps with existing network") + ErrNetworkAlreadyRegisterd = errors.New("requested network is already registered") + lock = sync.Mutex{} + allocatedIPs = networkSet{} + availableIPS = networkSet{} +) + +func RegisterNetwork(network *net.IPNet) error { + lock.Lock() + defer lock.Unlock() + + routes, err := netlink.NetworkGetRoutes() + if err != nil { + return err + } + + if err := checkRouteOverlaps(routes, network); err != nil { + return err + } + + if err := checkExistingNetworkOverlaps(network); err != nil { + return err + } + + allocatedIPs[newIPNet(network)] = iPSet{} + + return nil +} + +func RequestIP(ip *net.IPAddr) (*net.IPAddr, error) { + lock.Lock() + defer lock.Unlock() + + if ip == nil { + next, err := getNextIp() + if err != nil { + return nil, err + } + return next, nil + } + + if err := validateIP(ip); err != nil { + return nil, err + } + return ip, nil +} + +func ReleaseIP(ip *net.IPAddr) error { + lock.Lock() + defer lock.Unlock() + +} + +func getNextIp(network iPNet) (net.IPAddr, error) { + if available, exists := availableIPS[network]; exists { + } + + var ( + netNetwork = newNetIPNet(network) + firstIP, _ = networkRange(netNetwork) + ipNum = ipToInt(firstIP) + ownIP = ipToInt(netNetwork.IP) + size = networkSize(netNetwork.Mask) + + pos = int32(1) + max = size - 2 // -1 for the broadcast address, -1 for the gateway address + ) + + for { + var ( + newNum int32 + inUse bool + ) + + // Find first unused IP, give up after one whole round + for attempt := int32(0); attempt < max; attempt++ { + newNum = ipNum + pos + + pos = pos%max + 1 + + // The network's IP is never okay to use + if newNum == ownIP { + continue + } + + if _, inUse = alloc.inUse[newNum]; !inUse { + // We found an unused IP + break + } + } + + ip := allocatedIP{ip: intToIP(newNum)} + if inUse { + ip.err = errors.New("No unallocated IP available") + } + + select { + case quit := <-alloc.quit: + if quit { + return + } + case alloc.queueAlloc <- ip: + alloc.inUse[newNum] = struct{}{} + case released := <-alloc.queueReleased: + r := ipToInt(released) + delete(alloc.inUse, r) + + if inUse { + // If we couldn't allocate a new IP, the released one + // will be the only free one now, so instantly use it + // next time + pos = r - ipNum + } else { + // Use same IP as last time + if pos == 1 { + pos = max + } else { + pos-- + } + } + } + } + +} + +func validateIP(ip *net.IPAddr) error { + +} + +func checkRouteOverlaps(networks []netlink.Route, toCheck *net.IPNet) error { + for _, network := range networks { + if network.IPNet != nil && networkOverlaps(toCheck, network.IPNet) { + return ErrNetworkAlreadyAllocated + } + } + return nil +} + +// Detects overlap between one IPNet and another +func networkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { + if firstIP, _ := networkRange(netX); netY.Contains(firstIP) { + return true + } + if firstIP, _ := networkRange(netY); netX.Contains(firstIP) { + return true + } + return false +} + +func checkExistingNetworkOverlaps(network *net.IPNet) error { + for existing := range allocatedIPs { + if newIPNet(network) == existing { + return ErrNetworkAlreadyRegisterd + } + if networkOverlaps(network, existing) { + return ErrNetworkAlreadyAllocated + } + } + return nil +} + +// Calculates the first and last IP addresses in an IPNet +func networkRange(network *net.IPNet) (net.IP, net.IP) { + var ( + netIP = network.IP.To4() + firstIP = netIP.Mask(network.Mask) + lastIP = net.IPv4(0, 0, 0, 0).To4() + ) + + for i := 0; i < len(lastIP); i++ { + lastIP[i] = netIP[i] | ^network.Mask[i] + } + return firstIP, lastIP +} + +func newIPNet(network *net.IPNet) iPNet { + return iPNet{ + IP: string(network.IP), + Mask: string(network.Mask), + } +} + +func newNetIPNet(network iPNet) *net.IPNet { + return &net.IPNet{ + IP: []byte(network.IP), + Mask: []byte(network.Mask), + } +} + +// Converts a 4 bytes IP into a 32 bit integer +func ipToInt(ip net.IP) int32 { + return int32(binary.BigEndian.Uint32(ip.To4())) +} + +// Converts 32 bit integer into a 4 bytes IP address +func intToIP(n int32) net.IP { + b := make([]byte, 4) + binary.BigEndian.PutUint32(b, uint32(n)) + return net.IP(b) +} + +// Given a netmask, calculates the number of available hosts +func networkSize(mask net.IPMask) int32 { + m := net.IPv4Mask(0, 0, 0, 0) + for i := 0; i < net.IPv4len; i++ { + m[i] = ^mask[i] + } + + return int32(binary.BigEndian.Uint32(m)) + 1 +}