Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
860ad5842b | ||
![]() |
c05f9c4d47 | ||
![]() |
4ab83c9069 | ||
![]() |
a00442c204 |
8
ACKNOWLEDGEMENTS
Normal file
8
ACKNOWLEDGEMENTS
Normal 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)
|
2
TODO
2
TODO
@ -3,3 +3,5 @@
|
||||
- when checking/rendering reserved networks, currently the footnotes aren't returned.
|
||||
-- netsplit.IANARegistryFootnote
|
||||
-- encapsulated in the IANARegistry.Footnotes
|
||||
|
||||
- add new interface, Getter? different formatting?
|
@ -47,13 +47,268 @@ IPV4:
|
||||
{{- end }}
|
||||
|
||||
{{- if not $opts.NoIpv6 }}
|
||||
|
||||
{{- 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:
|
||||
{{- else }}
|
||||
{{ bold "CIDR:" }}
|
||||
|
@ -2,11 +2,13 @@ package main
|
||||
|
||||
type Args struct {
|
||||
Version verArgs `command:"version" alias:"v" description:"Show version information." validate:"omitempty"`
|
||||
SplitCIDR SplitCIDRArgs `command:"split-cidr" alias:"sc" description:"Split a network into as many equal subnets of prefix size N as possible." validate:"omitempty"`
|
||||
GetPfx GetPfxArgs `command:"get-net" alias:"gn" description:"Get a single subnet from a network at either the beginning or end. (You'll probably want to enable -r/--no-remaining.)" validate:"omitempty"`
|
||||
SplitCIDR SplitCIDRArgs `command:"split-cidr" alias:"sc" description:"Split a network into as many equal subnets of prefix size N as possible. (This may have issues/take a very long time if you are splitting a very large network into many very small subnets.)" validate:"omitempty"`
|
||||
SplitHost SplitHostArgs `command:"split-hosts" alias:"sh" description:"Split a network into N total number of hosts *per subnet* as cleanly/evenly as possible." validate:"omitempty"`
|
||||
SplitSubnets SplitSubnetArgs `command:"split-nets" alias:"sn" description:"Split a network into N number of subnets as cleanly as possible." validate:"omitempty"`
|
||||
VLSM VLSMArgs `command:"split-vlsm" alias:"sv" alias:"vlsm" description:"Use VLSM (Variable-Length Subnet Masks) to split a network into differently sized subnets." validate:"omitempty"`
|
||||
ExplicitNetwork XNetArgs `command:"net" alias:"xn" alias:"net" description:"Print information about an explicit network address." validate:"omitempty"`
|
||||
NumAddrs NAddrArgs `command:"num-addrs" alias:"na" alias:"addrs" description:"Return the number of addresses/hosts in a given network size. (This saves needing to lookup from the table subcommand.)" validate:"omitempty"`
|
||||
NumNets NNetArgs `command:"num-nets" alias:"nn" alias:"nets" description:"Return the number of subnets of a given size that can fit into a given network size. This is MUCH, MUCH FASTER than splitting (if you do not need addressing)." validate:"omitempty"`
|
||||
Parse ParseArgs `command:"parse" alias:"p" alias:"read" alias:"convert" description:"Parse/convert output from a previous subnetter run." validate:"omitempty"`
|
||||
Table TableArgs `command:"table" alias:"t" alias:"tab" alias:"tbl" description:"Show prefix summaries (by default both IPv4 and IPv6)." validate:"omitempty"`
|
||||
@ -49,6 +51,21 @@ type cacheArgs struct {
|
||||
DoResCache bool `short:"c" long:"cache-reservations" env:"SBNTR_RSVCACHE" description:"Enable caching/cache lookup for reservation data."`
|
||||
}
|
||||
|
||||
type GetPfxArgs struct {
|
||||
Position string `short:"P" long:"position" choice:"first" choice:"last" default:"first" description:"The position of the subnet within the network." validate:"required,oneof=first last"`
|
||||
Prefix uint8 `short:"s" long:"size" required:"true" description:"Prefix length/network size in bits (as CIDR number)." validate:"required"`
|
||||
splitArgs
|
||||
}
|
||||
|
||||
type NAddrArgs struct {
|
||||
Isv6 bool `short:"6" long:"v6" description:"If the prefix given is <=32, specify this flag to indicate that it is an IPv6 prefix and not IPv4. (If it is >32, it is automatically treated as an IPv6 prefix for obvious reasons.)"`
|
||||
InclNetAddr bool `short:"N" long:"incl-net" description:"If specified, include the network address in the count."`
|
||||
InclBcastAddr bool `short:"B" long:"incl-bcast" description:"If specified, include the broadcast/reserved broadcast address in the count."`
|
||||
Size struct {
|
||||
PrefixSize uint8 `positional-arg-name:"<prefix length>" required:"1" validate:"lte=128"`
|
||||
} `positional-args:"yes" required:"1" validate:"required"`
|
||||
}
|
||||
|
||||
type NNetArgs struct {
|
||||
Verbose bool `short:"v" long:"verbose" description:"Be verbose (more ideal for logging)."`
|
||||
NoV6Check bool `short:"6" long:"no-v6" description:"If specified, do not indicate if the subnetting is IPv6 only (true) or not (false; dual-stack/IPv4 supported)."`
|
||||
@ -77,7 +94,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
|
||||
}
|
||||
@ -89,6 +106,9 @@ type TableArgs struct {
|
||||
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)."`
|
||||
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 {
|
||||
|
@ -15,7 +15,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/projectdiscovery/mapcidr"
|
||||
"go4.org/netipx"
|
||||
"r00t2.io/subnetter/netsplit"
|
||||
"r00t2.io/subnetter/version"
|
||||
@ -159,8 +158,8 @@ func printMask(label string, pfx netip.Prefix, verb, indent int, indentStr strin
|
||||
fmt.Fprintf(sb, "%sBits:\t\t%d\n", pre2, pfx.Bits())
|
||||
fmt.Fprintf(sb, "%sFirst:\t\t%s\n", pre2, first.String())
|
||||
fmt.Fprintf(sb, "%sLast:\t\t%s\n", pre2, last.String())
|
||||
fmt.Fprintf(sb, "%sAddresses:\t%d\n", pre2, mapcidr.CountIPsInCIDR(true, true, netipx.PrefixIPNet(pfx.Masked())))
|
||||
fmt.Fprintf(sb, "%sHosts:\t\t%d\n", pre2, mapcidr.CountIPsInCIDR(false, false, netipx.PrefixIPNet(pfx.Masked())))
|
||||
fmt.Fprintf(sb, "%sAddresses:\t%d\n", pre2, netsplit.NumAddrsPfx(pfx.Masked(), true, true))
|
||||
fmt.Fprintf(sb, "%sHosts:\t\t%d\n", pre2, netsplit.NumAddrsPfx(pfx.Masked(), false, false))
|
||||
if verb >= 2 {
|
||||
fmt.Fprintf(sb, "%sExpanded:\t%s\n", pre2, netsplit.MaskExpand(mask, pfx.Addr().Is6()))
|
||||
fmt.Fprintf(sb, "%sHex:\t\t0x%s\n", pre2, mask.String())
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
`strings`
|
||||
|
||||
`github.com/TwiN/go-color`
|
||||
`github.com/projectdiscovery/mapcidr`
|
||||
`go4.org/netipx`
|
||||
`r00t2.io/subnetter/netsplit`
|
||||
)
|
||||
@ -423,8 +422,8 @@ func tplTablePrefixes(ipVer uint8, indent string, plain bool) (out string, err e
|
||||
return
|
||||
}
|
||||
dummyNet = netipx.PrefixIPNet(rows[idx].NetPrefix.Masked())
|
||||
rows[idx].Addresses = mapcidr.CountIPsInCIDR(true, true, dummyNet)
|
||||
rows[idx].Hosts = mapcidr.CountIPsInCIDR(false, false, dummyNet)
|
||||
rows[idx].Addresses = netsplit.NumAddrsNet(dummyNet, true, true)
|
||||
rows[idx].Hosts = netsplit.NumAddrsNet(dummyNet, false, false)
|
||||
}
|
||||
|
||||
colFields, colTitles, colSizes = sizeStructs(rows)
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
`math/big`
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
@ -34,6 +35,7 @@ func main() {
|
||||
var buf *bytes.Buffer
|
||||
var res *netsplit.StructuredResults
|
||||
var numNets uint
|
||||
var numAddrs *big.Int
|
||||
var v6Only bool
|
||||
var noStrict bool
|
||||
var strictErr error
|
||||
@ -69,7 +71,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
case "net":
|
||||
if err = validate.Struct(args.ExplicitNetwork.Network.Network); err != nil {
|
||||
if err = validate.Struct(args.ExplicitNetwork); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if origPfx, err = netip.ParsePrefix(args.ExplicitNetwork.Network.Network); err != nil {
|
||||
@ -84,6 +86,18 @@ func main() {
|
||||
log.Panicln(err)
|
||||
}
|
||||
return
|
||||
case "num-addrs":
|
||||
if err = validate.Struct(args.NumAddrs); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if args.NumAddrs.Size.PrefixSize > 32 {
|
||||
args.NumAddrs.Isv6 = true
|
||||
}
|
||||
if numAddrs, err = netsplit.NumAddrsIn(args.NumAddrs.Size.PrefixSize, args.NumAddrs.Isv6, args.NumAddrs.InclNetAddr, args.NumAddrs.InclBcastAddr); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
fmt.Println(numAddrs.String())
|
||||
return
|
||||
case "num-nets":
|
||||
if err = validate.Struct(args.NumNets); err != nil {
|
||||
log.Panicln(err)
|
||||
@ -195,6 +209,18 @@ func main() {
|
||||
These are all handily-dandily enclosed in a `common` struct type.
|
||||
*/
|
||||
switch parser.Active.Name {
|
||||
case "get-net": // Not a *true* splitter, per se; splitting is required but only for functional reasons.
|
||||
if err = validate.Struct(args.GetPfx); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
cmnArgs = args.GetPfx.common
|
||||
splitter = &netsplit.PrefixGetter{
|
||||
Pos: args.GetPfx.Position,
|
||||
PrefixLength: args.GetPfx.Prefix,
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
}
|
||||
noStrict = false
|
||||
strictErr = netsplit.ErrBadNumHosts // dummy
|
||||
case "split-hosts":
|
||||
if err = validate.Struct(args.SplitHost); err != nil {
|
||||
log.Panicln(err)
|
||||
|
9
go.mod
9
go.mod
@ -8,7 +8,6 @@ require (
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/goccy/go-yaml v1.15.23
|
||||
github.com/jessevdk/go-flags v1.6.1
|
||||
github.com/projectdiscovery/mapcidr v1.1.34
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||
golang.org/x/mod v0.24.0
|
||||
r00t2.io/goutils v1.8.1
|
||||
@ -16,18 +15,12 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/djherbis/times v1.6.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/projectdiscovery/blackrock v0.0.1 // indirect
|
||||
github.com/projectdiscovery/utils v0.4.14 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
|
32
go.sum
32
go.sum
@ -1,7 +1,5 @@
|
||||
github.com/TwiN/go-color v1.4.1 h1:mqG0P/KBgHKVqmtL5ye7K0/Gr4l6hTksPgTgMk3mUzc=
|
||||
github.com/TwiN/go-color v1.4.1/go.mod h1:WcPf/jtiW95WBIsEeY1Lc/b8aaWoiqQpu5cf8WFxu+s=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -22,58 +20,28 @@ github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ
|
||||
github.com/goccy/go-yaml v1.15.23 h1:WS0GAX1uNPDLUvLkNU2vXq6oTnsmfVFocjQ/4qA48qo=
|
||||
github.com/goccy/go-yaml v1.15.23/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
|
||||
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
|
||||
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
|
||||
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
|
||||
github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ=
|
||||
github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss=
|
||||
github.com/projectdiscovery/mapcidr v1.1.34 h1:udr83vQ7oz3kEOwlsU6NC6o08leJzSDQtls1wmXN/kM=
|
||||
github.com/projectdiscovery/mapcidr v1.1.34/go.mod h1:1+1R6OkKSAKtWDXE9RvxXtXPoajXTYX0eiEdkqlhQqQ=
|
||||
github.com/projectdiscovery/utils v0.0.85 h1:JpCVc9GJwJLNHy1MBPmAHJcE6rs7bRv72Trb3u84OHE=
|
||||
github.com/projectdiscovery/utils v0.0.85/go.mod h1:ttiPgS2LmLFd+VRBUdgfLKMMdrF98zX7z5W+K71MX40=
|
||||
github.com/projectdiscovery/utils v0.4.14 h1:BrEfO4f4P+Hu58jNfjho2aRt/Y4jxKhTVQqs2Ei4670=
|
||||
github.com/projectdiscovery/utils v0.4.14/go.mod h1:y5gnpQn802iEWqf0djTRNskJlS62P5eqe1VS1+ah0tk=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
`net/netip`
|
||||
`sync`
|
||||
|
||||
`github.com/go-playground/validator/v10`
|
||||
`github.com/go-resty/resty/v2`
|
||||
)
|
||||
|
||||
@ -39,8 +40,12 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO
|
||||
cacheLock sync.RWMutex
|
||||
// validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled(), validator.WithPrivateFieldValidation())
|
||||
validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
|
||||
)
|
||||
|
||||
var (
|
||||
cacheLock sync.RWMutex // TODO
|
||||
cacheClient *resty.Client
|
||||
// IPv4: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml#iana-ipv4-special-registry-1
|
||||
// IPv6: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
|
||||
|
@ -5,9 +5,11 @@ 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")
|
||||
ErrBigPrefix error = errors.New("prefix length exceeds remaining network space")
|
||||
ErrNoNetSpace error = errors.New("reached end of network space before splitting finished")
|
||||
ErrUnknownPos error = errors.New("unknown subnet position")
|
||||
)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
`bytes`
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
@ -298,6 +299,113 @@ func Contain(origPfx *netip.Prefix, nets []*netip.Prefix, remaining *netipx.IPSe
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
LastSubnetNet returns the last subnet of prefix length pfxLen from network pfx.
|
||||
|
||||
lastSubnet will be nil if pfx is nil or invalid.
|
||||
*/
|
||||
func LastSubnetNet(pfx *net.IPNet, pfxLen uint8) (lastSubnet *net.IPNet, err error) {
|
||||
|
||||
var nPfx netip.Prefix
|
||||
var ok bool
|
||||
|
||||
if pfx == nil {
|
||||
return
|
||||
}
|
||||
if nPfx, ok = netipx.FromStdIPNet(pfx); !ok {
|
||||
return
|
||||
}
|
||||
if !nPfx.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
if nPfx, err = LastSubnetPfx(nPfx, pfxLen); err != nil {
|
||||
return
|
||||
}
|
||||
if !nPfx.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
lastSubnet = netipx.PrefixIPNet(nPfx)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// LastSubnetPfx is exactly like LastSubnetNet but using netip.Prefix instead.
|
||||
func LastSubnetPfx(pfx netip.Prefix, pfxLen uint8) (lastSubnet netip.Prefix, err error) {
|
||||
|
||||
var ok bool
|
||||
var pfxBytes []byte
|
||||
var bitOffset uint
|
||||
var offset *big.Int
|
||||
var ipInt *big.Int
|
||||
var ipBytes []byte
|
||||
var byteLen int
|
||||
var lastNet *net.IPNet
|
||||
var maxBitLen uint8 = maxBitsv4
|
||||
|
||||
if !pfx.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
pfx = pfx.Masked()
|
||||
|
||||
if pfx.Addr().Is6() || pfxLen > maxBitsv4 {
|
||||
maxBitLen = maxBitsv6
|
||||
}
|
||||
if pfxLen > maxBitLen {
|
||||
err = ErrBadPrefixLen
|
||||
return
|
||||
}
|
||||
if pfxLen < uint8(pfx.Bits()) {
|
||||
err = ErrBigPrefix
|
||||
return
|
||||
}
|
||||
|
||||
if pfxBytes, err = pfx.Addr().MarshalBinary(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
byteLen = pfx.Addr().BitLen() / 8
|
||||
bitOffset = uint(pfxLen - uint8(pfx.Bits()))
|
||||
|
||||
offset = new(big.Int).Lsh(big.NewInt(1), bitOffset)
|
||||
offset.Sub(offset, big.NewInt(1))
|
||||
|
||||
// Cast the prefix (as represented as bytes) into a *big.Int for some number magic.
|
||||
ipInt = new(big.Int).SetBytes(pfxBytes)
|
||||
// Shift to the first "address" in the prefix/mask it.
|
||||
offset.Lsh(offset, uint(pfx.Addr().BitLen()-int(pfxLen)))
|
||||
// Now add the offset to the base network.
|
||||
ipInt.Add(ipInt, offset)
|
||||
|
||||
// If the base address starts at the "0 address" (e.g. 0.0.0.0 or :: etc....),
|
||||
// this can cause some strange behavior when casting the *big.Int to bytes.
|
||||
// So it gets left-null-padded to the appropriate length for the inet family.
|
||||
ipBytes = ipInt.Bytes()
|
||||
if len(ipBytes) < byteLen {
|
||||
ipBytes = append(
|
||||
bytes.Repeat([]byte{0x00}, byteLen-len(ipBytes)),
|
||||
ipBytes...,
|
||||
)
|
||||
}
|
||||
|
||||
// Create an explicit net.IPNet...
|
||||
lastNet = &net.IPNet{
|
||||
IP: net.IP(ipBytes),
|
||||
Mask: net.CIDRMask(int(pfxLen), pfx.Addr().BitLen()),
|
||||
}
|
||||
// And then make it a netip.Prefix.
|
||||
if lastSubnet, ok = netipx.FromStdIPNet(lastNet); !ok {
|
||||
return
|
||||
}
|
||||
if !lastSubnet.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
MaskExpand expands a net.IPMask's string format.
|
||||
Like AddrExpand but for netmasks.
|
||||
@ -391,29 +499,29 @@ func MaskInvert(mask net.IPMask) (inverted net.IPMask) {
|
||||
Note that for the single-host prefix (/32 for IPv4, /128 for IPv6), numAddrs will *always* be 1.
|
||||
For point-to-point prefix (IPv4 /31, IPv6 /127), numAddrs will *ALWAYS* be 2.
|
||||
*/
|
||||
func NumAddrsIn(prefixLen uint8, isIpv6, inclNet, inclBcast bool) (numAddrs *big.Int, err error) {
|
||||
func NumAddrsIn(pfxLen uint8, isIpv6, inclNet, inclBcast bool) (numAddrs *big.Int, err error) {
|
||||
|
||||
var numBits uint
|
||||
var numRemoved int64
|
||||
var maxBitLen uint8 = maxBitsv4
|
||||
|
||||
if isIpv6 {
|
||||
if isIpv6 || pfxLen > maxBitsv4 {
|
||||
maxBitLen = maxBitsv6
|
||||
}
|
||||
if prefixLen > maxBitLen {
|
||||
if pfxLen > maxBitLen {
|
||||
err = ErrBadPrefixLen
|
||||
return
|
||||
}
|
||||
if prefixLen == maxBitLen {
|
||||
if pfxLen == maxBitLen {
|
||||
numAddrs = big.NewInt(1)
|
||||
return
|
||||
}
|
||||
if (prefixLen + 1) == maxBitLen {
|
||||
if (pfxLen + 1) == maxBitLen {
|
||||
numAddrs = big.NewInt(2)
|
||||
return
|
||||
}
|
||||
|
||||
numBits = uint(maxBitLen - prefixLen)
|
||||
numBits = uint(maxBitLen - pfxLen)
|
||||
|
||||
numAddrs = new(big.Int).Lsh(big.NewInt(1), numBits)
|
||||
|
||||
@ -451,6 +559,9 @@ func NumAddrsNet(pfx *net.IPNet, inclNet, inclBcast bool) (numAddrs *big.Int) {
|
||||
if nPfx, ok = netipx.FromStdIPNet(pfx); !ok {
|
||||
return
|
||||
}
|
||||
if !nPfx.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
numAddrs = NumAddrsPfx(nPfx, inclNet, inclBcast)
|
||||
|
||||
@ -460,21 +571,16 @@ func NumAddrsNet(pfx *net.IPNet, inclNet, inclBcast bool) (numAddrs *big.Int) {
|
||||
// NumAddrsPfx is the exact same as NumAddrsNet but for a net/netip.Prefix instead.
|
||||
func NumAddrsPfx(pfx netip.Prefix, inclNet, inclBcast bool) (numAddrs *big.Int) {
|
||||
|
||||
var numBits uint
|
||||
var numRemoved int64
|
||||
var err error
|
||||
|
||||
numBits = uint(pfx.Addr().BitLen() - pfx.Bits())
|
||||
|
||||
numAddrs = new(big.Int).Lsh(big.NewInt(1), numBits)
|
||||
|
||||
if !inclNet {
|
||||
numRemoved++
|
||||
if !pfx.IsValid() {
|
||||
return
|
||||
}
|
||||
if !inclBcast {
|
||||
numRemoved++
|
||||
}
|
||||
if numRemoved > 0 {
|
||||
_ = numAddrs.Sub(numAddrs, big.NewInt(numRemoved))
|
||||
|
||||
// Since we're dealing with existing prefixes/networks, we should never get an error.
|
||||
if numAddrs, err = NumAddrsIn(uint8(pfx.Bits()), pfx.Addr().Is6(), inclNet, inclBcast); err != nil {
|
||||
// But *somehow* in case we do...
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -22,6 +22,10 @@ func (c *CIDRSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
return
|
||||
}
|
||||
|
||||
if err = validate.Struct(c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if base, ok = netipx.FromStdIPNet(c.network); !ok {
|
||||
err = ErrBadBoundary
|
||||
return
|
||||
@ -41,6 +45,15 @@ func (c *CIDRSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: This is *super* slow for tiny subnets in a large net.
|
||||
For future optimzation, first off benchmark to make sure it makes a difference, but
|
||||
chunk out the network (how to find appropriate length?) of n < x < y, where n is the network pfx len,
|
||||
and goroutine each split into a channel.
|
||||
Because this splits *on CIDR boundaries* and we aren't VLSM-ing, remaining never has to be considered-
|
||||
it'll always be clean splitting.
|
||||
See CIDRSplitter.LenSwitch.
|
||||
*/
|
||||
for {
|
||||
if sub, remaining, ok = remaining.RemoveFreePrefix(c.PrefixLength); !ok {
|
||||
if !sub.IsValid() {
|
||||
|
@ -28,6 +28,10 @@ func (h *HostSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
return
|
||||
}
|
||||
|
||||
if err = validate.Struct(h); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pfx, _ = netipx.FromStdIPNet(h.network)
|
||||
tgt = new(big.Int)
|
||||
tgt.SetUint64(uint64(h.NumberHosts))
|
||||
|
102
netsplit/funcs_prefixgetter.go
Normal file
102
netsplit/funcs_prefixgetter.go
Normal file
@ -0,0 +1,102 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`net/netip`
|
||||
|
||||
`go4.org/netipx`
|
||||
)
|
||||
|
||||
// Split is to conform to a NetSplitter, though a PrefixGetter is *technically* not a splitter.
|
||||
func (p *PrefixGetter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, err error) {
|
||||
|
||||
var ok bool
|
||||
var base netip.Prefix
|
||||
var vlsmS *VLSMSplitter
|
||||
var addr netip.Addr
|
||||
var maxPfxLen uint8 = maxBitsv4
|
||||
var ipsb *netipx.IPSetBuilder = new(netipx.IPSetBuilder)
|
||||
|
||||
if p == nil || p.PrefixLength == 0 || p.BaseSplitter == nil || p.network == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = validate.Struct(p); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If the position is "first", we can simply call a VLSM.
|
||||
if p.Pos == "first" {
|
||||
vlsmS = &VLSMSplitter{
|
||||
Ascending: false,
|
||||
Explicit: false,
|
||||
PrefixLengths: []uint8{p.PrefixLength},
|
||||
BaseSplitter: p.BaseSplitter,
|
||||
}
|
||||
if nets, remaining, err = vlsmS.Split(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if p.Pos != "last" {
|
||||
err = ErrUnknownPos
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise this gets... messy.
|
||||
if base, ok = netipx.FromStdIPNet(p.network); !ok {
|
||||
err = ErrBadBoundary
|
||||
return
|
||||
}
|
||||
if !base.IsValid() {
|
||||
err = ErrBadBoundary
|
||||
return
|
||||
}
|
||||
ipsb = new(netipx.IPSetBuilder)
|
||||
ipsb.AddPrefix(base.Masked())
|
||||
|
||||
// First if it's a single host prefix, ezpz gg no re.
|
||||
if base.Addr().Is6() {
|
||||
maxPfxLen = maxBitsv6
|
||||
}
|
||||
if p.PrefixLength == maxPfxLen {
|
||||
nets = make([]*netip.Prefix, 1)
|
||||
nets[0] = new(netip.Prefix)
|
||||
addr = netipx.PrefixLastIP(base)
|
||||
fmt.Println(addr.String())
|
||||
if *nets[0], err = addr.Prefix(int(p.PrefixLength)); err != nil {
|
||||
return
|
||||
}
|
||||
ipsb.Remove(addr)
|
||||
if remaining, err = ipsb.IPSet(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise this gets... interesting.
|
||||
/*
|
||||
For IPv4, performance NORMALLY would be "fine" on modern hardware with:
|
||||
1. straight CIDR-splitting
|
||||
2. grabbing the last prefix
|
||||
3. condensing the leading prefixes to a new IPSet
|
||||
But even this can take a long time (see CIDRSplitter.Split comments).
|
||||
|
||||
In almost all cases (unless subnetting like, n+12 prefix length),
|
||||
IPv6 takes WAY too long.
|
||||
|
||||
So use the same function (LastSubnetPfx) for both cases.
|
||||
*/
|
||||
nets = make([]*netip.Prefix, 1)
|
||||
nets[0] = new(netip.Prefix)
|
||||
if *nets[0], err = LastSubnetPfx(base, p.PrefixLength); err != nil {
|
||||
return
|
||||
}
|
||||
ipsb.RemovePrefix(*nets[0])
|
||||
if remaining, err = ipsb.IPSet(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
`net`
|
||||
`math`
|
||||
"net/netip"
|
||||
|
||||
`github.com/projectdiscovery/mapcidr`
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
@ -22,15 +21,16 @@ 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
|
||||
}
|
||||
|
||||
if err = validate.Struct(s); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if base, ok = netipx.FromStdIPNet(s.network); !ok {
|
||||
err = ErrBadBoundary
|
||||
return
|
||||
@ -40,65 +40,48 @@ func (s *SubnetSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet,
|
||||
return
|
||||
}
|
||||
|
||||
if split, err = mapcidr.SplitIPNetIntoN(s.network, int(s.NumberSubnets)); err != nil {
|
||||
return
|
||||
}
|
||||
// Previously, this used (github.com/projectdiscovery/mapcidr).SplitIPNetIntoN.
|
||||
// It no longer does: https://github.com/projectdiscovery/mapcidr/issues/628
|
||||
// I am Noticing.
|
||||
|
||||
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
|
||||
// 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
|
||||
}
|
||||
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)
|
||||
// Likewise.
|
||||
if base.Addr().Is6() {
|
||||
ok = pfxLen <= int(maxBitsv6)
|
||||
} 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),
|
||||
ok = pfxLen <= int(maxBitsv4)
|
||||
}
|
||||
}
|
||||
ipsb.AddPrefix(sub)
|
||||
} else {
|
||||
subPtr = new(netip.Prefix)
|
||||
*subPtr = sub
|
||||
nets = append(nets, subPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if remaining, err = ipsb.IPSet(); err != nil {
|
||||
if !ok {
|
||||
err = ErrBadPrefix
|
||||
return
|
||||
}
|
||||
|
||||
if len(nets) < int(s.NumberSubnets) {
|
||||
err = &SplitErr{
|
||||
Wrapped: ErrNoNetSpace,
|
||||
Nets: nets,
|
||||
Remaining: remaining,
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,15 @@ func (v *VLSMSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
var base netip.Prefix
|
||||
var sub netip.Prefix
|
||||
var subPtr *netip.Prefix
|
||||
var ipsb = new(netipx.IPSetBuilder)
|
||||
var ipsb *netipx.IPSetBuilder = new(netipx.IPSetBuilder)
|
||||
|
||||
if v == nil || v.PrefixLengths == nil || len(v.PrefixLengths) == 0 || v.BaseSplitter == nil || v.network == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = validate.Struct(v); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = ValidateSizes(v.network, v.PrefixLengths...); err != nil {
|
||||
return
|
||||
@ -38,10 +46,6 @@ func (v *VLSMSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
But, as I expected, netipx ftw again.
|
||||
*/
|
||||
|
||||
if v == nil || v.PrefixLengths == nil || len(v.PrefixLengths) == 0 || v.BaseSplitter == nil || v.network == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !v.Explicit {
|
||||
sort.SliceStable(
|
||||
v.PrefixLengths,
|
||||
|
@ -31,7 +31,16 @@ type NetSplitter interface {
|
||||
|
||||
// BaseSplitter is used to encapsulate the "parent" network to be split.
|
||||
type BaseSplitter struct {
|
||||
network *net.IPNet
|
||||
network *net.IPNet `validate:"required"`
|
||||
}
|
||||
|
||||
// PrefixGetter is a "pseudo-splitter"; it splits according to a given prefix but only returns a specfic subnet (either the first or the last).
|
||||
type PrefixGetter struct {
|
||||
// Pos is the position in BaseSplitter.network for the selected PrefixLength.
|
||||
Pos string `json:"pos" xml:"pos,attr" yaml:"Position" validate:"required,oneof=first last"`
|
||||
// PrefixLength specifies the CIDR/prefix length of the subnet.
|
||||
PrefixLength uint8 `json:"prefix" xml:"prefix,attr" yaml:"network Prefix Length" validate:"required,lte=128"`
|
||||
*BaseSplitter
|
||||
}
|
||||
|
||||
/*
|
||||
@ -40,7 +49,11 @@ It attemps to split the network into as many networks of size PrefixLength as cl
|
||||
*/
|
||||
type CIDRSplitter struct {
|
||||
// PrefixLength specifies the CIDR/prefix length of the subnets to split out.
|
||||
PrefixLength uint8 `json:"prefix" xml:"prefix,attr" yaml:"network Prefix Length"`
|
||||
PrefixLength uint8 `json:"prefix" xml:"prefix,attr" yaml:"network Prefix Length" validate:"required,lte=128"`
|
||||
// TODO: See CIDRSplitter.Split for future optimization using this.
|
||||
// LenSwitch specifies the threshold bit offset after which (inclusive) it switches from a repeated prefix removal to manual recursive binary split.
|
||||
// If 0, 12 is the default.
|
||||
// LenSwitch uint8 `json:"switch_at" xml:"switchAt,attr" yaml:"Switch Offset Threshold"`
|
||||
*BaseSplitter `json:"net" xml:"net,omitempty" yaml:"network,omitempty"`
|
||||
}
|
||||
|
||||
@ -50,7 +63,7 @@ It attempts to evenly distribute addresses amoungs subnets.
|
||||
*/
|
||||
type HostSplitter struct {
|
||||
// NumberHosts is the number of hosts to be placed in each subnet to split out.
|
||||
NumberHosts uint `json:"hosts" xml:"hosts,attr" yaml:"Number of Hosts Per Subnet"`
|
||||
NumberHosts uint `json:"hosts" xml:"hosts,attr" yaml:"Number of Hosts Per Subnet" validate:"required"`
|
||||
// InclNetAddr, if true, specifies that NumberHosts includes the network address.
|
||||
InclNetAddr bool `json:"net_addr" xml:"netAddr,attr,omitempty" yaml:"Network Address Included,omitempty"`
|
||||
// InclBcastAddr, if true, specifies that NumberHosts includes the broadcast address.
|
||||
@ -66,7 +79,7 @@ as cleanly as poossible.
|
||||
*/
|
||||
type SubnetSplitter struct {
|
||||
// NumberSubnets indicates the number of subnets to split the network into.
|
||||
NumberSubnets uint `json:"nets" xml:"nets,attr" yaml:"Number of Target Subnets"`
|
||||
NumberSubnets uint `json:"nets" xml:"nets,attr" yaml:"Number of Target Subnets" validate:"required"`
|
||||
// Strict, if true, will return an error from Split if the network sizes cannot split into equally-sized networks.
|
||||
Strict bool `json:"strict" xml:"strict,attr,omitempty" yaml:"Strictly Equal Subnet Sizes"`
|
||||
*BaseSplitter `json:"net" xml:"net,omitempty" yaml:"network,omitempty"`
|
||||
@ -91,7 +104,7 @@ type VLSMSplitter struct {
|
||||
*/
|
||||
Explicit bool `json:"explicit,omitempty" xml:"explicit,attr,omitempty" yaml:"Explicit Ordering,omitempty"`
|
||||
// PrefixLengths contains the prefix lengths of each subnet to split out from the network.
|
||||
PrefixLengths []uint8 `json:"prefixes" xml:"prefixes>prefix" yaml:"Prefix Lengths"`
|
||||
PrefixLengths []uint8 `json:"prefixes" xml:"prefixes>prefix" yaml:"Prefix Lengths" validate:"required,dive,lte=128"`
|
||||
*BaseSplitter `json:"net" xml:"net,omitempty" yaml:"network,omitempty"`
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user