go_subnetter/netsplit/funcs_subnetsplitter.go
brent saner 860ad5842b
v0.2.5
ADDED:
* num-addrs subcommand, to get the number of hosts/addresses in a given
  prefix length
* get-net subcommand, to more easily get a single subnet from either the
  beginning or the end of a prefix. (MUCH FASTER than CIDR-splitting!)
2025-04-13 18:25:32 -04:00

90 lines
2.3 KiB
Go

package netsplit
import (
`math`
"net/netip"
"go4.org/netipx"
)
/*
Split splits the network defined in a SubnetSplitter alongside its configuration and performs the subnetting.
This strategy allows for splitting a network into exactly evenly sized specified number of subnets.
remaining may or may not be nil depending on if the specified number of subnets fit cleanly into the network boundaries.
An ErrNoNetSpace error will be returned if subnetting size exhaustion occurs before the specified number of subnets is reached
(but nets will be populated and remaining will contain any left over subnets).
*/
func (s *SubnetSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, err error) {
var ok bool
var pfxLen int
var base netip.Prefix
var vlsm *VLSMSplitter
if s == nil || s.BaseSplitter == nil || s.network == nil || s.NumberSubnets == 0 {
return
}
if err = validate.Struct(s); err != nil {
return
}
if base, ok = netipx.FromStdIPNet(s.network); !ok {
err = ErrBadBoundary
return
}
if !base.IsValid() {
err = ErrBadBoundary
return
}
// Previously, this used (github.com/projectdiscovery/mapcidr).SplitIPNetIntoN.
// It no longer does: https://github.com/projectdiscovery/mapcidr/issues/628
// I am Noticing.
// First the number of bits needed is calculated.
pfxLen = int(math.Ceil(math.Log2(float64(s.NumberSubnets))))
// And this is then added to the original prefix length to get the new prefix size.
pfxLen = pfxLen + base.Bits()
// I don't know how this would happen, but it'd be bad if it did.
if pfxLen < base.Bits() {
err = ErrBigPrefix
return
}
// Likewise.
if base.Addr().Is6() {
ok = pfxLen <= int(maxBitsv6)
} else {
ok = pfxLen <= int(maxBitsv4)
}
if !ok {
err = ErrBadPrefix
return
}
// We can now VLSM.
vlsm = &VLSMSplitter{
// Ascenting and Explicit are pointless to set as all defined sizes are the same.
Ascending: false,
Explicit: false,
PrefixLengths: make([]uint8, s.NumberSubnets),
BaseSplitter: s.BaseSplitter,
}
for i := 0; i < int(s.NumberSubnets); i++ {
vlsm.PrefixLengths[i] = uint8(pfxLen)
}
if nets, remaining, err = vlsm.Split(); err != nil {
return
}
if s.Strict && remaining != nil && remaining.Prefixes() != nil && len(remaining.Prefixes()) > 0 {
err = ErrBadNumNets
return
}
return
}