package netsplit import ( `net` "net/netip" `github.com/projectdiscovery/mapcidr` "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 sub netip.Prefix var subPtr *netip.Prefix var split []*net.IPNet var ipsb *netipx.IPSetBuilder = new(netipx.IPSetBuilder) if s == nil || s.BaseSplitter == nil || s.network == nil || s.NumberSubnets == 0 { return } if base, ok = netipx.FromStdIPNet(s.network); !ok { err = ErrBadBoundary return } if !base.IsValid() { err = ErrBadBoundary return } if split, err = mapcidr.SplitIPNetIntoN(s.network, int(s.NumberSubnets)); err != nil { return } for _, n := range split { if sub, ok = netipx.FromStdIPNet(n); !ok { // We bail early on this error. err = &SplitErr{ Wrapped: ErrBadBoundary, Nets: nets, Remaining: remaining, LastSubnet: subPtr, RequestedPrefixLen: 0, } err = ErrBadBoundary return } if sub.String() == base.String() { continue } if pfxLen == 0 { pfxLen = sub.Bits() if nets == nil { nets = make([]*netip.Prefix, 0) } subPtr = new(netip.Prefix) *subPtr = sub nets = append(nets, subPtr) } else { if sub.Bits() != pfxLen { if err == nil { // Return this err but don't return early; wait for the populate. err = &SplitErr{ Wrapped: ErrNoNetSpace, Nets: nets, Remaining: remaining, LastSubnet: subPtr, RequestedPrefixLen: uint8(pfxLen), } } ipsb.AddPrefix(sub) } else { subPtr = new(netip.Prefix) *subPtr = sub nets = append(nets, subPtr) } } } if remaining, err = ipsb.IPSet(); err != nil { return } if len(nets) < int(s.NumberSubnets) { err = &SplitErr{ Wrapped: ErrNoNetSpace, Nets: nets, Remaining: remaining, } return } return }