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 }