github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/azure/internal/iputils/iputils.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package iputils 5 6 import ( 7 "math/big" 8 "net" 9 10 "github.com/juju/errors" 11 ) 12 13 // Azure reserves the first four addresses in each subnet 14 // (or so it seems -- it's not clearly documented). 15 const reservedAddressRangeEnd = 3 16 17 // NextSubnetIP returns the next available IP address in a given subnet. 18 func NextSubnetIP(subnet *net.IPNet, ipsInUse []net.IP) (net.IP, error) { 19 ones, bits := subnet.Mask.Size() 20 subnetMaskUint32 := ipUint32(net.IP(subnet.Mask)) 21 22 inUse := big.NewInt(0) 23 for _, ip := range ipsInUse { 24 if !subnet.Contains(ip) { 25 continue 26 } 27 index := ipIndex(ip, subnetMaskUint32) 28 inUse = inUse.SetBit(inUse, index, 1) 29 } 30 31 // Now iterate through all addresses in the subnet and return the 32 // first address that is not in use. We start at the first non- 33 // reserved address, and stop short of the last address in the 34 // subnet (i.e. all non-mask bits set), which is the broadcast 35 // address for the subnet. 36 n := ipUint32(subnet.IP) 37 for i := reservedAddressRangeEnd + 1; i < (1<<uint64(bits-ones) - 1); i++ { 38 ip := uint32IP(n + uint32(i)) 39 if !ip.IsGlobalUnicast() { 40 continue 41 } 42 index := ipIndex(ip, subnetMaskUint32) 43 if inUse.Bit(index) == 0 { 44 return ip, nil 45 } 46 } 47 return nil, errors.Errorf("no addresses available in %s", subnet) 48 } 49 50 // NthSubnetIP returns the n'th IP address in a given subnet, where n is a 51 // zero-based index, zero being the first available IP address in the subnet. 52 // 53 // If n is out of range, NthSubnetIP will return nil. 54 func NthSubnetIP(subnet *net.IPNet, n int) net.IP { 55 ones, bits := subnet.Mask.Size() 56 base := ipUint32(subnet.IP) 57 var valid int 58 for i := reservedAddressRangeEnd + 1; i < (1<<uint64(bits-ones) - 1); i++ { 59 ip := uint32IP(base + uint32(i)) 60 if !ip.IsGlobalUnicast() { 61 continue 62 } 63 if n == valid { 64 return ip 65 } 66 valid++ 67 } 68 return nil 69 } 70 71 // ipIndex calculates the index of the IP in the subnet. 72 // e.g. 10.0.0.1 in 10.0.0.0/8 has index 1. 73 func ipIndex(ip net.IP, subnetMask uint32) int { 74 return int(ipUint32(ip) & ^subnetMask) 75 } 76 77 func ipUint32(ip net.IP) uint32 { 78 ip = ip.To4() 79 return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3]) 80 } 81 82 func uint32IP(n uint32) net.IP { 83 return net.IPv4(byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) 84 }