From c05f9c4d47e3ca7deeaf2b2c46b8e1b973dcf162 Mon Sep 17 00:00:00 2001 From: brent saner Date: Sun, 13 Apr 2025 03:36:47 -0400 Subject: [PATCH] v0.2.4 FIXED: * IPv6 split-nets splitter didn't work. It does not. https://github.com/projectdiscovery/mapcidr/issues/628 Noticed. As such, this library/program has *completely removed* ALL use of the mapcidr library as it cannot be expected to be accurate. --- cmd/subnetter/args.go | 2 +- netsplit/errs.go | 1 + netsplit/funcs_subnetsplitter.go | 93 +++++++++++++------------------- 3 files changed, 38 insertions(+), 58 deletions(-) diff --git a/cmd/subnetter/args.go b/cmd/subnetter/args.go index e27966b..22db66e 100644 --- a/cmd/subnetter/args.go +++ b/cmd/subnetter/args.go @@ -77,7 +77,7 @@ type SplitHostArgs struct { } type SplitSubnetArgs struct { - Strict bool `short:"t" long:"strict" description:"If specified, an error will occur if the number of possible equally-sized subnets is not exactly -n/--num-nets."` + Strict bool `short:"t" long:"strict" description:"If specified, an error will occur if the number of subnets is not exactly -n/--num-nets."` NumNets uint `short:"n" long:"num-nets" required:"true" description:"Number of networks." validate:"required"` splitArgs } diff --git a/netsplit/errs.go b/netsplit/errs.go index b0a612e..7281d84 100644 --- a/netsplit/errs.go +++ b/netsplit/errs.go @@ -5,6 +5,7 @@ import "errors" var ( ErrBadBoundary error = errors.New("subnet does not align on bit boundary") ErrBadNumHosts error = errors.New("bad number of hosts; cannot split into prefix exactly") + ErrBadNumNets error = errors.New("bad number of nets; cannot split into prefix exactly") ErrBadPrefix error = errors.New("prefix is invalid") ErrBadPrefixLen error = errors.New("prefix length exceeds maximum possible for prefix's inet family") ErrBadSplitter error = errors.New("invalid or unknown splitter when containing") diff --git a/netsplit/funcs_subnetsplitter.go b/netsplit/funcs_subnetsplitter.go index 8816e6e..47ebead 100644 --- a/netsplit/funcs_subnetsplitter.go +++ b/netsplit/funcs_subnetsplitter.go @@ -1,10 +1,9 @@ package netsplit import ( - `net` + `math` "net/netip" - `github.com/projectdiscovery/mapcidr` "go4.org/netipx" ) @@ -22,10 +21,7 @@ func (s *SubnetSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, 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) + var vlsm *VLSMSplitter if s == nil || s.BaseSplitter == nil || s.network == nil || s.NumberSubnets == 0 { return @@ -40,65 +36,48 @@ func (s *SubnetSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, return } - if split, err = mapcidr.SplitIPNetIntoN(s.network, int(s.NumberSubnets)); err != nil { + // 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 } - 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) - } - } + // 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 remaining, err = ipsb.IPSet(); err != nil { + if nets, remaining, err = vlsm.Split(); err != nil { return } - if len(nets) < int(s.NumberSubnets) { - err = &SplitErr{ - Wrapped: ErrNoNetSpace, - Nets: nets, - Remaining: remaining, - } + if s.Strict && remaining != nil && remaining.Prefixes() != nil && len(remaining.Prefixes()) > 0 { + err = ErrBadNumNets return }