Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c239a4d09
|
||
|
|
701b598b1c
|
||
|
|
32297d1bba
|
||
|
|
d37aa3eb6b
|
||
|
|
0c8577f149
|
||
|
|
166fb3be23
|
@@ -50,7 +50,7 @@ A tool to assist in design of segregate/segment/split/subnet networks.
|
||||
**** If the `XDG_CACHE_HOME` environment variable is not present...
|
||||
***** On macOS, an explicit fallback of `~/Library/Caches/subnetter/` will be used. (To my knowledge/understanding, this is the standard user cache directory and cannot be changed.) This usually evaluates to `/Users/<username>/Library/Caches/subnetter/`.
|
||||
***** On all others, an explicit fallback of `~/.cache/subnetter` will be used.
|
||||
****** On most non-macOS \*NIX-like systems , this is usually `/home/<username>/.cache/subetter/`, provided normal user homes. On http://p9f.org/[Plan9^] platforms (e.g. https://9p.io/plan9/index.html[Plan 9 4th Ed.^], https://9front.org/[9front^], http://9legacy.org/[9legacy^]), the `/env/home` environment variable (`$home`) will be used, the `./lib/` subdirectory under there (which typically/should already exist) will be appended to it, and that appended with `./cache/subnetter/` (this usually evaluates to `/usr/<username>/lib/cache/subnetter/`).
|
||||
****** On most non-macOS/*NIX-like systems , this is usually `/home/<username>/.cache/subetter/`, provided normal user homes. On http://p9f.org/[Plan9^] platforms (e.g. https://9p.io/plan9/index.html[Plan 9 4th Ed.^], https://9front.org/[9front^], http://9legacy.org/[9legacy^]), the `/env/home` environment variable (`$home`) will be used, the `./lib/` subdirectory under there (which typically/should already exist) will be appended to it, and that appended with `./cache/subnetter/` (this usually evaluates to `/usr/<username>/lib/cache/subnetter/`).
|
||||
*** For Windows systems...
|
||||
**** If https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#constants[the `LOCALAPPDATA` environment variable^] is present, it will be `%LOCALAPPDATA%\Cache\subnetter\` (or `${env:LOCALAPPDATA}\Cache\subnetter\` in Powershell syntax). This usually evaluates to `C:\Users\<username>\AppData\Local\Cache\subnetter\`.
|
||||
|
||||
@@ -61,3 +61,5 @@ This program in general draws inspiration from `ipcalc` (http://jodies.de/ipcalc
|
||||
The `table` subcommand is inspired by `iptab` from https://metacpan.org/pod/Net::IP[Perl Net-IP^].
|
||||
|
||||
Additional notes for certain contexts are primarily taken from https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing[the Wikipedia article on _Classless Inter-Domain Routing_^] (as of _Jan 28, 2025_).
|
||||
|
||||
Reservations are pulled/cached directly from the IANA registries (https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml[IPv4^], https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml[IPv6^]).
|
||||
6
TODO
6
TODO
@@ -1 +1,7 @@
|
||||
- add table rendering for reserved networks?
|
||||
|
||||
- when checking/rendering reserved networks, currently the footnotes aren't returned.
|
||||
-- netsplit.IANARegistryFootnote
|
||||
-- encapsulated in the IANARegistry.Footnotes
|
||||
|
||||
- split-hosts returns the entire original subnet as an unallocated subnet if hosts exceed the number of assignable addrs.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
{{- /*gotype: subnetter/cmd/subnetter.ReservedResults*/ -}}
|
||||
{{- /*gotype: r00t2.io/subnetter/cmd/subnetter.ReservedResults*/ -}}
|
||||
{{- $opts := . -}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{- /*gotype: subnetter/cmd/subnetter.TableArgs*/ -}}
|
||||
{{- /*gotype: r00t2.io/subnetter/cmd/subnetter.TableArgs*/ -}}
|
||||
{{- $opts := . -}}
|
||||
{{- $numRows := 0 -}}
|
||||
{{- if not $opts.NoIpv4 }}
|
||||
|
||||
@@ -5,8 +5,9 @@ type Args struct {
|
||||
SplitCIDR SplitCIDRArgs `command:"split-cidr" alias:"se" description:"Split a network into as many equal subnets of prefix size N as possible." 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. (VERY easy to run out of memory for IPv6 prefixes; be sure to specify very small network!)" 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:"vlsm" alias:"sv" description:"Use VLSM (Variable-Length Subnet Masks) to split a network into differently sized subnets." validate:"omitempty"`
|
||||
ExplicitNetwork XNetArgs `command:"net" alias:"xn" description:"Print information about an explicit network address." 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"`
|
||||
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"`
|
||||
Check CheckArgs `command:"reserved" alias:"r" description:"Check if a subnet is reserved per IANA/RFC." validate:"omitempty"`
|
||||
@@ -16,7 +17,7 @@ type verArgs struct {
|
||||
DetailVersion bool `short:"V" long:"detail" description:"Print detailed version info and exit."`
|
||||
}
|
||||
|
||||
type common struct {
|
||||
type commonBase struct {
|
||||
cacheArgs
|
||||
SuppressRemaining bool `short:"r" long:"no-remaining" description:"Don't show leftover/unallocated/remaining space."`
|
||||
Plain bool `short:"p" long:"plain" description:"Show plain output instead of unicode (only used if -f/--format=pretty)."`
|
||||
@@ -26,6 +27,10 @@ type common struct {
|
||||
AllowReserved bool `short:"R" long:"allow-reserved" description:"If specified, do not warn about reserved IP addresses/networks."`
|
||||
reservedArgs
|
||||
AllowHostNet bool `short:"H" long:"allow-host" description:"If specified, do not warn about host bits. Host bits are always removed for subnetting (as otherwise there would be errors); this is only used only for output."`
|
||||
}
|
||||
|
||||
type common struct {
|
||||
commonBase
|
||||
Network Net `positional-args:"yes" required:"true" description:"The network/parent prefix to operate on." validate:"required"`
|
||||
}
|
||||
|
||||
@@ -44,8 +49,17 @@ type cacheArgs struct {
|
||||
DoResCache bool `short:"c" long:"cache-reservations" env:"SBNTR_RSVCACHE" description:"Enable caching/cache lookup for reservation data."`
|
||||
}
|
||||
|
||||
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)."`
|
||||
Sizes struct {
|
||||
SubnetSize uint8 `required:"1" validate:"required,le=128,gtefield=NetworkSize"`
|
||||
NetworkSize uint8 `required:"1" validate:"required,le=128,ltefield=SubnetSize"`
|
||||
} `positional-args:"yes" required:"2" validate:"required"`
|
||||
}
|
||||
|
||||
type ParseArgs struct {
|
||||
splitArgs
|
||||
commonBase
|
||||
InFile string `short:"i" long:"input" default:"-" description:"Input file to parse. Default is '-' (for STDIN)." required:"true" validate:"required,filepath|eq=-"`
|
||||
}
|
||||
|
||||
@@ -89,7 +103,10 @@ type XNetArgs struct {
|
||||
|
||||
type VLSMArgs struct {
|
||||
Asc bool `short:"A" long:"ascending" description:"If specified, place smaller networks (larger prefixes) at the beginning. You almost assuredly do not want to do this."`
|
||||
Sizes []uint8 `short:"s" long:"size" required:"true" description:"Prefix lengths. May be specified multiple times." validate:"required"`
|
||||
Explicit bool `short:"O" long:"explicit-order" description:"If specified, ignore -A/--ascending and do no reordering of prefix sizes whatsoever, instead using the order given. This is EXTREMELY suboptimal and can lead to drastic addressing waste."`
|
||||
// Custom type for now; see https://github.com/jessevdk/go-flags/issues/245
|
||||
// Sizes []uint8 `short:"s" long:"size" required:"true" description:"Prefix lengths. May be specified multiple times." validate:"required"`
|
||||
Sizes []vlsmSize `short:"s" long:"size" required:"true" description:"Prefix lengths. May be specified multiple times or as a comma-delimited list." validate:"required"`
|
||||
splitArgs
|
||||
}
|
||||
|
||||
|
||||
@@ -5,5 +5,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadFmt error = errors.New("unknown output format")
|
||||
errBadNet error = errors.New("bad inet/addr family/version")
|
||||
)
|
||||
|
||||
@@ -10,15 +10,15 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
`sort`
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/projectdiscovery/mapcidr"
|
||||
"go4.org/netipx"
|
||||
"subnetter/netsplit"
|
||||
`subnetter/version`
|
||||
"r00t2.io/subnetter/netsplit"
|
||||
"r00t2.io/subnetter/version"
|
||||
)
|
||||
|
||||
func printHostPrefix(label string, pfx *netip.Prefix, verb, indent int, indentStr string) (out string) {
|
||||
@@ -236,7 +236,9 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
|
||||
if args == nil {
|
||||
args = &common{
|
||||
commonBase: commonBase{
|
||||
Separator: "\n",
|
||||
},
|
||||
}
|
||||
}
|
||||
fmts = sectFmts[args.Plain]
|
||||
@@ -471,7 +473,180 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
return
|
||||
}
|
||||
|
||||
func printReserved(nets []*netip.Prefix, remaining *netipx.IPSet, args *common) (err error) {
|
||||
func printReserved(records map[netip.Prefix]*netsplit.IANAAddrNetResRecord, origNet netip.Prefix, plain bool, fmtType string) (err error) {
|
||||
|
||||
var b []byte
|
||||
var idx int
|
||||
var pfx netip.Prefix
|
||||
var rec *netsplit.IANAAddrNetResRecord
|
||||
var sortedKeys []netip.Prefix
|
||||
var sb = new(strings.Builder)
|
||||
|
||||
switch fmtType {
|
||||
case "json":
|
||||
if b, err = json.MarshalIndent(records, "", " "); err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
return
|
||||
case "xml":
|
||||
if b, err = xml.MarshalIndent(records, "", " "); err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
return
|
||||
case "yml", "yaml":
|
||||
if b, err = yaml.Marshal(records); err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
return
|
||||
}
|
||||
|
||||
if fmtType != "pretty" {
|
||||
err = ErrBadFmt
|
||||
return
|
||||
}
|
||||
|
||||
if records == nil || len(records) == 0 {
|
||||
fmt.Println("No IANA/IETF/RFC-reserved subnet(s) found.")
|
||||
return
|
||||
}
|
||||
|
||||
sortedKeys = make([]netip.Prefix, len(records))
|
||||
idx = 0
|
||||
for pfx, _ = range records {
|
||||
sortedKeys[idx] = pfx
|
||||
idx++
|
||||
}
|
||||
sort.SliceStable(
|
||||
sortedKeys,
|
||||
func(i, j int) (isBefore bool) {
|
||||
isBefore = (netipx.ComparePrefix(sortedKeys[i], sortedKeys[j])) <= 0
|
||||
return
|
||||
},
|
||||
)
|
||||
|
||||
fmt.Fprintf(sb, "= %s =\n", origNet.String())
|
||||
for _, pfx = range sortedKeys {
|
||||
rec = records[pfx]
|
||||
fmt.Fprint(sb, sectFmts[plain][0]+"\n")
|
||||
// Name
|
||||
fmt.Fprintf(sb, "Reservation Name:\t%s\n", rec.Name)
|
||||
fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Networks
|
||||
fmt.Fprint(sb, "\tCanonical Reserved Networks:")
|
||||
if rec.Networks != nil {
|
||||
fmt.Fprint(sb, "\n")
|
||||
for _, recPfx := range rec.Networks.Prefixes {
|
||||
fmt.Fprint(sb, "\t\t"+sectFmts[plain][2]+"\n")
|
||||
fmt.Fprintf(sb, "\t\t%s\n", recPfx.String())
|
||||
// TODO: Print footnotes/refs!
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(sb, "\t(N/A)\n")
|
||||
}
|
||||
fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Reference/Specification
|
||||
fmt.Fprint(sb, "\tSpecification:")
|
||||
if rec.Spec != nil {
|
||||
fmt.Fprint(sb, "\n")
|
||||
for _, line := range strings.Split(rec.Spec.Text, "\n") {
|
||||
fmt.Fprint(sb, "\t\t"+line+"\n")
|
||||
}
|
||||
if rec.Spec.References != nil {
|
||||
fmt.Fprintf(sb, "\t\t%s\n", sectFmts[plain][2])
|
||||
for rIdx, recref := range rec.Spec.References {
|
||||
if recref != nil {
|
||||
fmt.Fprintf(sb, "\t\t[%d] (%s) %s\n", rIdx, recref.Type, recref.Reference)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(sb, "\t(None)\n")
|
||||
}
|
||||
fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Allocated (always present)
|
||||
fmt.Fprintf(sb, "\tAllocated:\t%s\n", time.Time(rec.Allocation).String())
|
||||
// fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Updated
|
||||
fmt.Fprint(sb, "\tUpdated:\t")
|
||||
if rec.Updated != nil {
|
||||
fmt.Fprintf(sb, "%s\n", time.Time(*rec.Updated).String())
|
||||
} else {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
}
|
||||
// fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Termination
|
||||
fmt.Fprint(sb, "\tTerminated:\t")
|
||||
if rec.Termination != nil {
|
||||
fmt.Fprintf(sb, "%s\n", time.Time(*rec.Termination).String())
|
||||
} else {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
}
|
||||
fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Source
|
||||
fmt.Fprint(sb, "\tValid Source:\t\t\t")
|
||||
if rec.Source != nil {
|
||||
if rec.Source.Applicable != nil && !bool(*rec.Source.Applicable) {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
} else {
|
||||
fmt.Fprintf(sb, "%v\n", bool(*rec.Source.Evaluated))
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
}
|
||||
// fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Destination
|
||||
fmt.Fprint(sb, "\tValid Destination:\t\t")
|
||||
if rec.Dest != nil {
|
||||
if rec.Dest.Applicable != nil && !bool(*rec.Dest.Applicable) {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
} else {
|
||||
fmt.Fprintf(sb, "%v\n", bool(*rec.Dest.Evaluated))
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
}
|
||||
// fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Forwardable
|
||||
fmt.Fprint(sb, "\tForwardable:\t\t\t")
|
||||
if rec.Forwardable != nil {
|
||||
if rec.Forwardable.Applicable != nil && !bool(*rec.Forwardable.Applicable) {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
} else {
|
||||
fmt.Fprintf(sb, "%v\n", bool(*rec.Forwardable.Evaluated))
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
}
|
||||
// fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Globally reachable
|
||||
fmt.Fprint(sb, "\tGlobally Routable/Reachable:\t")
|
||||
if rec.GlobalReach != nil {
|
||||
if rec.GlobalReach.Applicable != nil && !bool(*rec.GlobalReach.Applicable) {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
} else {
|
||||
fmt.Fprintf(sb, "%v\n", bool(*rec.GlobalReach.Evaluated))
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
}
|
||||
// fmt.Fprint(sb, "\t"+sectFmts[plain][1]+"\n")
|
||||
// Reserved by Protocol
|
||||
fmt.Fprint(sb, "\tReserved by Protocol:\t\t")
|
||||
if rec.ProtoReserved != nil {
|
||||
if rec.ProtoReserved.Applicable != nil && !bool(*rec.ProtoReserved.Applicable) {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
} else {
|
||||
fmt.Fprintf(sb, "%v\n", bool(*rec.ProtoReserved.Evaluated))
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(sb, "(N/A)\n")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Print(sb.String())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
`github.com/TwiN/go-color`
|
||||
`github.com/projectdiscovery/mapcidr`
|
||||
`go4.org/netipx`
|
||||
`subnetter/netsplit`
|
||||
`r00t2.io/subnetter/netsplit`
|
||||
)
|
||||
|
||||
// renderHdr renders a header. Note that the first line does *not* include the indent, but subsequent ones do.
|
||||
|
||||
26
cmd/subnetter/funcs_vlsmargs.go
Normal file
26
cmd/subnetter/funcs_vlsmargs.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
AllSizes returns a properly parsed and consolidated slice
|
||||
of all specified sizes, as it takes two valid syntaxes (`-s 32 -s 32`, `-s 32,32`)
|
||||
that can be mixed together.
|
||||
*/
|
||||
func (v *VLSMArgs) AllSizes() (sizes []uint8, err error) {
|
||||
|
||||
var sizeSlice []uint8
|
||||
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
if v.Sizes == nil || len(v.Sizes) == 0 {
|
||||
return
|
||||
}
|
||||
for _, s := range v.Sizes {
|
||||
if sizeSlice, err = s.Sizes(); err != nil {
|
||||
return
|
||||
}
|
||||
sizes = append(sizes, sizeSlice...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
33
cmd/subnetter/funcs_vlsmsize.go
Normal file
33
cmd/subnetter/funcs_vlsmsize.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
`strconv`
|
||||
`strings`
|
||||
)
|
||||
|
||||
// Sizes returns a parsed/split slice of uint8s from a vlsmSize.
|
||||
func (v *vlsmSize) Sizes() (sizes []uint8, err error) {
|
||||
|
||||
var s []string
|
||||
var u uint64
|
||||
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
s = strings.Split(string(*v), ",")
|
||||
for idx, i := range s {
|
||||
s[idx] = strings.TrimSpace(i)
|
||||
}
|
||||
|
||||
sizes = make([]uint8, len(s))
|
||||
|
||||
// No validation is performed since we don't have access to the addr inet family; that's up to the parsers.
|
||||
for idx, i := range s {
|
||||
if u, err = strconv.ParseUint(i, 10, 8); err != nil {
|
||||
return
|
||||
}
|
||||
sizes[idx] = uint8(u)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
`fmt`
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"go4.org/netipx"
|
||||
"subnetter/netsplit"
|
||||
`subnetter/version`
|
||||
"r00t2.io/subnetter/netsplit"
|
||||
"r00t2.io/subnetter/version"
|
||||
|
||||
"github.com/jessevdk/go-flags"
|
||||
"r00t2.io/sysutils/paths"
|
||||
@@ -26,14 +26,18 @@ func main() {
|
||||
var pfx *net.IPNet
|
||||
var resPfx *netip.Prefix
|
||||
var origPfx netip.Prefix
|
||||
var vlsmSizes []uint8
|
||||
var splitter netsplit.NetSplitter
|
||||
var cmnArgs common
|
||||
var nets []*netip.Prefix
|
||||
var remaining *netipx.IPSet
|
||||
var buf *bytes.Buffer
|
||||
var res *netsplit.StructuredResults
|
||||
var numNets uint
|
||||
var v6Only bool
|
||||
var noStrict bool
|
||||
var strictErr error
|
||||
var reservations map[netip.Prefix]*netsplit.IANAAddrNetResRecord
|
||||
var splitErr *netsplit.SplitErr = new(netsplit.SplitErr)
|
||||
var parser *flags.Parser = flags.NewParser(args, flags.Default)
|
||||
|
||||
@@ -77,8 +81,47 @@ func main() {
|
||||
log.Panicln(err)
|
||||
}
|
||||
return
|
||||
case "num-nets":
|
||||
if numNets, v6Only, err = netsplit.NumNets(
|
||||
args.NumNets.Sizes.SubnetSize,
|
||||
args.NumNets.Sizes.NetworkSize,
|
||||
); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if !args.NumNets.Verbose {
|
||||
fmt.Printf("%d\n", numNets)
|
||||
if !args.NumNets.NoV6Check {
|
||||
fmt.Println(v6Only)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Network Size:\t\t\t%d\n", args.NumNets.Sizes.NetworkSize)
|
||||
fmt.Printf("Subnet Size:\t\t\t%d\n", args.NumNets.Sizes.SubnetSize)
|
||||
fmt.Printf("Number of Subnets:\t\t%d\n", numNets)
|
||||
if !args.NumNets.NoV6Check {
|
||||
fmt.Printf("Subnetting is IPv6-Only:\t%v\n", v6Only)
|
||||
}
|
||||
}
|
||||
return
|
||||
case "reserved":
|
||||
// TODO
|
||||
if origPfx, err = netip.ParsePrefix(args.Check.Network.Network); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
nets = make([]*netip.Prefix, 1)
|
||||
nets[0] = new(netip.Prefix)
|
||||
*nets[0] = origPfx
|
||||
if err = netsplit.SetCachePath(args.Check.CacheDir); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if err = netsplit.EnableCache(args.Check.DoResCache); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if reservations, err = netsplit.CheckReserved(nets, !args.Check.NoRevRecursive, !args.Check.NoRecursive, !args.Check.NoPrivate); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
if err = printReserved(reservations, origPfx, args.Check.Plain, args.Check.Fmt); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
return
|
||||
case "table":
|
||||
// Account for a weird redundant CLI condition.
|
||||
if args.Table.NoIpv4 && args.Table.NoIpv6 {
|
||||
@@ -116,7 +159,12 @@ func main() {
|
||||
origPfx = *resPfx
|
||||
}
|
||||
pfx = netipx.PrefixIPNet(origPfx.Masked())
|
||||
cmnArgs = args.Parse.common
|
||||
cmnArgs = common{
|
||||
commonBase: args.Parse.commonBase,
|
||||
Network: Net{
|
||||
Network: res.Original.String(),
|
||||
},
|
||||
}
|
||||
if err = printNets(&origPfx, pfx, nets, remaining, &cmnArgs, res.GetSplitter()); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
@@ -165,14 +213,18 @@ func main() {
|
||||
PrefixLength: args.SplitCIDR.Prefix,
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
}
|
||||
case "vlsm":
|
||||
case "split-vlsm":
|
||||
if err = validate.Struct(args.VLSM); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
cmnArgs = args.VLSM.common
|
||||
if vlsmSizes, err = args.VLSM.AllSizes(); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
splitter = &netsplit.VLSMSplitter{
|
||||
Ascending: args.VLSM.Asc,
|
||||
PrefixLengths: args.VLSM.Sizes,
|
||||
Explicit: args.VLSM.Explicit,
|
||||
PrefixLengths: vlsmSizes,
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
`math/big`
|
||||
`net`
|
||||
"math/big"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
`subnetter/netsplit`
|
||||
"r00t2.io/subnetter/netsplit"
|
||||
)
|
||||
|
||||
// subnetResult is only used for human/"pretty" printing.
|
||||
@@ -96,6 +96,9 @@ type tableFormatter struct {
|
||||
NoBoldTitle bool
|
||||
}
|
||||
|
||||
// vlsmSize is a custom type to let us specify multiple sizes as a repeated or consolidated argument.
|
||||
type vlsmSize string
|
||||
|
||||
type ReservedResults struct {
|
||||
Opts CheckArgs
|
||||
Reserved map[netip.Prefix]*netsplit.IANAAddrNetResRecord
|
||||
|
||||
27
go.mod
27
go.mod
@@ -1,19 +1,17 @@
|
||||
module subnetter
|
||||
module r00t2.io/subnetter
|
||||
|
||||
go 1.23.2
|
||||
|
||||
toolchain go1.23.5
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/TwiN/go-color v1.4.1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/go-playground/validator/v10 v10.24.0
|
||||
github.com/go-playground/validator/v10 v10.25.0
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/goccy/go-yaml v1.15.16
|
||||
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.22.0
|
||||
golang.org/x/mod v0.24.0
|
||||
r00t2.io/goutils v1.8.1
|
||||
r00t2.io/sysutils v1.12.0
|
||||
)
|
||||
|
||||
@@ -28,12 +26,11 @@ require (
|
||||
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.9 // indirect
|
||||
github.com/projectdiscovery/utils v0.4.14 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
r00t2.io/goutils v1.7.2 // 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
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
)
|
||||
|
||||
36
go.sum
36
go.sum
@@ -15,19 +15,23 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
|
||||
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
|
||||
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||
github.com/goccy/go-yaml v1.15.16 h1:PMTVcGI9uNPIn7KLs0H7KC1rE+51yPl5YNh4i8rGuRA=
|
||||
github.com/goccy/go-yaml v1.15.16/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
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=
|
||||
@@ -38,8 +42,10 @@ github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k
|
||||
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.4.9 h1:GzYKy5iiCWEZZPGxrtgTOnRTZYiIAiCditGufp0nhGU=
|
||||
github.com/projectdiscovery/utils v0.4.9/go.mod h1:/68d0OHGgYF4aW4X7kS1qlFlYOnZxgtFDN85iH732JI=
|
||||
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=
|
||||
@@ -48,24 +54,34 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
|
||||
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/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
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=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
r00t2.io/goutils v1.7.2 h1:dJ+pzY/U1yVi2V6eKoxe/4roM+Tb3d0umMEL9Dx4+Lw=
|
||||
r00t2.io/goutils v1.7.2/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
||||
r00t2.io/goutils v1.8.1 h1:TQcUycPKsYn0QI4uCqb56utmvu/vVSxlblBg98iXStg=
|
||||
r00t2.io/goutils v1.8.1/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
||||
r00t2.io/sysutils v1.1.1/go.mod h1:Wlfi1rrJpoKBOjWiYM9rw2FaiZqraD6VpXyiHgoDo/o=
|
||||
r00t2.io/sysutils v1.12.0 h1:Ce3qUOyLixE1ZtFT/+SVwOT5kSkzg5+l1VloGeGugrU=
|
||||
r00t2.io/sysutils v1.12.0/go.mod h1:bNTKNBk9MnUhj9coG9JBNicSi5FrtJHEM645um85pyw=
|
||||
|
||||
@@ -8,6 +8,12 @@ import (
|
||||
`github.com/go-resty/resty/v2`
|
||||
)
|
||||
|
||||
const (
|
||||
maxBitsv4 uint8 = 32
|
||||
maxBitsv6 uint8 = 128
|
||||
maxBits uint8 = maxBitsv6
|
||||
)
|
||||
|
||||
const (
|
||||
cachedirEnvName string = "SBNTR_RSVCACHE_DIR"
|
||||
// https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
`math`
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
@@ -375,6 +376,38 @@ func MaskInvert(mask net.IPMask) (inverted net.IPMask) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
NumNets returns the number of times prefix size subnet fits into prefix size network.
|
||||
|
||||
It will error if network is larger than 128 or if subnet is smaller than network.
|
||||
|
||||
This is MUCH more performant than splitting out an actual network into explicit subnets,
|
||||
and does not require an actual network.
|
||||
*/
|
||||
func NumNets(subnet, network uint8) (numNets uint, ipv6Only bool, err error) {
|
||||
|
||||
var x float64
|
||||
|
||||
// network cannot be higher than 128, as that's the maximum for IPv6.
|
||||
if network > maxBits {
|
||||
err = ErrBadPrefixLen
|
||||
return
|
||||
}
|
||||
if subnet < network {
|
||||
err = ErrBigPrefix
|
||||
return
|
||||
}
|
||||
if network > maxBitsv4 {
|
||||
ipv6Only = true
|
||||
}
|
||||
|
||||
x = float64(subnet - network)
|
||||
|
||||
numNets = uint(math.Pow(2, x))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Parse parses b for JSON/XML/YAML and tries to return a StructuredResults from it.
|
||||
func Parse(b []byte) (s *StructuredResults, err error) {
|
||||
|
||||
|
||||
@@ -195,6 +195,7 @@ func SetCachePath(cacheDirPath string) (err error) {
|
||||
}
|
||||
|
||||
if cacheDirPath != oldPath {
|
||||
cacheDir = cacheDirPath
|
||||
if err = os.MkdirAll(cacheDir, cacheDirPerms); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func (c *CIDRSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
return
|
||||
}
|
||||
|
||||
if c.PrefixLength > uint8(base.Bits()) {
|
||||
if c.PrefixLength < uint8(base.Bits()) {
|
||||
err = ErrBigPrefix
|
||||
return
|
||||
}
|
||||
@@ -48,11 +48,11 @@ func (c *CIDRSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
// We just hit the end of the prefix.
|
||||
break
|
||||
}
|
||||
}
|
||||
subPtr = new(netip.Prefix)
|
||||
*subPtr = sub
|
||||
nets = append(nets, subPtr)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package netsplit
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"sort"
|
||||
`sort`
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
@@ -42,6 +42,7 @@ func (v *VLSMSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
return
|
||||
}
|
||||
|
||||
if !v.Explicit {
|
||||
sort.SliceStable(
|
||||
v.PrefixLengths,
|
||||
func(i, j int) (isBefore bool) { // We use a reverse sorting by default so we get larger prefixes at the beginning.
|
||||
@@ -53,6 +54,7 @@ func (v *VLSMSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, e
|
||||
return
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pfxLen, _ = v.network.Mask.Size()
|
||||
pfxLen8 = uint8(pfxLen)
|
||||
|
||||
@@ -79,6 +79,13 @@ type VLSMSplitter struct {
|
||||
You almost assuredly do not want to do this.
|
||||
*/
|
||||
Ascending bool
|
||||
/*
|
||||
Explicit, if true, will ignore Ascending completely and split in the explicit order of PrefixLengths.
|
||||
|
||||
This has the potential to be *extremely* wasteful of addressing space as the resulting blocks are
|
||||
VERY unoptimized.
|
||||
*/
|
||||
Explicit bool
|
||||
// PrefixLengths contains the prefix lengths of each subnet to split out from the network.
|
||||
PrefixLengths []uint8 `json:"prefixes" xml:"prefixes>prefix" yaml:"Prefix Lengths"`
|
||||
*BaseSplitter `json:"net" xml:"net,omitempty" yaml:"network,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user