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  }