2 Commits

Author SHA1 Message Date
brent saner
c05f9c4d47 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.
2025-04-13 03:52:43 -04:00
brent saner
4ab83c9069 v0.2.3
ADDED:
* Additional diagram under table subcommand for the IPv6 CIDR segments
2025-04-07 16:03:20 -04:00
5 changed files with 317 additions and 71 deletions

8
ACKNOWLEDGEMENTS Normal file
View File

@@ -0,0 +1,8 @@
The "IPv6 Segment Reference Diagram" output as rendered in the `table`
subcommand is from:
https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing
as fetched on April 07, 2025.
It is licensed under Creative Commons "CC BY-SA 4.0";
see:
https://creativecommons.org/licenses/by-sa/4.0/
https://creativecommons.org/licenses/by-sa/4.0/legalcode.en)

View File

@@ -5,7 +5,7 @@
{{- if $opts.Plain }} {{- if $opts.Plain }}
IPV4: IPV4:
{{- else }} {{- else }}
{{- bold "IPv4:"}} {{- bold "IPv4:" }}
{{- end }} {{- end }}
{{- if $opts.Legacy }} {{- if $opts.Legacy }}
{{- if $opts.Plain }} {{- if $opts.Plain }}
@@ -47,13 +47,268 @@ IPV4:
{{- end }} {{- end }}
{{- if not $opts.NoIpv6 }} {{- if not $opts.NoIpv6 }}
{{- if $opts.Plain }}
IPV6:
{{- else }}
{{ bold "IPv6:"}}
{{- end }}
{{- if $opts.Plain }} {{- if $opts.Plain }}
IPV6:
{{- else }}
{{ bold "IPv6:"}}
{{- end }}
{{- if not $opts.NoIpv6Seg }}
{{- if $opts.Plain }}
IPv6 Segment Reference Diagram:
{{- if $opts.VertSeg }}
Example: 2001:0db8:0123:4567:89ab:cdef:1234:5678
{{- if not $opts.VertInvert }}
2 4
0 8
0 12
1 16
:
0 20
d 24
b 28
8 32
:
0 36
1 40
2 44
3 48
:
4 52
5 56
6 60
7 64
:
8 68
9 72
a 76
b 80
:
c 84
d 88
e 92
f 96
:
1 100
2 104
3 108
4 112
:
5 116
6 120
7 124
8 127 or 128
{{- else }}
4 ____ 2
8 ____ 0
12 ___ 0
16 ___ 1
___ :
20 _____ 0
24 _____ d
28 _____ b
32 _____ 8
_____ :
36 _______ 0
40 _______ 1
44 _______ 2
48 _______ 3
_______ :
52 _________ 4
56 _________ 5
60 _________ 6
64 _________ 7
_________ :
68 ___________ 8
72 ___________ 9
76 ___________ a
80 ___________ b
___________ :
84 _____________ c
88 _____________ d
92 _____________ e
96 _____________ f
_____________ :
100 ______________ 1
104 ______________ 2
108 ______________ 3
112 ______________ 4
______________ :
116 ________________ 5
120 ________________ 6
124 ________________ 7
127 ________________ (8)
128 ________________ (8)
{{- end }}
{{- else }}
2001:0db8:0123:4567:89ab:cdef:1234:5678
|||| |||| |||| |||| |||| |||| |||| ||||
|||| |||| |||| |||| |||| |||| |||| |||128
|||| |||| |||| |||| |||| |||| |||| |||127
|||| |||| |||| |||| |||| |||| |||| ||124
|||| |||| |||| |||| |||| |||| |||| |120
|||| |||| |||| |||| |||| |||| |||| 116
|||| |||| |||| |||| |||| |||| |||112
|||| |||| |||| |||| |||| |||| ||108
|||| |||| |||| |||| |||| |||| |104
|||| |||| |||| |||| |||| |||| 100
|||| |||| |||| |||| |||| |||96
|||| |||| |||| |||| |||| ||92
|||| |||| |||| |||| |||| |88
|||| |||| |||| |||| |||| 84
|||| |||| |||| |||| |||80
|||| |||| |||| |||| ||76
|||| |||| |||| |||| |72
|||| |||| |||| |||| 68
|||| |||| |||| |||64
|||| |||| |||| ||60
|||| |||| |||| |56
|||| |||| |||| 52
|||| |||| |||48
|||| |||| ||44
|||| |||| |40
|||| |||| 36
|||| |||32
|||| ||28
|||| |24
|||| 20
|||16
||12
|8
4
{{- end }}
{{- else }}
{{ bold "IPv6 Segment Reference Diagram:" }}
{{- if $opts.VertSeg }}
{{ bold "Example:"}} 2001:0db8:0123:4567:89ab:cdef:1234:5678
{{- if not $opts.VertInvert }}
2 4
0 8
0 12
1 16
:
0 20
d 24
b 28
8 32
:
0 36
1 40
2 44
3 48
:
4 52
5 56
6 60
7 64
:
8 68
9 72
a 76
b 80
:
c 84
d 88
e 92
f 96
:
1 100
2 104
3 108
4 112
:
5 116
6 120
7 124
8 127 or 128
{{- else }}
4 ━━━━ 2
8 ━━━━ 0
12 ━━━ 0
16 ━━━ 1
━━━ :
20 ━━━━━ 0
24 ━━━━━ d
28 ━━━━━ b
32 ━━━━━ 8
━━━━━ :
36 ━━━━━━━ 0
40 ━━━━━━━ 1
44 ━━━━━━━ 2
48 ━━━━━━━ 3
━━━━━━━ :
52 ━━━━━━━━━ 4
56 ━━━━━━━━━ 5
60 ━━━━━━━━━ 6
64 ━━━━━━━━━ 7
━━━━━━━━━ :
68 ━━━━━━━━━━━ 8
72 ━━━━━━━━━━━ 9
76 ━━━━━━━━━━━ a
80 ━━━━━━━━━━━ b
━━━━━━━━━━━ :
84 ━━━━━━━━━━━━━ c
88 ━━━━━━━━━━━━━ d
92 ━━━━━━━━━━━━━ e
96 ━━━━━━━━━━━━━ f
━━━━━━━━━━━━━ :
100 ━━━━━━━━━━━━━━ 1
104 ━━━━━━━━━━━━━━ 2
108 ━━━━━━━━━━━━━━ 3
112 ━━━━━━━━━━━━━━ 4
━━━━━━━━━━━━━━ :
116 ━━━━━━━━━━━━━━━━ 5
120 ━━━━━━━━━━━━━━━━ 6
124 ━━━━━━━━━━━━━━━━ 7
127 ━━━━━━━━━━━━━━━━ (8)
128 ━━━━━━━━━━━━━━━━ (8)
{{- end }}
{{- else }}
{{ bold "2001:0db8:0123:4567:89ab:cdef:1234:5678" }}
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃128
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃127
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃124
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃120
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ 116
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃112
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃108
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃104
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ 100
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃96
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃92
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃88
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ 84
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃80
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃76
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃72
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃┃ 68
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃┃64
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃┃60
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ ┃56
┃┃┃┃ ┃┃┃┃ ┃┃┃┃ 52
┃┃┃┃ ┃┃┃┃ ┃┃┃48
┃┃┃┃ ┃┃┃┃ ┃┃44
┃┃┃┃ ┃┃┃┃ ┃40
┃┃┃┃ ┃┃┃┃ 36
┃┃┃┃ ┃┃┃32
┃┃┃┃ ┃┃28
┃┃┃┃ ┃24
┃┃┃┃ 20
┃┃┃16
┃┃12
┃8
4
{{- end }}
{{- end }}
{{- end }}
{{- if $opts.Plain }}
CIDR: CIDR:
{{- else }} {{- else }}
{{ bold "CIDR:" }} {{ bold "CIDR:" }}

View File

@@ -77,18 +77,21 @@ type SplitHostArgs struct {
} }
type SplitSubnetArgs 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"` NumNets uint `short:"n" long:"num-nets" required:"true" description:"Number of networks." validate:"required"`
splitArgs splitArgs
} }
type TableArgs struct { type TableArgs struct {
Notes bool `short:"n" long:"notes" description:"Include notes about prefixes (as a separate table)."` Notes bool `short:"n" long:"notes" description:"Include notes about prefixes (as a separate table)."`
Legacy bool `short:"l" long:"legacy" description:"Include legacy/obsolete/deprecated information (as separate table(s))."` Legacy bool `short:"l" long:"legacy" description:"Include legacy/obsolete/deprecated information (as separate table(s))."`
Plain bool `short:"p" long:"plain" description:"Show plain table output instead of unicode."` Plain bool `short:"p" long:"plain" description:"Show plain table output instead of unicode."`
NoV4Mask bool `short:"M" long:"no-mask" description:"Do not include netmasks for IPv4."` NoV4Mask bool `short:"M" long:"no-mask" description:"Do not include netmasks for IPv4."`
NoIpv6 bool `short:"4" long:"ipv4" description:"Only show IPv4 table(s)."` NoIpv6 bool `short:"4" long:"ipv4" description:"Only show IPv4 table(s)."`
NoIpv4 bool `short:"6" long:"ipv6" description:"Only show IPv6 table(s)."` NoIpv4 bool `short:"6" long:"ipv6" description:"Only show IPv6 table(s)."`
NoIpv6Seg bool `short:"D" long:"no-ipv6-seg" description:"Do not show the IPv6 Segment Reference Diagram (ignored if -4/--ipv4 is specified)."`
VertSeg bool `short:"V" long:"vert-ipv6-seg" description:"If specified, display the IPv6 Segment Reference Diagram vertically-aligned instead of horizontally."`
VertInvert bool `short:"I" long:"vert-invert" description:"When printing a vertical-aligned IPv6 Segment Reference Diagram, flip so the prefix length is on the left. This takes up less width and is recommended for smaller terminals, and may be easier to read in general."`
} }
type CheckArgs struct { type CheckArgs struct {

View File

@@ -5,6 +5,7 @@ import "errors"
var ( var (
ErrBadBoundary error = errors.New("subnet does not align on bit boundary") ErrBadBoundary error = errors.New("subnet does not align on bit boundary")
ErrBadNumHosts error = errors.New("bad number of hosts; cannot split into prefix exactly") 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") ErrBadPrefix error = errors.New("prefix is invalid")
ErrBadPrefixLen error = errors.New("prefix length exceeds maximum possible for prefix's inet family") ErrBadPrefixLen error = errors.New("prefix length exceeds maximum possible for prefix's inet family")
ErrBadSplitter error = errors.New("invalid or unknown splitter when containing") ErrBadSplitter error = errors.New("invalid or unknown splitter when containing")

View File

@@ -1,10 +1,9 @@
package netsplit package netsplit
import ( import (
`net` `math`
"net/netip" "net/netip"
`github.com/projectdiscovery/mapcidr`
"go4.org/netipx" "go4.org/netipx"
) )
@@ -22,10 +21,7 @@ func (s *SubnetSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet,
var ok bool var ok bool
var pfxLen int var pfxLen int
var base netip.Prefix var base netip.Prefix
var sub netip.Prefix var vlsm *VLSMSplitter
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 { if s == nil || s.BaseSplitter == nil || s.network == nil || s.NumberSubnets == 0 {
return return
@@ -40,65 +36,48 @@ func (s *SubnetSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet,
return 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 return
} }
for _, n := range split { // We can now VLSM.
if sub, ok = netipx.FromStdIPNet(n); !ok { vlsm = &VLSMSplitter{
// We bail early on this error. // Ascenting and Explicit are pointless to set as all defined sizes are the same.
err = &SplitErr{ Ascending: false,
Wrapped: ErrBadBoundary, Explicit: false,
Nets: nets, PrefixLengths: make([]uint8, s.NumberSubnets),
Remaining: remaining, BaseSplitter: s.BaseSplitter,
LastSubnet: subPtr, }
RequestedPrefixLen: 0, for i := 0; i < int(s.NumberSubnets); i++ {
} vlsm.PrefixLengths[i] = uint8(pfxLen)
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 { if nets, remaining, err = vlsm.Split(); err != nil {
return return
} }
if len(nets) < int(s.NumberSubnets) { if s.Strict && remaining != nil && remaining.Prefixes() != nil && len(remaining.Prefixes()) > 0 {
err = &SplitErr{ err = ErrBadNumNets
Wrapped: ErrNoNetSpace,
Nets: nets,
Remaining: remaining,
}
return return
} }