package ipam import ( "fmt" "net" "github.com/docker/libnetwork/bitseq" ) const ( // The biggest configurable host subnets minNetSize = 8 minNetSizeV6 = 64 // The effective network size for v6 minNetSizeV6Eff = 96 // The size of the host subnet used internally, it's the most granular sequence addresses defaultInternalHostSize = 16 ) // Allocator provides per address space ipv4/ipv6 book keeping type Allocator struct { // The internal subnets host size internalHostSize int // Static subnet information subnetsInfo map[subnetKey]*subnetData // Allocated addresses in each address space's internal subnet addresses map[isKey]*bitmask } // NewAllocator returns an instance of libnetwork ipam func NewAllocator() *Allocator { a := &Allocator{} a.subnetsInfo = make(map[subnetKey]*subnetData) a.addresses = make(map[isKey]*bitmask) a.internalHostSize = defaultInternalHostSize return a } // Pointer to the configured subnets in each address space type subnetKey struct { addressSpace AddressSpace subnet string } // Pointer to the internal subnets in each address space type isKey subnetKey // The structs contains the configured subnet information // along with the pointers to the respective internal subnets type subnetData struct { info *SubnetInfo // Configured subnet intSubKeyes []*isKey // Pointers to child internal subnets } // The structs containing the address allocation bitmask for the internal subnet. // The bitmask is stored a run-length encoded seq.Sequence of 4 bytes blcoks. type bitmask struct { subnet *net.IPNet addressMask *bitseq.Sequence freeAddresses int } type ipVersion int const ( v4 = 4 v6 = 6 ) /******************* * IPAMConf Contract ********************/ // AddSubnet adds a subnet for the specified address space func (a *Allocator) AddSubnet(addrSpace AddressSpace, subnetInfo *SubnetInfo) error { // Sanity check if addrSpace == "" { return ErrInvalidAddressSpace } if subnetInfo == nil || subnetInfo.Subnet == nil { return ErrInvalidSubnet } if a.contains(addrSpace, subnetInfo) { return ErrOverlapSubnet } // Sanity check and size adjustment for v6 subnetToSplit, err := adjustAndCheckSubnetSize(subnetInfo.Subnet) if err != nil { return err } // Convert to smaller internal subnets (if needed) subnetList, err := getInternalSubnets(subnetToSplit, a.internalHostSize) if err != nil { return err } // Store the configured subnet information subnetKey := subnetKey{addrSpace, subnetInfo.Subnet.String()} info := &subnetData{info: subnetInfo, intSubKeyes: make([]*isKey, len(subnetList))} a.subnetsInfo[subnetKey] = info // Create and insert the internal subnet(s) addresses masks into the address database for i, sub := range subnetList { ones, bits := sub.Mask.Size() numAddresses := 1 << uint(bits-ones) // Create and store internal subnet key into parent subnet handle smallKey := &isKey{addrSpace, sub.String()} info.intSubKeyes[i] = smallKey // Add the new address masks a.addresses[*smallKey] = &bitmask{ subnet: sub, addressMask: bitseq.New(uint32(numAddresses)), freeAddresses: numAddresses, } } return nil } // Check subnets size. In case configured subnet is v6 and host size is // greater than 32 bits, adjust subnet to /96. func adjustAndCheckSubnetSize(subnet *net.IPNet) (*net.IPNet, error) { ones, bits := subnet.Mask.Size() if v6 == getAddressVersion(subnet.IP) { if ones < minNetSizeV6 { return nil, ErrInvalidSubnet } if ones < minNetSizeV6Eff { newMask := net.CIDRMask(minNetSizeV6Eff, bits) return &net.IPNet{IP: subnet.IP, Mask: newMask}, nil } } else { if ones < minNetSize { return nil, ErrInvalidSubnet } } return subnet, nil } // Checks whether the passed subnet is a superset or subset of any of the subset in the db func (a *Allocator) contains(space AddressSpace, subInfo *SubnetInfo) bool { for k, v := range a.subnetsInfo { if space == k.addressSpace { if subInfo.Subnet.Contains(v.info.Subnet.IP) || v.info.Subnet.Contains(subInfo.Subnet.IP) { return true } } } return false } // Splits the passed subnet into N internal subnets with host size equal to internalHostSize. // If the subnet's host size is equal to or smaller than internalHostSize, there won't be any // split and the return list will contain only the passed subnet. func getInternalSubnets(subnet *net.IPNet, internalHostSize int) ([]*net.IPNet, error) { var subnetList []*net.IPNet // Get network/host subnet information netBits, bits := subnet.Mask.Size() hostBits := bits - netBits extraBits := hostBits - internalHostSize if extraBits <= 0 { subnetList = make([]*net.IPNet, 1) subnetList[0] = subnet } else { // Split in smaller internal subnets numIntSubs := 1 << uint(extraBits) subnetList = make([]*net.IPNet, numIntSubs) // Construct one copy of the internal subnets's mask intNetBits := bits - internalHostSize intMask := net.CIDRMask(intNetBits, bits) // Construct the prefix portion for each internal subnet for i := 0; i < numIntSubs; i++ { intIP := make([]byte, len(subnet.IP)) copy(intIP, subnet.IP) // IPv6 is too big, just work on the extra portion addIntToIP(intIP, i< 192.168.53 func addIntToIP(array []byte, ordinal int) { for i := len(array) - 1; i >= 0; i-- { array[i] |= (byte)(ordinal & 0xff) ordinal >>= 8 } } // Convert an ordinal to the respective IP address func ipToInt(ip []byte) int { value := 0 for i := 0; i < len(ip); i++ { j := len(ip) - 1 - i value += int(ip[i]) << uint(j*8) } return value } // Given an address and subnet, returns the host portion address func getHostPortionIP(address net.IP, subnet *net.IPNet) net.IP { hostPortion := make([]byte, len(address)) for i := 0; i < len(subnet.Mask); i++ { hostPortion[i] = address[i] &^ subnet.Mask[i] } return hostPortion } func printLine(head *bitseq.Sequence) { fmt.Println() for head != nil { fmt.Printf("-") head = head.Next } }