github.com/hairyhenderson/gomplate/v4@v4.0.0-pre-2.0.20240520121557-362f058f0c93/internal/cidr/cidr.go (about) 1 package cidr 2 3 import ( 4 "fmt" 5 "math/big" 6 "net/netip" 7 8 "go4.org/netipx" 9 ) 10 11 // taken from github.com/apparentelymart/go-cidr/ and modified to use the net/netip 12 // package instead of the stdlib net package - this will hopefully be merged back 13 // upstream at some point 14 15 // SubnetBig takes a parent CIDR range and creates a subnet within it with the 16 // given number of additional prefix bits and the given network number. It 17 // differs from Subnet in that it takes a *big.Int for the num, instead of an int. 18 // 19 // For example, 10.3.0.0/16, extended by 8 bits, with a network number of 5, 20 // becomes 10.3.5.0/24 . 21 func SubnetBig(base netip.Prefix, newBits int, num *big.Int) (netip.Prefix, error) { 22 parentLen := base.Bits() 23 addrLen := base.Addr().BitLen() 24 25 newPrefixLen := parentLen + newBits 26 27 if newPrefixLen > addrLen { 28 return netip.Prefix{}, fmt.Errorf("insufficient address space to extend prefix of %d by %d", parentLen, newBits) 29 } 30 31 maxNetNum := uint64(1<<uint64(newBits)) - 1 32 if num.Uint64() > maxNetNum { 33 return netip.Prefix{}, fmt.Errorf("prefix extension of %d does not accommodate a subnet numbered %d", newBits, num) 34 } 35 36 prefix := netip.PrefixFrom(insertNumIntoIP(base.Masked().Addr(), num, newPrefixLen), newPrefixLen) 37 38 return prefix, nil 39 } 40 41 // HostBig takes a parent CIDR range and turns it into a host IP address with 42 // the given host number. It differs from Host in that it takes a *big.Int for 43 // the num, instead of an int. 44 // 45 // For example, 10.3.0.0/16 with a host number of 2 gives 10.3.0.2. 46 func HostBig(base netip.Prefix, num *big.Int) (netip.Addr, error) { 47 parentLen := base.Bits() 48 addrLen := base.Addr().BitLen() 49 50 hostLen := addrLen - parentLen 51 52 maxHostNum := big.NewInt(int64(1)) 53 maxHostNum.Lsh(maxHostNum, uint(hostLen)) 54 maxHostNum.Sub(maxHostNum, big.NewInt(1)) 55 56 numUint64 := big.NewInt(int64(num.Uint64())) 57 if num.Cmp(big.NewInt(0)) == -1 { 58 numUint64.Neg(num) 59 numUint64.Sub(numUint64, big.NewInt(int64(1))) 60 num.Sub(maxHostNum, numUint64) 61 } 62 63 if numUint64.Cmp(maxHostNum) == 1 { 64 return netip.Addr{}, fmt.Errorf("prefix of %d does not accommodate a host numbered %d", parentLen, num) 65 } 66 67 return insertNumIntoIP(base.Masked().Addr(), num, addrLen), nil 68 } 69 70 func ipToInt(ip netip.Addr) (*big.Int, int) { 71 val := &big.Int{} 72 val.SetBytes(ip.AsSlice()) 73 74 return val, ip.BitLen() 75 } 76 77 func intToIP(ipInt *big.Int, bits int) netip.Addr { 78 ipBytes := ipInt.Bytes() 79 ret := make([]byte, bits/8) 80 // Pack our IP bytes into the end of the return array, 81 // since big.Int.Bytes() removes front zero padding. 82 for i := 1; i <= len(ipBytes); i++ { 83 ret[len(ret)-i] = ipBytes[len(ipBytes)-i] 84 } 85 86 addr, ok := netip.AddrFromSlice(ret) 87 if !ok { 88 panic("invalid IP address") 89 } 90 91 return addr 92 } 93 94 func insertNumIntoIP(ip netip.Addr, bigNum *big.Int, prefixLen int) netip.Addr { 95 ipInt, totalBits := ipToInt(ip) 96 bigNum.Lsh(bigNum, uint(totalBits-prefixLen)) 97 ipInt.Or(ipInt, bigNum) 98 return intToIP(ipInt, totalBits) 99 } 100 101 // PreviousSubnet returns the subnet of the desired mask in the IP space 102 // just lower than the start of Prefix provided. If the IP space rolls over 103 // then the second return value is true 104 func PreviousSubnet(network netip.Prefix, prefixLen int) (netip.Prefix, bool) { 105 previousIP := network.Masked().Addr().Prev() 106 107 previous, err := previousIP.Prefix(prefixLen) 108 if err != nil { 109 return netip.Prefix{}, false 110 } 111 if !previous.IsValid() { 112 return previous, true 113 } 114 115 return previous.Masked(), false 116 } 117 118 // NextSubnet returns the next available subnet of the desired mask size 119 // starting for the maximum IP of the offset subnet 120 // If the IP exceeds the maxium IP then the second return value is true 121 func NextSubnet(network netip.Prefix, prefixLen int) (netip.Prefix, bool) { 122 currentLast := netipx.PrefixLastIP(network) 123 124 currentSubnet, err := currentLast.Prefix(prefixLen) 125 if err != nil { 126 return netip.Prefix{}, false 127 } 128 129 last := netipx.PrefixLastIP(currentSubnet).Next() 130 next, err := last.Prefix(prefixLen) 131 if err != nil { 132 return netip.Prefix{}, false 133 } 134 if !last.IsValid() { 135 return next, true 136 } 137 return next, false 138 }