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  }