almost done ackshually

This commit is contained in:
2025-01-31 17:18:35 -05:00
parent 6dcf5b9e2e
commit b09cb83017
21 changed files with 1646 additions and 3 deletions

64
cmd/subnetter/args.go Normal file
View File

@@ -0,0 +1,64 @@
package main
type Args struct {
SplitCIDR SplitCIDRArgs `command:"split-cidr" alias:"se" description:"Split a network into as many equal parts of a given prefix as possible." validate:"omitempty"`
SplitHost SplitHostArgs `command:"split-hosts" alias:"sh" description:"Split a network into n total number of hosts into 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:"vlsm" alias:"v" description:"Use VLSM (Variable-Length Subnet Masks) to split a network into differently sized subnets." 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"`
}
type outputOpts struct {
SuppressRemaining bool `short:"r" long:"no-remaining" description:"Don't show leftover/unallocated/remaining space.'"`
Verbose []bool `short:"v" long:"verbose" description:"Show verbose information. May be specified multiple times to increase verbosity (up to 3 levels)."`
Seperator string `short:"S" long:"seperator" default:"\n" description:"Separator between addresses; only used for -f/--format=pretty with no verbosity."`
Fmt string `short:"f" long:"format" choice:"json" choice:"pretty" choice:"yml" choice:"xml" default:"pretty" description:"Output format. DO NOT parse 'pretty' as its output is not guaranteed between versions."`
}
type common struct {
outputOpts
AllowReserved bool `short:"R" long:"allow-reserved" description:"If specified, do not warn about reserved IP addresses/networks."`
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."`
Network Net `positional-args:"yes" required:"true" description:"The network to be split/subnetted." validate:"required"`
}
type ParseArgs struct {
outputOpts
AllowReserved bool `short:"R" long:"allow-reserved" description:"If specified, do not warn about reserved IP addresses/networks."`
AllowHostNet bool `short:"H" long:"allow-host" description:"If specified, do not warn about host bits."`
InFile string `short:"i" long:"input" default:"-" description:"Input file to parse. Default is '-' (for STDIN)." required:"true" validate:"required,filepath|eq=-"`
}
type SplitCIDRArgs struct {
Prefix uint8 `short:"s" long:"size" required:"true" description:"Prefix length/network size in bits (as CIDR number)." validate:"required"`
common
}
type SplitHostArgs struct {
Hosts uint `short:"n" long:"num-hosts" required:"true" description:"Number of hosts (usable addresses) per subnet." validate:"required"`
common
}
type SplitSubnetArgs struct {
NumNets uint `short:"n" long:"num-nets" required:"true" description:"Number of networks." validate:"required"`
common
}
type TableArgs struct {
NoIpv6 bool `short:"4" long:"ipv4" description:"Show IPv4 table."`
NoIpv4 bool `short:"6" long:"ipv6" description:"Show IPv6 table."`
Verbose []bool `short:"v" long:"verbose" description:"Show verbose information. May be specified multiple times to increase verbosity (up to 3 levels)."`
Fmt string `short:"f" long:"format" choice:"csv" choice:"json" choice:"pretty" choice:"tsv" choice:"yml" choice:"xml" default:"pretty" description:"Output format."`
Net *string `short:"n" long:"network" description:"If specified, provide information explicitly about this network. Ignores -4/--ipv4 and -6/--ipv6." validate:"omitempty,cidr"`
}
type VLSMArgs struct {
Asc bool `short:"a" long:"asc" 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"`
common
}
type Net struct {
Network string `positional-arg-name:"<network>/<prefix>" description:"network address with prefix. Can be IPv4 or IPv6." validate:"required,cidr"`
}

18
cmd/subnetter/consts.go Normal file
View File

@@ -0,0 +1,18 @@
package main
import (
"github.com/go-playground/validator/v10"
"strings"
)
var (
args *Args = new(Args)
validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
)
var (
sectSepCnt int = 48
sectSep1 string = strings.Repeat("=", sectSepCnt)
sectSep2 string = strings.Repeat("-", sectSepCnt)
sectSep3 string = strings.Repeat(".", sectSepCnt)
)

438
cmd/subnetter/funcs.go Normal file
View File

@@ -0,0 +1,438 @@
package main
import (
"bytes"
"encoding/binary"
"encoding/json"
"encoding/xml"
"fmt"
"github.com/goccy/go-yaml"
"github.com/projectdiscovery/mapcidr"
"go4.org/netipx"
"io"
"net"
"net/netip"
"os"
"strings"
"subnetter/netsplit"
"time"
)
func printHostPrefix(label string, pfx *netip.Prefix, verb, indent int, indentStr string) (out string) {
var maskEvery uint
var sb *strings.Builder = new(strings.Builder)
var pre string = strings.Repeat(indentStr, indent)
var pre2 string = strings.Repeat(indentStr, indent+1)
if pfx == nil {
fmt.Fprintf(sb, "%s%s:\n%sAddress:\t(N/A)\n", pre, label, pre2)
if verb >= 2 {
fmt.Fprintf(sb, "%sExpanded:\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sCompressed:\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sHex:\t\t(N/A)\n", pre2)
}
if verb >= 3 {
fmt.Fprintf(sb, "%sDecimal:\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sBinary:\t\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sOctal:\t\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sUnicast:\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sILM:\t\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sLLM:\t\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sLLU:\t\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sLoopback:\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sMulticast:\t(N/A)\n", pre2)
fmt.Fprintf(sb, "%sPrivate:\t(N/A)\n", pre2)
}
out = sb.String()
return
}
if pfx.Addr().Is4() {
maskEvery = 1
} else {
maskEvery = 2
}
fmt.Fprintf(sb,
"%s%s:\n%sAddress:\t%s\n",
pre, label, pre2, pfx.Addr().String(),
)
if verb >= 2 {
fmt.Fprintf(sb, "%sExpanded:\t%s\n", pre2, netsplit.AddrExpand(pfx.Addr()))
fmt.Fprintf(sb, "%sCompressed:\t%s\n", pre2, netsplit.AddrCompress(pfx))
fmt.Fprintf(sb,
"%sHex:\t\t0x%s\n",
pre2, netsplit.AddrFmt(pfx.Addr(), "02x", "", "", 0, 0),
)
}
if verb >= 3 {
fmt.Fprintf(sb, "%sDecimal:\t%d\n", pre2, binary.BigEndian.Uint32(pfx.Addr().AsSlice()))
fmt.Fprintf(sb,
"%sBinary:\t\t0b%s\n",
pre2, netsplit.AddrFmt(
pfx.Addr(), "08b", ".", fmt.Sprintf("\n%s\t\t ", pre2), maskEvery, 2,
),
)
fmt.Fprintf(sb,
"%sOctal:\t\t0o%s\n",
pre2, netsplit.AddrFmt(
pfx.Addr(), "03o", ".", fmt.Sprintf("\n%s\t\t ", pre2), 1, 8,
),
)
fmt.Fprintf(sb, "%sUnicast:\t%v\n", pre2, pfx.Addr().IsGlobalUnicast())
fmt.Fprintf(sb, "%sILM:\t\t%v\n", pre2, pfx.Addr().IsInterfaceLocalMulticast())
fmt.Fprintf(sb, "%sLLM:\t\t%v\n", pre2, pfx.Addr().IsLinkLocalMulticast())
fmt.Fprintf(sb, "%sLLU:\t\t%v\n", pre2, pfx.Addr().IsLinkLocalUnicast())
fmt.Fprintf(sb, "%sLoopback:\t%v\n", pre2, pfx.Addr().IsLoopback())
fmt.Fprintf(sb, "%sMulticast:\t%v\n", pre2, pfx.Addr().IsMulticast())
fmt.Fprintf(sb, "%sPrivate:\t%v\n", pre2, pfx.Addr().IsPrivate())
}
out = sb.String()
return
}
func printMask(label string, pfx netip.Prefix, verb, indent int, indentStr string) (out string) {
var maskF string
var maskSep string
var maskEvery uint
var mask net.IPMask
var first netip.Addr
var last netip.Addr
var sb *strings.Builder = new(strings.Builder)
var pre string = strings.Repeat(indentStr, indent)
var pre2 string = strings.Repeat(indentStr, indent+1)
var pre3 string = strings.Repeat(indentStr, indent+2)
if !pfx.IsValid() {
return
}
mask = netipx.PrefixIPNet(pfx).Mask
if pfx.Addr().Is4() {
maskF = "d"
maskSep = "."
maskEvery = 1
// IPv4 *always* reserves last addr for broadcast UNLESS it's a /31 (or /32). RFC 919, RFC 1770, RFC 5735.
switch pfx.Bits() {
case 32: // Host
first = pfx.Masked().Addr()
last = pfx.Masked().Addr()
case 31: // Point-to-Point
first = pfx.Masked().Addr()
last = pfx.Masked().Addr().Next()
default: // RFC 919, RFC 5735
first = pfx.Masked().Addr().Next()
last = netipx.PrefixLastIP(pfx.Masked()).Prev()
}
} else {
maskF = "02x"
maskSep = ":"
maskEvery = 2
switch pfx.Bits() {
case 128: // Host/Loopback
first = pfx.Masked().Addr()
last = pfx.Masked().Addr()
case 127: // Point-to-Point
first = pfx.Masked().Addr()
last = pfx.Masked().Addr().Next()
case 64:
first = pfx.Masked().Addr().Next()
// IPv6 only reserves the last address (for EUI-64 reasons) for /64's.
last = netipx.PrefixLastIP(pfx.Masked()).Prev()
default:
first = pfx.Masked().Addr()
last = netipx.PrefixLastIP(pfx.Masked())
}
}
fmt.Fprintf(sb,
"%s%s:\n%sNetmask:\t%s\n",
pre, label, pre2, netsplit.MaskFmt(mask, maskF, maskSep, "", maskEvery, 0),
)
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())
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())
}
if verb >= 3 {
fmt.Fprintf(sb, "%sDecimal:\t%d\n", pre2, binary.BigEndian.Uint32(mask))
fmt.Fprintf(sb,
"%sBinary:\t\t0b%s\n",
pre2, netsplit.MaskFmt(
mask, "08b", ".", fmt.Sprintf("\n%s\t\t ", pre2), maskEvery, 2,
),
)
fmt.Fprintf(sb,
"%sOctal:\t\t0o%s\n",
pre2, netsplit.MaskFmt(
mask, "03o", ".", fmt.Sprintf("\n%s\t\t ", pre2), 1, 8,
),
)
// Inverted mask
mask = netsplit.MaskInvert(mask)
fmt.Fprintf(sb,
"%sInverted Mask (\"Cisco Wildcard\"):\n%sNetmask:\t%s\n",
pre2, pre3, netsplit.MaskFmt(mask, maskF, maskSep, "", maskEvery, 0),
)
fmt.Fprintf(sb, "%sBits:\t\t%d\n", pre3, pfx.Bits())
fmt.Fprintf(sb, "%sExpanded:\t%s\n", pre3, netsplit.MaskExpand(mask, pfx.Addr().Is6()))
fmt.Fprintf(sb, "%sHex:\t\t0x%s\n", pre3, mask.String())
fmt.Fprintf(sb, "%sDecimal:\t%d\n", pre3, binary.BigEndian.Uint32(mask))
fmt.Fprintf(sb,
"%sBinary:\t\t0b%s\n",
pre3, netsplit.MaskFmt(
mask, "08b", ".", fmt.Sprintf("\n%s\t\t ", pre3), maskEvery, 2,
),
)
fmt.Fprintf(sb,
"%sOctal:\t\t0o%s\n",
pre3, netsplit.MaskFmt(
mask, "03o", ".", fmt.Sprintf("\n%s\t\t ", pre3), 1, 8,
),
)
}
out = sb.String()
return
}
func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, remaining *netipx.IPSet, args *common, splitter netsplit.NetSplitter) (err error) {
var b []byte
var netsLen uint
var remLen uint
var buf *bytes.Buffer
var masked netip.Prefix
var remPfxs []*netip.Prefix
var invertedMask net.IPMask
var res *netsplit.StructuredResults
var verb int = -1
if orig == nil {
return
}
if args == nil {
args = &common{
outputOpts: outputOpts{
Seperator: "\n",
},
}
}
if args.outputOpts.Verbose != nil {
verb = 0
for _, i := range args.outputOpts.Verbose {
if i {
verb++
}
}
}
if nets != nil && len(nets) > 0 {
netsLen = uint(len(nets))
}
if remaining != nil {
remLen = uint(len(remaining.Prefixes()))
remPfxs = make([]*netip.Prefix, remLen)
for idx, p := range remaining.Prefixes() {
remPfxs[idx] = new(netip.Prefix)
*remPfxs[idx] = p
}
}
masked = orig.Masked()
invertedMask = netsplit.MaskInvert(origNet.Mask)
if verb < 0 {
verb = -1
}
if args.outputOpts.Fmt == "pretty" {
// "Human"-formatted
// Header
if !args.AllowHostNet && (orig.String() != origNet.String()) {
// The host bits were removed. Warn to STDERR.
fmt.Fprintf(
os.Stderr,
"!! WARNING: !!"+
"\n\tOriginal prefix '%s' had host bits set; converted to actual network boundary '%s'.\n",
orig.String(), origNet.String(),
)
}
if verb >= 1 {
fmt.Printf(
"= %s =\n%d Subnets, %d Remaining/Left Over/Unallocated.\n",
origNet.String(), netsLen, remLen,
)
fmt.Println(sectSep1)
// Host (if specified)
if orig.String() != origNet.String() {
fmt.Print(printHostPrefix("Host", orig, verb, 0, "\t"))
} else {
fmt.Print(printHostPrefix("Host", nil, verb, 0, "\t"))
}
fmt.Println(sectSep2)
// Net mask
fmt.Print(printMask("Mask", orig.Masked(), verb, 0, "\t"))
fmt.Println(sectSep2)
// network address
fmt.Print(printHostPrefix("Network", &masked, verb, 0, "\t"))
fmt.Println(sectSep1)
}
// Allocations
if verb >= 1 {
fmt.Println()
fmt.Println(sectSep1)
fmt.Println("Subnets:")
}
if netsLen == 0 {
if verb >= 1 {
fmt.Println("(Subnetting not possible.)")
}
} else {
for _, n := range nets {
fmt.Print(resFromPfx(n).pretty(verb, 1, args.outputOpts.Seperator, "\t", false))
}
}
if verb >= 1 {
fmt.Println(sectSep1)
}
// Remaining
if !args.outputOpts.SuppressRemaining {
if verb >= 1 {
fmt.Println()
fmt.Println(sectSep1)
fmt.Println("Remaining/Left Over/Unallocated:")
}
if remLen == 0 {
if verb >= 1 {
fmt.Println("(No network space left over/unallocated.)")
}
} else {
if remaining == nil {
// This will never, ever fire; it's here to make IDEs stop being dumb and complaining.
return
}
for _, n := range remaining.Prefixes() {
fmt.Print(resFromPfx(&n).pretty(verb, 1, args.outputOpts.Seperator, "\t", true))
}
}
if verb >= 1 {
fmt.Println(sectSep1)
}
}
} else {
buf = new(bytes.Buffer)
// TODO: data-formatted/structured output
if res, err = netsplit.Contain(orig, nets, remaining, splitter); err != nil {
return
}
switch strings.ToLower(args.outputOpts.Fmt) {
case "json":
if b, err = json.MarshalIndent(res, "", " "); err != nil {
return
}
buf.Write(b)
case "xml":
fmt.Fprintf(
buf,
`<?xml version="1.0" encoding="UTF-8"?>`+
"<!--\n"+
" Generated by subnetter.\n"+
" %s\n"+
"-->\n",
time.Now().String(),
)
if b, err = xml.MarshalIndent(res, "", " "); err != nil {
return
}
buf.Write(b)
case "yml":
fmt.Fprintf(
buf,
"# Generated by subnetter.\n"+
"# %s\n\n",
time.Now().String(),
)
if b, err = yaml.Marshal(res); err != nil {
return
}
buf.Write(b)
default:
return
}
if _, err = io.Copy(os.Stdout, buf); err != nil {
return
}
}
_ = b
_ = invertedMask
return
}
func printSplitErr(e *netsplit.SplitErr) {
if e == nil {
return
}
os.Stderr.WriteString("\n!! ERROR !!!\n")
os.Stderr.WriteString("\t" + e.Wrapped.Error() + "\n")
os.Stderr.WriteString("\nnetwork Iteration Details\n(when error was encountered):\n\n")
if e.Nets == nil {
os.Stderr.WriteString("Nets:\t\t\t(N/A)\n")
} else {
os.Stderr.WriteString("Nets:\n")
for _, n := range e.Nets {
fmt.Fprintf(os.Stderr, "\t%s\n", n.String())
}
}
if e.Remaining == nil {
os.Stderr.WriteString("Remaining:\t\t(N/A)\n")
} else {
os.Stderr.WriteString("Remaining:\n")
for _, n := range e.Remaining.Prefixes() {
fmt.Fprintf(os.Stderr, "\t%s\n", n.String())
}
}
if e.LastSubnet == nil {
os.Stderr.WriteString("Last Subnet:\t\t(N/A)")
} else {
fmt.Fprintf(os.Stderr, "Last Subnet:\t\t%s\n", e.LastSubnet.String())
}
fmt.Fprintf(os.Stderr, "Desired Prefix Length:\t%d\n", e.RequestedPrefixLen)
}
func resFromPfx(pfx *netip.Prefix) (res *subnetResult) {
var txPfx subnetResult
if pfx == nil {
return
}
txPfx = subnetResult(*pfx)
res = &txPfx
return
}

View File

@@ -0,0 +1,53 @@
package main
import (
"fmt"
"net/netip"
"strings"
)
func (s *subnetResult) pretty(verb, indent int, sep, indentStr string, isRemaining bool) (out string) {
var pfx netip.Prefix
var bullet string = "+"
var sb *strings.Builder = new(strings.Builder)
var pre string = strings.Repeat(indentStr, indent)
var pre2 string = strings.Repeat(indentStr, indent+1)
if s == nil {
return
}
pfx = netip.Prefix(*s)
if verb < 0 {
verb = -1
}
if isRemaining {
bullet = "-"
}
if verb <= 0 {
sb.WriteString(pfx.String() + sep)
out = sb.String()
return
} else {
sb.WriteString(pre + sectSep2 + "\n")
sb.WriteString(
printMask(
fmt.Sprintf("%s %s", bullet, pfx.String()),
pfx,
verb,
indent,
indentStr,
),
)
sb.WriteString(pre2 + sectSep3 + "\n")
sb.WriteString(printHostPrefix("Network", &pfx, verb, 2, "\t"))
sb.WriteString(pre + sectSep2 + "\n")
}
out = sb.String()
return
}

161
cmd/subnetter/main.go Normal file
View File

@@ -0,0 +1,161 @@
package main
import (
"bytes"
"errors"
"go4.org/netipx"
"io"
"log"
"net"
"net/netip"
"os"
"strings"
"subnetter/netsplit"
"github.com/jessevdk/go-flags"
"r00t2.io/sysutils/paths"
)
func main() {
var err error
var b []byte
var pfx *net.IPNet
var resPfx *netip.Prefix
var origPfx netip.Prefix
var splitter netsplit.NetSplitter
var cmnArgs common
var nets []*netip.Prefix
var remaining *netipx.IPSet
var buf *bytes.Buffer
var res *netsplit.StructuredResults
var splitErr *netsplit.SplitErr = new(netsplit.SplitErr)
var parser *flags.Parser = flags.NewParser(args, flags.Default)
if _, err = parser.Parse(); err != nil {
switch flagsErr := err.(type) {
case *flags.Error:
switch flagsErr.Type {
case flags.ErrHelp, flags.ErrCommandRequired, flags.ErrRequired: // These print their relevant messages by themselves.
return
default:
log.Panicln(err)
}
default:
log.Panicln(err)
}
}
switch parser.Active.Name {
case "table":
// TODO: print table and exit
return
case "parse":
// TODO: parse file/bytes, unmarshal, and render with new options then exit
if strings.TrimSpace(args.Parse.InFile) == "-" {
buf = new(bytes.Buffer)
if _, err = io.Copy(buf, os.Stdin); err != nil {
log.Panicln(err)
}
b = buf.Bytes()
} else {
if err = paths.RealPath(&args.Parse.InFile); err != nil {
log.Panicln(err)
}
if b, err = os.ReadFile(args.Parse.InFile); err != nil {
log.Panicln(err)
}
}
if res, err = netsplit.Parse(b); err != nil {
log.Panicln(err)
}
if resPfx, nets, remaining, splitter, err = res.Uncontain(); err != nil {
log.Panicln(err)
}
if resPfx != nil {
origPfx = *resPfx
}
pfx = netipx.PrefixIPNet(origPfx.Masked())
cmnArgs = common{
outputOpts: args.Parse.outputOpts,
AllowReserved: args.Parse.AllowReserved,
AllowHostNet: args.Parse.AllowHostNet,
}
if err = printNets(&origPfx, pfx, nets, remaining, &cmnArgs, res.GetSplitter()); err != nil {
log.Panicln(err)
}
return
default:
// Actually subnet (and print results).
/*
A netsplit.NetSplitter is needed, along with:
* prefix
* verbosity
* disable showing remaining
* formatter
These are all handily-dandily enclosed in a `common` struct type.
*/
switch parser.Active.Name {
case "split-hosts":
if err = validate.Struct(args.SplitHost); err != nil {
log.Panicln(err)
}
cmnArgs = args.SplitHost.common
splitter = &netsplit.HostSplitter{
BaseSplitter: new(netsplit.BaseSplitter),
NumberHosts: args.SplitHost.Hosts,
}
case "split-nets":
if err = validate.Struct(args.SplitSubnets); err != nil {
log.Panicln(err)
}
cmnArgs = args.SplitSubnets.common
splitter = &netsplit.SubnetSplitter{
BaseSplitter: new(netsplit.BaseSplitter),
NumberSubnets: args.SplitSubnets.NumNets,
}
case "split-cidr":
if err = validate.Struct(args.SplitCIDR); err != nil {
log.Panicln(err)
}
cmnArgs = args.SplitCIDR.common
splitter = &netsplit.CIDRSplitter{
BaseSplitter: new(netsplit.BaseSplitter),
PrefixLength: args.SplitCIDR.Prefix,
}
case "vlsm":
if err = validate.Struct(args.VLSM); err != nil {
log.Panicln(err)
}
cmnArgs = args.VLSM.common
splitter = &netsplit.VLSMSplitter{
BaseSplitter: new(netsplit.BaseSplitter),
Ascending: args.VLSM.Asc,
PrefixLengths: args.VLSM.Sizes,
}
}
if origPfx, err = netip.ParsePrefix(cmnArgs.Network.Network); err != nil {
log.Panicln(err)
}
// This can be a direct conversion. We have to make sure we mask off the host bits to avoid errors, though.
/*
if _, pfx, err = net.ParseCIDR(cmnArgs.network.network); err != nil {
log.Panicln(err)
}
*/
pfx = netipx.PrefixIPNet(origPfx.Masked())
splitter.SetParent(*pfx)
if nets, remaining, err = splitter.Split(); err != nil {
if errors.As(err, &splitErr) {
printSplitErr(splitErr)
os.Exit(1)
} else {
log.Panicln(err)
}
}
if err = printNets(&origPfx, pfx, nets, remaining, &cmnArgs, splitter); err != nil {
log.Panicln(err)
}
}
}

8
cmd/subnetter/types.go Normal file
View File

@@ -0,0 +1,8 @@
package main
import (
"net/netip"
)
// subnetResult is only used for human/"pretty" printing.
type subnetResult netip.Prefix