github.com/sacloud/libsacloud/v2@v2.32.3/pkg/cidr/cidr.go (about) 1 // Copyright 2016-2022 The Libsacloud Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package cidr is a collection of assorted utilities for computing 16 // network and host addresses within network ranges. 17 // 18 // It expects a CIDR-type address structure where addresses are divided into 19 // some number of prefix bits representing the network and then the remaining 20 // suffix bits represent the host. 21 // 22 // For example, it can help to calculate addresses for sub-networks of a 23 // parent network, or to calculate host addresses within a particular prefix. 24 // 25 // At present this package is prioritizing simplicity of implementation and 26 // de-prioritizing speed and memory usage. Thus caution is advised before 27 // using this package in performance-critical applications or hot codepaths. 28 // Patches to improve the speed and memory usage may be accepted as long as 29 // they do not result in a significant increase in code complexity. 30 // 31 // ********************************************************************************* 32 // This was copied from https://github.com/apparentlymart/go-cidr 33 // ORIGINAL LICENSE is: 34 // 35 // Copyright (c) 2015 Martin Atkins 36 // 37 // Permission is hereby granted, free of charge, to any person obtaining a copy 38 // of this software and associated documentation files (the "Software"), to deal 39 // in the Software without restriction, including without limitation the rights 40 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 41 // copies of the Software, and to permit persons to whom the Software is 42 // furnished to do so, subject to the following conditions: 43 // 44 // The above copyright notice and this permission notice shall be included in 45 // all copies or substantial portions of the Software. 46 // 47 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 48 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 49 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 50 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 51 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 52 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 53 // THE SOFTWARE. 54 // ********************************************************************************* 55 package cidr 56 57 import ( 58 "fmt" 59 "math/big" 60 "net" 61 ) 62 63 // Subnet takes a parent CIDR range and creates a subnet within it 64 // with the given number of additional prefix bits and the given 65 // network number. 66 // 67 // For example, 10.3.0.0/16, extended by 8 bits, with a network number 68 // of 5, becomes 10.3.5.0/24 . 69 func Subnet(base *net.IPNet, newBits int, num int) (*net.IPNet, error) { 70 ip := base.IP 71 mask := base.Mask 72 73 parentLen, addrLen := mask.Size() 74 newPrefixLen := parentLen + newBits 75 76 if newPrefixLen > addrLen { 77 return nil, fmt.Errorf("insufficient address space to extend prefix of %d by %d", parentLen, newBits) 78 } 79 80 maxNetNum := uint64(1<<uint64(newBits)) - 1 81 if uint64(num) > maxNetNum { 82 return nil, fmt.Errorf("prefix extension of %d does not accommodate a subnet numbered %d", newBits, num) 83 } 84 85 return &net.IPNet{ 86 IP: insertNumIntoIP(ip, num, newPrefixLen), 87 Mask: net.CIDRMask(newPrefixLen, addrLen), 88 }, nil 89 } 90 91 // Host takes a parent CIDR range and turns it into a host IP address with 92 // the given host number. 93 // 94 // For example, 10.3.0.0/16 with a host number of 2 gives 10.3.0.2. 95 func Host(base *net.IPNet, num int) (net.IP, error) { 96 ip := base.IP 97 mask := base.Mask 98 99 parentLen, addrLen := mask.Size() 100 hostLen := addrLen - parentLen 101 102 maxHostNum := uint64(1<<uint64(hostLen)) - 1 103 104 numUint64 := uint64(num) 105 if num < 0 { 106 numUint64 = uint64(-num) - 1 107 num = int(maxHostNum - numUint64) 108 } 109 110 if numUint64 > maxHostNum { 111 return nil, fmt.Errorf("prefix of %d does not accommodate a host numbered %d", parentLen, num) 112 } 113 var bitlength int 114 if ip.To4() != nil { 115 bitlength = 32 116 } else { 117 bitlength = 128 118 } 119 return insertNumIntoIP(ip, num, bitlength), nil 120 } 121 122 // AddressRange returns the first and last addresses in the given CIDR range. 123 func AddressRange(network *net.IPNet) (net.IP, net.IP) { 124 // the first IP is easy 125 firstIP := network.IP 126 127 // the last IP is the network address OR NOT the mask address 128 prefixLen, bits := network.Mask.Size() 129 if prefixLen == bits { 130 // Easy! 131 // But make sure that our two slices are distinct, since they 132 // would be in all other cases. 133 lastIP := make([]byte, len(firstIP)) 134 copy(lastIP, firstIP) 135 return firstIP, lastIP 136 } 137 138 firstIPInt, bits := ipToInt(firstIP) 139 hostLen := uint(bits) - uint(prefixLen) 140 lastIPInt := big.NewInt(1) 141 lastIPInt.Lsh(lastIPInt, hostLen) 142 lastIPInt.Sub(lastIPInt, big.NewInt(1)) 143 lastIPInt.Or(lastIPInt, firstIPInt) 144 145 return firstIP, intToIP(lastIPInt, bits) 146 } 147 148 // AddressCount returns the number of distinct host addresses within the given 149 // CIDR range. 150 // 151 // Since the result is a uint64, this function returns meaningful information 152 // only for IPv4 ranges and IPv6 ranges with a prefix size of at least 65. 153 func AddressCount(network *net.IPNet) uint64 { 154 prefixLen, bits := network.Mask.Size() 155 return 1 << (uint64(bits) - uint64(prefixLen)) 156 } 157 158 //VerifyNoOverlap takes a list subnets and supernet (CIDRBlock) and verifies 159 //none of the subnets overlap and all subnets are in the supernet 160 //it returns an error if any of those conditions are not satisfied 161 func VerifyNoOverlap(subnets []*net.IPNet, CIDRBlock *net.IPNet) error { 162 firstLastIP := make([][]net.IP, len(subnets)) 163 for i, s := range subnets { 164 first, last := AddressRange(s) 165 firstLastIP[i] = []net.IP{first, last} 166 } 167 for i, s := range subnets { 168 if !CIDRBlock.Contains(firstLastIP[i][0]) || !CIDRBlock.Contains(firstLastIP[i][1]) { 169 return fmt.Errorf("%s does not fully contain %s", CIDRBlock.String(), s.String()) 170 } 171 for j := 0; j < len(subnets); j++ { 172 if i == j { 173 continue 174 } 175 176 first := firstLastIP[j][0] 177 last := firstLastIP[j][1] 178 if s.Contains(first) || s.Contains(last) { 179 return fmt.Errorf("%s overlaps with %s", subnets[j].String(), s.String()) 180 } 181 } 182 } 183 return nil 184 } 185 186 // PreviousSubnet returns the subnet of the desired mask in the IP space 187 // just lower than the start of IPNet provided. If the IP space rolls over 188 // then the second return value is true 189 func PreviousSubnet(network *net.IPNet, prefixLen int) (*net.IPNet, bool) { 190 startIP := checkIPv4(network.IP) 191 previousIP := make(net.IP, len(startIP)) 192 copy(previousIP, startIP) 193 cMask := net.CIDRMask(prefixLen, 8*len(previousIP)) 194 previousIP = Dec(previousIP) 195 previous := &net.IPNet{IP: previousIP.Mask(cMask), Mask: cMask} 196 if startIP.Equal(net.IPv4zero) || startIP.Equal(net.IPv6zero) { 197 return previous, true 198 } 199 return previous, false 200 } 201 202 // NextSubnet returns the next available subnet of the desired mask size 203 // starting for the maximum IP of the offset subnet 204 // If the IP exceeds the maxium IP then the second return value is true 205 func NextSubnet(network *net.IPNet, prefixLen int) (*net.IPNet, bool) { 206 _, currentLast := AddressRange(network) 207 mask := net.CIDRMask(prefixLen, 8*len(currentLast)) 208 currentSubnet := &net.IPNet{IP: currentLast.Mask(mask), Mask: mask} 209 _, last := AddressRange(currentSubnet) 210 last = Inc(last) 211 next := &net.IPNet{IP: last.Mask(mask), Mask: mask} 212 if last.Equal(net.IPv4zero) || last.Equal(net.IPv6zero) { 213 return next, true 214 } 215 return next, false 216 } 217 218 //Inc increases the IP by one this returns a new []byte for the IP 219 func Inc(IP net.IP) net.IP { 220 IP = checkIPv4(IP) 221 incIP := make([]byte, len(IP)) 222 copy(incIP, IP) 223 for j := len(incIP) - 1; j >= 0; j-- { 224 incIP[j]++ 225 if incIP[j] > 0 { 226 break 227 } 228 } 229 return incIP 230 } 231 232 //Dec decreases the IP by one this returns a new []byte for the IP 233 func Dec(IP net.IP) net.IP { 234 IP = checkIPv4(IP) 235 decIP := make([]byte, len(IP)) 236 copy(decIP, IP) 237 decIP = checkIPv4(decIP) 238 for j := len(decIP) - 1; j >= 0; j-- { 239 decIP[j]-- 240 if decIP[j] < 255 { 241 break 242 } 243 } 244 return decIP 245 } 246 247 func checkIPv4(ip net.IP) net.IP { 248 // Go for some reason allocs IPv6len for IPv4 so we have to correct it 249 if v4 := ip.To4(); v4 != nil { 250 return v4 251 } 252 return ip 253 } 254 255 func ipToInt(ip net.IP) (*big.Int, int) { 256 val := &big.Int{} 257 val.SetBytes([]byte(ip)) 258 if len(ip) == net.IPv4len { 259 return val, 32 260 } else if len(ip) == net.IPv6len { 261 return val, 128 262 } else { 263 panic(fmt.Errorf("Unsupported address length %d", len(ip))) 264 } 265 } 266 267 func intToIP(ipInt *big.Int, bits int) net.IP { 268 ipBytes := ipInt.Bytes() 269 ret := make([]byte, bits/8) 270 // Pack our IP bytes into the end of the return array, 271 // since big.Int.Bytes() removes front zero padding. 272 for i := 1; i <= len(ipBytes); i++ { 273 ret[len(ret)-i] = ipBytes[len(ipBytes)-i] 274 } 275 return net.IP(ret) 276 } 277 278 func insertNumIntoIP(ip net.IP, num int, prefixLen int) net.IP { 279 ipInt, totalBits := ipToInt(ip) 280 bigNum := big.NewInt(int64(num)) 281 bigNum.Lsh(bigNum, uint(totalBits-prefixLen)) 282 ipInt.Or(ipInt, bigNum) 283 return intToIP(ipInt, totalBits) 284 }