this is cool and all but the tables don't render properly
This commit is contained in:
52
cmd/subnetter/_tpl/table.tpl
Normal file
52
cmd/subnetter/_tpl/table.tpl
Normal file
@@ -0,0 +1,52 @@
|
||||
{{- /*gotype: subnetter/cmd/subnetter.tableOpts*/ -}}
|
||||
{{- $opts := . -}}
|
||||
{{- $numRows := 0 -}}
|
||||
{{- if not $opts.NoIpv4 }}
|
||||
IPv4:
|
||||
{{- if $opts.Legacy -}}
|
||||
{{- $legacyspec := legacy4 }}
|
||||
{{- $numRows = len $legacyspec.Rows }}
|
||||
|
||||
LEGACY:
|
||||
{{ $legacyspec.Sizer.Hdr "" $opts.Plain }}
|
||||
{{- range $idx, $row := $legacyspec.Rows }}
|
||||
{{- $row.Row $legacyspec.Sizer "\t" $opts.Plain -}}
|
||||
{{- $legacyspec.Sizer.Line "\t" $opts.Plain $idx $numRows }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if not $opts.NoV4Mask }}
|
||||
{{- $masks := mask4 }}
|
||||
NETMASKS:
|
||||
{{ $masks.Sizer.Hdr "\t" $opts.Plain }}
|
||||
{{- range $idx, $row := $masks.Rows }}
|
||||
{{- $row.Row $masks.Sizer "\t" $opts.Plain }}
|
||||
{{- $masks.Sizer.Line "\t" $opts.Plain $idx $numRows }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
CIDR:
|
||||
{{- $pfxs := addrs 4 }}
|
||||
{{- $numRows = len $pfxs.Rows }}
|
||||
{{ $pfxs.Sizer.Hdr "" $opts.Plain }}
|
||||
{{- range $idx, $row := $pfxs.Rows }}
|
||||
{{- $row.Row $pfxs.Sizer "\t" $opts.Plain }}
|
||||
{{- $pfxs.Sizer.Line "\t" $opts.Plain $idx $numRows }}
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
|
||||
{{- if not $opts.NoIpv6 }}
|
||||
|
||||
IPv6:
|
||||
|
||||
CIDR:
|
||||
{{- $pfxs := addrs 6 }}
|
||||
{{- $numRows = len $pfxs.Rows }}
|
||||
{{- $pfxs.Sizer.Hdr "\t" $opts.Plain }}
|
||||
{{- range $idx, $row := $pfxs.Rows }}
|
||||
{{- $row.Row $pfxs.Sizer "\t" $opts.Plain }}
|
||||
{{- $pfxs.Sizer.Line "\t" $opts.Plain $idx $numRows }}
|
||||
{{- end }}
|
||||
|
||||
{{- end }}
|
||||
@@ -1,19 +1,21 @@
|
||||
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"`
|
||||
Version bool `short:"v" long:"version" description:"Print the version and exit."`
|
||||
DetailVersion bool `short:"V" long:"detail" description:"Print detailed version info and exit."`
|
||||
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:"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)."`
|
||||
Verbose []bool `short:"v" long:"verbose" description:"Show verbose information if -f/--format=pretty. 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."`
|
||||
Fmt string `short:"f" long:"format" choice:"json" choice:"pretty" choice:"yml" choice:"xml" default:"pretty" description:"Output format. 'pretty' is not intended to be parseable, either by subnetter or by external tooling."`
|
||||
}
|
||||
|
||||
type common struct {
|
||||
@@ -36,21 +38,21 @@ type SplitCIDRArgs struct {
|
||||
}
|
||||
|
||||
type SplitHostArgs struct {
|
||||
Hosts uint `short:"n" long:"num-hosts" required:"true" description:"Number of hosts (usable addresses) per subnet." validate:"required"`
|
||||
Strict bool `short:"t" long:"strict" description:"If specified, an error will occur if the number of hosts/assignable addresses in a subnet is not exactly -n/--num-hosts."`
|
||||
Hosts uint `short:"n" long:"num-hosts" required:"true" description:"Number of hosts (usable addresses) per subnet." validate:"required"`
|
||||
common
|
||||
}
|
||||
|
||||
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."`
|
||||
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"`
|
||||
tableOpts
|
||||
Verbose []bool `short:"v" long:"verbose" description:"Show verbose information (if -n/--network is specified). May be specified multiple times to increase verbosity (up to 2 levels)."`
|
||||
Net *string `short:"n" long:"network" description:"If specified, print detailed information explicitly about this network instead of reference. Ignores all other options except -v/--verbose." validate:"omitempty,cidr"`
|
||||
}
|
||||
|
||||
type VLSMArgs struct {
|
||||
|
||||
@@ -1,18 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
`embed`
|
||||
"strings"
|
||||
`text/template`
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
var (
|
||||
args *Args = new(Args)
|
||||
validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
|
||||
args = new(Args)
|
||||
validate = validator.New(validator.WithRequiredStructEnabled())
|
||||
)
|
||||
|
||||
const (
|
||||
/*
|
||||
fixedPad is a fixed "surrounding" pad always present (as minimum), even for values max len on columns.
|
||||
*Must* be positive even, as <fixed left pad> and <fixed right pad> == fixedPad/2.
|
||||
*/
|
||||
fixedPad int = 2
|
||||
/*
|
||||
padChars is what fills the pads in table cells.
|
||||
At the *LEAST*, a cell will be "<fixedPad/2 * padChars><str><fixedPad/2 * padChars>"
|
||||
*/
|
||||
padChars string = " "
|
||||
)
|
||||
|
||||
var (
|
||||
sectSepCnt int = 48
|
||||
sectSep1 string = strings.Repeat("=", sectSepCnt)
|
||||
sectSep2 string = strings.Repeat("-", sectSepCnt)
|
||||
sectSep3 string = strings.Repeat(".", sectSepCnt)
|
||||
//go:embed "_tpl"
|
||||
tplDir embed.FS
|
||||
tblTpl *template.Template = template.Must(template.New("").Funcs(
|
||||
template.FuncMap{
|
||||
"legacy4": tplClass4Iter,
|
||||
"addrs": tplAddrIter,
|
||||
"mask4": tplMaskIter4,
|
||||
},
|
||||
).ParseFS(tplDir, "_tpl/*.tpl"))
|
||||
)
|
||||
|
||||
var (
|
||||
// Primarily output formatting stuff in this block.
|
||||
sectSepCnt = 48
|
||||
sectSep1 = strings.Repeat("=", sectSepCnt)
|
||||
sectSep2 = strings.Repeat("-", sectSepCnt)
|
||||
sectSep3 = strings.Repeat(".", sectSepCnt)
|
||||
// tblFmts contains a lookup of map[<is plain>]*tableFormatter.
|
||||
tblFmts map[bool]*tableFormatter = map[bool]*tableFormatter{
|
||||
// Plaintext/ASCII-only
|
||||
true: &tableFormatter{
|
||||
TopLeftHdr: "*", // Or _
|
||||
TopFillHdr: "*", // ""
|
||||
TopColSepHdr: "*", // ""
|
||||
TopRightHdr: "*", // ""
|
||||
ColSepHdr: "|",
|
||||
BottomLeftHdr: "*", // Or +
|
||||
BottomFillHdr: "*", // Or -
|
||||
BottomColSepHdr: "*", // Or +
|
||||
BottomRightHdr: "*", // ""
|
||||
Left: "|",
|
||||
Fill: "-",
|
||||
LineColSep: "|",
|
||||
LineLeft: "|",
|
||||
LineRight: "|",
|
||||
ColSep: "|",
|
||||
Right: "|",
|
||||
LastLeft: "+",
|
||||
LastFill: "-",
|
||||
LastSep: "-",
|
||||
LastRight: "+",
|
||||
SuppressLineSep: true,
|
||||
NoUpperTitle: false,
|
||||
NoBoldTitle: true,
|
||||
},
|
||||
// Unicode/UTF-8
|
||||
// https://en.wikipedia.org/wiki/Box-drawing_characters
|
||||
false: &tableFormatter{
|
||||
TopLeftHdr: "┏",
|
||||
TopFillHdr: "━",
|
||||
TopColSepHdr: "┳",
|
||||
TopRightHdr: "┓",
|
||||
ColSepHdr: "┃",
|
||||
BottomLeftHdr: "┣",
|
||||
BottomFillHdr: "━",
|
||||
BottomColSepHdr: "╇",
|
||||
BottomRightHdr: "┫",
|
||||
Left: "┃",
|
||||
Fill: "─",
|
||||
LineColSep: "┼",
|
||||
LineLeft: "┠",
|
||||
LineRight: "┨",
|
||||
ColSep: "│",
|
||||
Right: "┃",
|
||||
LastLeft: "┗",
|
||||
LastFill: "━",
|
||||
LastSep: "┷",
|
||||
LastRight: "┛",
|
||||
SuppressLineSep: false,
|
||||
NoUpperTitle: true,
|
||||
NoBoldTitle: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
9
cmd/subnetter/errs.go
Normal file
9
cmd/subnetter/errs.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
`errors`
|
||||
)
|
||||
|
||||
var (
|
||||
errBadNet error = errors.New("bad inet/addr family/version")
|
||||
)
|
||||
@@ -6,24 +6,26 @@ import (
|
||||
"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"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/projectdiscovery/mapcidr"
|
||||
"go4.org/netipx"
|
||||
"subnetter/netsplit"
|
||||
`subnetter/version`
|
||||
)
|
||||
|
||||
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)
|
||||
var sb = new(strings.Builder)
|
||||
var pre = strings.Repeat(indentStr, indent)
|
||||
var pre2 = strings.Repeat(indentStr, indent+1)
|
||||
|
||||
if pfx == nil {
|
||||
fmt.Fprintf(sb, "%s%s:\n%sAddress:\t(N/A)\n", pre, label, pre2)
|
||||
@@ -102,10 +104,10 @@ func printMask(label string, pfx netip.Prefix, verb, indent int, indentStr strin
|
||||
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)
|
||||
var sb = new(strings.Builder)
|
||||
var pre = strings.Repeat(indentStr, indent)
|
||||
var pre2 = strings.Repeat(indentStr, indent+1)
|
||||
var pre3 = strings.Repeat(indentStr, indent+2)
|
||||
|
||||
if !pfx.IsValid() {
|
||||
return
|
||||
@@ -156,7 +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())
|
||||
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())))
|
||||
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())
|
||||
@@ -215,7 +218,7 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
var remPfxs []*netip.Prefix
|
||||
var invertedMask net.IPMask
|
||||
var res *netsplit.StructuredResults
|
||||
var verb int = -1
|
||||
var verb = -1
|
||||
|
||||
if orig == nil {
|
||||
return
|
||||
@@ -341,7 +344,6 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
}
|
||||
} else {
|
||||
buf = new(bytes.Buffer)
|
||||
// TODO: data-formatted/structured output
|
||||
if res, err = netsplit.Contain(orig, nets, remaining, splitter); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -355,11 +357,11 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
fmt.Fprintf(
|
||||
buf,
|
||||
`<?xml version="1.0" encoding="UTF-8"?>`+
|
||||
"<!--\n"+
|
||||
" Generated by subnetter.\n"+
|
||||
"\n<!--\n"+
|
||||
" Generated by subnetter %s\n"+
|
||||
" %s\n"+
|
||||
"-->\n",
|
||||
time.Now().String(),
|
||||
version.Ver.Short(), time.Now().String(),
|
||||
)
|
||||
if b, err = xml.MarshalIndent(res, "", " "); err != nil {
|
||||
return
|
||||
@@ -368,9 +370,9 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
case "yml":
|
||||
fmt.Fprintf(
|
||||
buf,
|
||||
"# Generated by subnetter.\n"+
|
||||
"# Generated by subnetter %s\n"+
|
||||
"# %s\n\n",
|
||||
time.Now().String(),
|
||||
version.Ver.Short(), time.Now().String(),
|
||||
)
|
||||
if b, err = yaml.Marshal(res); err != nil {
|
||||
return
|
||||
|
||||
64
cmd/subnetter/funcs_tblfmt.go
Normal file
64
cmd/subnetter/funcs_tblfmt.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`strings`
|
||||
)
|
||||
|
||||
func padStr(str string, colSize uint8) (out string) {
|
||||
|
||||
var fill int
|
||||
var lFill int
|
||||
var rFill int
|
||||
var strLen int = len(str)
|
||||
|
||||
// EZPZ. Exact match.
|
||||
if strLen+fixedPad == int(colSize) {
|
||||
out = fmt.Sprintf(
|
||||
"%s%s%s",
|
||||
strings.Repeat(padChars, fixedPad/2), str, strings.Repeat(padChars, fixedPad/2),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// This is where it gets... annoying.
|
||||
fill = int(colSize) - (strLen + fixedPad)
|
||||
if fill%2 == 0 {
|
||||
/*
|
||||
Split evenly left/right.
|
||||
This condition will be met if BOTH strLen and colSize are even,
|
||||
or if BOTH strLen and colSize are odd.
|
||||
Math!
|
||||
*/
|
||||
lFill = fill / 2
|
||||
rFill = fill / 2
|
||||
} else {
|
||||
// Either the value or the width is odd, and the other is even.
|
||||
// (Note: Goland automatically floors an int/int calculation's result.)
|
||||
// As such, asymmetrical padding is needed.
|
||||
if strLen%2 == 0 {
|
||||
/*
|
||||
String is even, width is odd.
|
||||
Favor (smaller fill) the left.
|
||||
*/
|
||||
lFill = fill / 2
|
||||
rFill = (fill + 1) / 2 // This works instead of math.Ceil because dividing by 2.
|
||||
} else {
|
||||
/*
|
||||
String is odd, width is even.
|
||||
Favor right pad.
|
||||
*/
|
||||
lFill = (fill + 1) / 2
|
||||
rFill = fill / 2
|
||||
}
|
||||
}
|
||||
|
||||
out = fmt.Sprintf(
|
||||
"%s%s%s",
|
||||
strings.Repeat(padChars, lFill+fixedPad/2),
|
||||
str,
|
||||
strings.Repeat(padChars, rFill+fixedPad/2),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
54
cmd/subnetter/funcs_tblrows.go
Normal file
54
cmd/subnetter/funcs_tblrows.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
`reflect`
|
||||
)
|
||||
|
||||
// Row prints the formatted row for a tableAddr.
|
||||
func (t *tableAddr) Row(sizer *tableAddrSizer, indent string, plain bool) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
var sizerVal reflect.Value
|
||||
|
||||
if t == nil || sizer == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
sizerVal = reflect.ValueOf(*sizer)
|
||||
|
||||
out = rowRender(val, sizerVal, indent, plain)
|
||||
return
|
||||
}
|
||||
|
||||
// Row prints the formatted row for a tableLegacy4.
|
||||
func (t *tableLegacy4) Row(sizer *tableLegacy4Sizer, indent string, plain bool) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
var sizerVal reflect.Value
|
||||
|
||||
if t == nil || sizer == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
sizerVal = reflect.ValueOf(*sizer)
|
||||
|
||||
out = rowRender(val, sizerVal, indent, plain)
|
||||
return
|
||||
}
|
||||
|
||||
// Row prints the formatted row for a tableMask4.
|
||||
func (t *tableMask4) Row(sizer *tableMask4Sizer, indent string, plain bool) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
var sizerVal reflect.Value
|
||||
|
||||
if t == nil || sizer == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
sizerVal = reflect.ValueOf(*sizer)
|
||||
|
||||
out = rowRender(val, sizerVal, indent, plain)
|
||||
|
||||
return
|
||||
}
|
||||
113
cmd/subnetter/funcs_tblsizers.go
Normal file
113
cmd/subnetter/funcs_tblsizers.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
`reflect`
|
||||
)
|
||||
|
||||
/*
|
||||
Hdr prints the header for a tableAddrSizer corresponding to a slice of tableAddr.
|
||||
|
||||
indent will be printed before the string.
|
||||
|
||||
If plain is true, only ASCII chars will be used; otherwise fancy-schmancy Unicode.
|
||||
*/
|
||||
func (t *tableAddrSizer) Hdr(indent string, plain bool) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
|
||||
out = hdrRender(val, indent, plain)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Line either prints the *last line* (the border) or a *row separator* (if allowed in the format).
|
||||
func (t *tableAddrSizer) Line(indent string, plain bool, rowIdx, numRows int) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
|
||||
out = hdrLineRender(val, indent, plain, rowIdx, numRows)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Hdr prints the header for a tableLegacy4Sizer corresponding to a slice of tableLegacy4.
|
||||
|
||||
indent will be printed before the string.
|
||||
|
||||
If plain is true, only ASCII chars will be used; otherwise fancy-schmancy Unicode.
|
||||
*/
|
||||
func (t *tableLegacy4Sizer) Hdr(indent string, plain bool) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
|
||||
out = hdrRender(val, indent, plain)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Line either prints the *last line* (the border) or a *row separator* (if allowed in the format).
|
||||
func (t *tableLegacy4Sizer) Line(indent string, plain bool, rowIdx, numRows int) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
|
||||
out = hdrLineRender(val, indent, plain, rowIdx, numRows)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Hdr prints the header for a tableMask4Sizer corresponding to a slice of tableMask4.
|
||||
|
||||
indent will be printed before the string.
|
||||
|
||||
If plain is true, only ASCII chars will be used; otherwise fancy-schmancy Unicode.
|
||||
*/
|
||||
func (t *tableMask4Sizer) Hdr(indent string, plain bool) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
|
||||
out = hdrRender(val, indent, plain)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Line either prints the *last line* (the border) or a *row separator* (if allowed in the format).
|
||||
func (t *tableMask4Sizer) Line(indent string, plain bool, rowIdx, numRows int) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
val = reflect.ValueOf(*t)
|
||||
|
||||
out = hdrLineRender(val, indent, plain, rowIdx, numRows)
|
||||
|
||||
return
|
||||
}
|
||||
447
cmd/subnetter/funcs_tpl.go
Normal file
447
cmd/subnetter/funcs_tpl.go
Normal file
@@ -0,0 +1,447 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
`encoding/binary`
|
||||
`fmt`
|
||||
`net`
|
||||
`net/netip`
|
||||
`reflect`
|
||||
`strconv`
|
||||
`strings`
|
||||
|
||||
`github.com/TwiN/go-color`
|
||||
`github.com/projectdiscovery/mapcidr`
|
||||
`go4.org/netipx`
|
||||
`subnetter/netsplit`
|
||||
)
|
||||
|
||||
/*
|
||||
tplClass4Iter should only be called if legacy info is enabled.
|
||||
It returns a tableLegacy4Sizer and a slice of tableLegacy4.
|
||||
|
||||
It takes no input.
|
||||
*/
|
||||
func tplClass4Iter() (legacySpec *tableLegacy4Ret, err error) {
|
||||
|
||||
// This whole thing feels dirty.
|
||||
// It's like adding a microcontroller to a rock.
|
||||
// But it works.
|
||||
var pfx *net.IPNet
|
||||
var classNets []*netip.Prefix
|
||||
var netRange netipx.IPRange
|
||||
var v *netsplit.VLSMSplitter = &netsplit.VLSMSplitter{
|
||||
Ascending: false,
|
||||
PrefixLengths: []uint8{
|
||||
1, // A
|
||||
2, // B
|
||||
3, // C
|
||||
4, // D
|
||||
4, // E
|
||||
},
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
}
|
||||
|
||||
if _, pfx, err = net.ParseCIDR("0.0.0.0/0"); err != nil {
|
||||
return
|
||||
}
|
||||
v.SetParent(*pfx)
|
||||
if classNets, _, err = v.Split(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
legacySpec = &tableLegacy4Ret{
|
||||
Sizer: &tableLegacy4Sizer{
|
||||
Class: 5, // "CLASS"
|
||||
CIDR: 4, // "BITS"
|
||||
Start: 5, // "START"
|
||||
End: 3, // "END"
|
||||
},
|
||||
Rows: make([]tableLegacy4, 5),
|
||||
}
|
||||
for idx, cls := range []string{
|
||||
"A", "B", "C", "D", "E",
|
||||
} {
|
||||
legacySpec.Rows[idx] = tableLegacy4{
|
||||
Class: cls,
|
||||
CIDR: classNets[idx].String(),
|
||||
NetCIDR: *classNets[idx],
|
||||
}
|
||||
netRange = netipx.RangeOfPrefix(legacySpec.Rows[idx].NetCIDR)
|
||||
legacySpec.Rows[idx].NetStart = netRange.From()
|
||||
legacySpec.Rows[idx].NetEnd = netRange.To()
|
||||
legacySpec.Rows[idx].Start = legacySpec.Rows[idx].NetStart.String()
|
||||
legacySpec.Rows[idx].End = legacySpec.Rows[idx].NetEnd.String()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
tplAddrIter takes a 4 or 6 for inet family/version and returns a tableAddrSizer and
|
||||
slice of tableAddr.
|
||||
tableAddr is sorted from smallest prefix/largest network to largest prefix/smallest network.
|
||||
*/
|
||||
func tplAddrIter(ipVer uint8) (addrs *tableAddrRet, err error) {
|
||||
|
||||
var dummyAddr netip.Addr
|
||||
var dummyNet *net.IPNet
|
||||
var l int
|
||||
|
||||
addrs = &tableAddrRet{
|
||||
Sizer: &tableAddrSizer{
|
||||
Prefix: 6, // "PREFIX"
|
||||
Bits: 4, // "BITS"
|
||||
Addresses: 9, // "ADDRESSES"
|
||||
Hosts: 5, // "HOSTS"
|
||||
},
|
||||
}
|
||||
|
||||
switch ipVer {
|
||||
case 4:
|
||||
if dummyAddr, err = netip.ParseAddr("0.0.0.0"); err != nil {
|
||||
return
|
||||
}
|
||||
case 6:
|
||||
if dummyAddr, err = netip.ParseAddr("::"); err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = errBadNet
|
||||
return
|
||||
}
|
||||
|
||||
// Before we size, we generate the tableAddrs.
|
||||
addrs.Rows = make([]tableAddr, dummyAddr.BitLen()+1)
|
||||
for i := 0; i <= dummyAddr.BitLen(); i++ {
|
||||
addrs.Rows[i] = tableAddr{
|
||||
Prefix: uint8(i),
|
||||
Bits: uint8(dummyAddr.BitLen() - i),
|
||||
}
|
||||
if addrs.Rows[i].NetPrefix, err = dummyAddr.Prefix(i); err != nil {
|
||||
return
|
||||
}
|
||||
dummyNet = netipx.PrefixIPNet(addrs.Rows[i].NetPrefix.Masked())
|
||||
addrs.Rows[i].Addresses = mapcidr.CountIPsInCIDR(true, true, dummyNet)
|
||||
addrs.Rows[i].Hosts = mapcidr.CountIPsInCIDR(false, false, dummyNet)
|
||||
}
|
||||
|
||||
// Now the sizer. The padding itself is handled in different logic, just need the length of the longest value as a string.
|
||||
for _, addr := range addrs.Rows {
|
||||
// I *abhor* walrus operators in anything but loops.
|
||||
l = len(strconv.Itoa(int(addr.Prefix)))
|
||||
if int(addrs.Sizer.Prefix) < l {
|
||||
addrs.Sizer.Prefix = uint8(l)
|
||||
}
|
||||
l = len(strconv.Itoa(int(addr.Bits)))
|
||||
if int(addrs.Sizer.Bits) < l {
|
||||
addrs.Sizer.Bits = uint8(l)
|
||||
}
|
||||
// Use the full numeric length.
|
||||
l = len(addr.Addresses.String())
|
||||
if int(addrs.Sizer.Addresses) < l {
|
||||
addrs.Sizer.Addresses = uint8(l)
|
||||
}
|
||||
l = len(addr.Hosts.String())
|
||||
if int(addrs.Sizer.Hosts) < l {
|
||||
addrs.Sizer.Hosts = uint8(l)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
tplMaskIter4 returns a slice of IPv4 netmasks and returns a slice of tableMask4.
|
||||
Sorted from smallest prefix/largest network to largest prefix/smallest network.
|
||||
*/
|
||||
func tplMaskIter4() (masks *tableMask4Ret, err error) {
|
||||
|
||||
var dummyAddr netip.Addr
|
||||
var pfx netip.Prefix
|
||||
var dummyNet *net.IPNet
|
||||
var l int
|
||||
|
||||
masks = &tableMask4Ret{
|
||||
Sizer: &tableMask4Sizer{
|
||||
Prefix: 6, // "PREFIX"
|
||||
Netmask: 7, // "NETMASK"
|
||||
Hex: 3, // "HEX"
|
||||
Dec: 3, // "DEC"
|
||||
Bin: 3, // "BIN"
|
||||
},
|
||||
}
|
||||
|
||||
if dummyAddr, err = netip.ParseAddr("0.0.0.0"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
masks.Rows = make([]tableMask4, dummyAddr.BitLen()+1)
|
||||
for i := 0; i <= dummyAddr.BitLen(); i++ {
|
||||
if pfx, err = dummyAddr.Prefix(i); err != nil {
|
||||
return
|
||||
}
|
||||
dummyNet = netipx.PrefixIPNet(pfx.Masked())
|
||||
masks.Rows[i] = tableMask4{
|
||||
Prefix: uint8(i),
|
||||
Netmask: netsplit.MaskFmt(
|
||||
dummyNet.Mask,
|
||||
"d", ".", "",
|
||||
1, 0,
|
||||
),
|
||||
Hex: dummyNet.Mask.String(),
|
||||
Dec: binary.BigEndian.Uint32(dummyNet.Mask),
|
||||
Bin: netsplit.MaskFmt(
|
||||
dummyNet.Mask,
|
||||
"08b", ".", "",
|
||||
1, 0,
|
||||
),
|
||||
Mask: dummyNet.Mask,
|
||||
}
|
||||
}
|
||||
|
||||
// Now the sizer.
|
||||
for _, mask := range masks.Rows {
|
||||
l = len(strconv.Itoa(int(mask.Prefix)))
|
||||
if int(masks.Sizer.Prefix) < l {
|
||||
masks.Sizer.Prefix = uint8(l)
|
||||
}
|
||||
l = len(mask.Netmask)
|
||||
if int(masks.Sizer.Netmask) < l {
|
||||
masks.Sizer.Netmask = uint8(l)
|
||||
}
|
||||
l = len(mask.Hex)
|
||||
if int(masks.Sizer.Hex) < l {
|
||||
masks.Sizer.Hex = uint8(l)
|
||||
}
|
||||
l = len(strconv.FormatUint(uint64(mask.Dec), 10))
|
||||
if int(masks.Sizer.Dec) < l {
|
||||
masks.Sizer.Dec = uint8(l)
|
||||
}
|
||||
l = len(mask.Bin)
|
||||
if int(masks.Sizer.Bin) < l {
|
||||
masks.Sizer.Bin = uint8(l)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// do not include in template funcs; used externally
|
||||
func hdrRender(hdrVal reflect.Value, indent string, plain bool) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
var field reflect.StructField
|
||||
var fieldVal reflect.Value
|
||||
var colLen uint8
|
||||
var colTitle string
|
||||
var lastField int
|
||||
var valType reflect.Type
|
||||
var tfmt *tableFormatter = tblFmts[plain]
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
val = hdrVal
|
||||
valType = val.Type()
|
||||
|
||||
// Avoid the edge case where a struct's last field is skipped rendering
|
||||
for i := val.NumField(); i > 0; i-- {
|
||||
field = valType.Field(i - 1)
|
||||
if field.Tag.Get("render") == "-" {
|
||||
continue
|
||||
}
|
||||
lastField = i
|
||||
break
|
||||
}
|
||||
|
||||
// Top-most line.
|
||||
sb.WriteString(indent)
|
||||
sb.WriteString(tfmt.TopLeftHdr)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field = valType.Field(i)
|
||||
if field.Tag.Get("render") == "-" {
|
||||
continue
|
||||
}
|
||||
fieldVal = val.Field(i)
|
||||
colLen = uint8(fieldVal.Uint())
|
||||
sb.WriteString(strings.Repeat(tfmt.TopFillHdr, int(colLen)+fixedPad))
|
||||
if i == lastField {
|
||||
sb.WriteString(tfmt.TopRightHdr)
|
||||
} else {
|
||||
sb.WriteString(tfmt.TopColSepHdr)
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
// Column titles
|
||||
sb.WriteString(indent)
|
||||
sb.WriteString(tfmt.Left)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field = valType.Field(i)
|
||||
if field.Tag.Get("render") == "-" {
|
||||
continue
|
||||
}
|
||||
fieldVal = val.Field(i)
|
||||
colLen = uint8(fieldVal.Uint()) + uint8(fixedPad)
|
||||
colTitle = field.Name
|
||||
if !tfmt.NoUpperTitle {
|
||||
colTitle = strings.ToUpper(colTitle)
|
||||
}
|
||||
if !tfmt.NoBoldTitle {
|
||||
sb.WriteString(color.InBold(padStr(colTitle, colLen)))
|
||||
} else {
|
||||
sb.WriteString(padStr(colTitle, colLen))
|
||||
}
|
||||
if i == lastField {
|
||||
sb.WriteString(tfmt.Right)
|
||||
} else {
|
||||
sb.WriteString(tfmt.ColSepHdr)
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
// Header bottom line; headers always include bottom separators.
|
||||
sb.WriteString(indent)
|
||||
sb.WriteString(tfmt.BottomLeftHdr)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field = valType.Field(i)
|
||||
if field.Tag.Get("render") == "-" {
|
||||
continue
|
||||
}
|
||||
fieldVal = val.Field(i)
|
||||
colLen = uint8(fieldVal.Uint())
|
||||
sb.WriteString(strings.Repeat(tfmt.BottomFillHdr, int(colLen)+fixedPad))
|
||||
if i == lastField {
|
||||
sb.WriteString(tfmt.BottomRightHdr)
|
||||
} else {
|
||||
sb.WriteString(tfmt.BottomColSepHdr)
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
out = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// do not include in template funcs; used externally
|
||||
func hdrLineRender(hdrVal reflect.Value, indent string, plain bool, rowIdx int, numRows int) (out string) {
|
||||
|
||||
var val reflect.Value
|
||||
var field reflect.StructField
|
||||
var fieldVal reflect.Value
|
||||
var colLen uint8
|
||||
var lastField int
|
||||
var isLastLine bool
|
||||
var valType reflect.Type
|
||||
var tfmt *tableFormatter = tblFmts[plain]
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
isLastLine = rowIdx == (numRows - 1)
|
||||
if !isLastLine && tfmt.SuppressLineSep {
|
||||
return
|
||||
}
|
||||
|
||||
val = hdrVal
|
||||
valType = val.Type()
|
||||
lastField = valType.NumField() - 1
|
||||
|
||||
for i := val.NumField(); i >= 0; i-- {
|
||||
field = valType.Field(i - 1)
|
||||
if field.Tag.Get("render") == "-" {
|
||||
continue
|
||||
}
|
||||
lastField = i
|
||||
break
|
||||
}
|
||||
|
||||
sb.WriteString(indent)
|
||||
if isLastLine {
|
||||
sb.WriteString(tfmt.LastLeft)
|
||||
} else {
|
||||
sb.WriteString(tfmt.LineLeft)
|
||||
}
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field = valType.Field(i)
|
||||
if field.Tag.Get("render") == "-" {
|
||||
continue
|
||||
}
|
||||
fieldVal = val.Field(i)
|
||||
colLen = uint8(fieldVal.Uint())
|
||||
if isLastLine {
|
||||
sb.WriteString(strings.Repeat(tfmt.LastFill, int(colLen)+fixedPad))
|
||||
} else {
|
||||
sb.WriteString(strings.Repeat(tfmt.Fill, int(colLen)+fixedPad))
|
||||
}
|
||||
if i == lastField {
|
||||
if isLastLine {
|
||||
sb.WriteString(tfmt.LastRight)
|
||||
} else {
|
||||
sb.WriteString(tfmt.LineRight)
|
||||
}
|
||||
} else {
|
||||
if isLastLine {
|
||||
sb.WriteString(tfmt.LastSep)
|
||||
} else {
|
||||
sb.WriteString(tfmt.LineColSep)
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
out = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// do not include in template funcs; used externally
|
||||
func rowRender(val reflect.Value, sizerVal reflect.Value, indent string, plain bool) (out string) {
|
||||
|
||||
var field reflect.StructField
|
||||
var fieldVal reflect.Value
|
||||
var colLen uint8
|
||||
var sizerName string
|
||||
var sizerField reflect.Value
|
||||
var callVal string
|
||||
var valType reflect.Type = val.Type()
|
||||
var tfmt *tableFormatter = tblFmts[plain]
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
sb.WriteString(indent)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field = valType.Field(i)
|
||||
if field.Tag.Get("render") == "-" {
|
||||
continue
|
||||
}
|
||||
sb.WriteString(tfmt.Left)
|
||||
fieldVal = val.Field(i)
|
||||
sizerName = field.Tag.Get("renderSizeName")
|
||||
if sizerName == "" {
|
||||
sizerName = field.Name
|
||||
}
|
||||
sizerField = sizerVal.FieldByName(sizerName)
|
||||
colLen = uint8(sizerField.Uint()) + uint8(fixedPad)
|
||||
switch fieldVal.Kind() {
|
||||
// This is tailored specifically to this implementation.
|
||||
case reflect.String:
|
||||
sb.WriteString(fieldVal.String())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
sb.WriteString(padStr(fmt.Sprintf("%d", fieldVal.Int()), colLen))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
sb.WriteString(padStr(fmt.Sprintf("%d", fieldVal.Uint()), colLen))
|
||||
case reflect.Ptr:
|
||||
// It's a *big.Int.
|
||||
if fieldVal.IsNil() {
|
||||
sb.WriteString(padStr(strings.Repeat(padChars, int(colLen)), colLen))
|
||||
} else {
|
||||
// TIL you can even *do* this in reflection.
|
||||
fieldVal = fieldVal.MethodByName("String").Call(nil)[0]
|
||||
callVal = fieldVal.String()
|
||||
sb.WriteString(padStr(callVal, colLen))
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
out = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
@@ -3,14 +3,17 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"go4.org/netipx"
|
||||
`fmt`
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"go4.org/netipx"
|
||||
"subnetter/netsplit"
|
||||
`subnetter/version`
|
||||
|
||||
"github.com/jessevdk/go-flags"
|
||||
"r00t2.io/sysutils/paths"
|
||||
@@ -29,8 +32,10 @@ func main() {
|
||||
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)
|
||||
var noStrict bool
|
||||
var strictErr error
|
||||
var splitErr = new(netsplit.SplitErr)
|
||||
var parser = flags.NewParser(args, flags.Default)
|
||||
|
||||
if _, err = parser.Parse(); err != nil {
|
||||
switch flagsErr := err.(type) {
|
||||
@@ -46,12 +51,30 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
if version.Ver, err = version.Version(); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
// If args.Version or args.DetailVersion are true, just print them and exit.
|
||||
if args.DetailVersion || args.Version {
|
||||
if args.Version {
|
||||
fmt.Println(version.Ver.Short())
|
||||
return
|
||||
} else if args.DetailVersion {
|
||||
fmt.Println(version.Ver.Detail())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch parser.Active.Name {
|
||||
case "table":
|
||||
// TODO: print table and exit
|
||||
buf = new(bytes.Buffer)
|
||||
if err = tblTpl.ExecuteTemplate(buf, "table.tpl", args.Table.tableOpts); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
os.Stdout.Write(buf.Bytes())
|
||||
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 {
|
||||
@@ -102,26 +125,32 @@ func main() {
|
||||
}
|
||||
cmnArgs = args.SplitHost.common
|
||||
splitter = &netsplit.HostSplitter{
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
NumberHosts: args.SplitHost.Hosts,
|
||||
Strict: args.SplitHost.Strict,
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
}
|
||||
noStrict = !args.SplitHost.Strict
|
||||
strictErr = netsplit.ErrBadNumHosts
|
||||
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),
|
||||
Strict: args.SplitSubnets.Strict,
|
||||
NumberSubnets: args.SplitSubnets.NumNets,
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
}
|
||||
noStrict = !args.SplitSubnets.Strict
|
||||
strictErr = netsplit.ErrNoNetSpace
|
||||
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,
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
}
|
||||
case "vlsm":
|
||||
if err = validate.Struct(args.VLSM); err != nil {
|
||||
@@ -129,9 +158,9 @@ func main() {
|
||||
}
|
||||
cmnArgs = args.VLSM.common
|
||||
splitter = &netsplit.VLSMSplitter{
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
Ascending: args.VLSM.Asc,
|
||||
PrefixLengths: args.VLSM.Sizes,
|
||||
BaseSplitter: new(netsplit.BaseSplitter),
|
||||
}
|
||||
}
|
||||
if origPfx, err = netip.ParsePrefix(cmnArgs.Network.Network); err != nil {
|
||||
@@ -147,8 +176,12 @@ func main() {
|
||||
splitter.SetParent(*pfx)
|
||||
if nets, remaining, err = splitter.Split(); err != nil {
|
||||
if errors.As(err, &splitErr) {
|
||||
printSplitErr(splitErr)
|
||||
os.Exit(1)
|
||||
if noStrict && errors.Is(splitErr.Wrapped, strictErr) {
|
||||
err = nil
|
||||
} else {
|
||||
printSplitErr(splitErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
`math/big`
|
||||
`net`
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// subnetResult is only used for human/"pretty" printing.
|
||||
type subnetResult netip.Prefix
|
||||
|
||||
type tableOpts struct {
|
||||
Plain bool `short:"p" long:"plain" description:"Show plain table output."`
|
||||
Legacy bool `short:"l" long:"legacy" description:"Include legacy/obsolete/deprecated information."`
|
||||
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)."`
|
||||
}
|
||||
|
||||
type tableAddrRet struct {
|
||||
Sizer *tableAddrSizer
|
||||
Rows []tableAddr
|
||||
}
|
||||
|
||||
type tableAddr struct {
|
||||
Prefix uint8
|
||||
Bits uint8
|
||||
Addresses *big.Int
|
||||
Hosts *big.Int
|
||||
NetPrefix netip.Prefix `render:"-"`
|
||||
}
|
||||
|
||||
// tableAddrSizer is used to control spacing/sizing of a tableAddr table's columns.
|
||||
type tableAddrSizer struct {
|
||||
Prefix uint8
|
||||
Bits uint8
|
||||
Addresses uint8
|
||||
Hosts uint8
|
||||
}
|
||||
|
||||
type tableMask4Ret struct {
|
||||
Sizer *tableMask4Sizer
|
||||
Rows []tableMask4
|
||||
}
|
||||
|
||||
// tableMask4 is used to hold string representation of netmask information.
|
||||
type tableMask4 struct {
|
||||
Prefix uint8
|
||||
Netmask string
|
||||
Hex string
|
||||
Dec uint32
|
||||
Bin string
|
||||
Mask net.IPMask `render:"-"`
|
||||
}
|
||||
|
||||
// tableMask4Sizer, like tableAddrSizer, is used to control spacing/sizing of a tableMask4 table's columns.
|
||||
type tableMask4Sizer struct {
|
||||
Prefix uint8
|
||||
Netmask uint8
|
||||
Hex uint8
|
||||
Dec uint8
|
||||
Bin uint8
|
||||
}
|
||||
|
||||
type tableLegacy4Ret struct {
|
||||
Sizer *tableLegacy4Sizer
|
||||
Rows []tableLegacy4
|
||||
}
|
||||
|
||||
// tableLegacy4 contains a spec for a class in the legacy "classed" IPv4 networking.
|
||||
type tableLegacy4 struct {
|
||||
Class string
|
||||
CIDR string
|
||||
Start string
|
||||
End string
|
||||
NetStart netip.Addr `render:"-"`
|
||||
NetEnd netip.Addr `render:"-"`
|
||||
NetCIDR netip.Prefix `render:"-"`
|
||||
}
|
||||
|
||||
// tableLegacy4Sizer is used to size tableLegacy4 entries.
|
||||
type tableLegacy4Sizer struct {
|
||||
Class uint8
|
||||
CIDR uint8
|
||||
Start uint8
|
||||
End uint8
|
||||
}
|
||||
|
||||
// tableFormatter is used for "rendering" table output.
|
||||
type tableFormatter struct {
|
||||
// Headers...
|
||||
// First char, first line
|
||||
TopLeftHdr string
|
||||
// Char to fill between TopLeftHdr and TopColSepHdr.
|
||||
TopFillHdr string
|
||||
// Column separator, first line
|
||||
TopColSepHdr string
|
||||
// Last char, first line
|
||||
TopRightHdr string
|
||||
// Column separator for both column separators (title line) and left/right-most lines.
|
||||
ColSepHdr string
|
||||
// First char, last line of header
|
||||
BottomLeftHdr string
|
||||
// Char to fill between BottomLeftHdr and BottomColSepHdr.
|
||||
BottomFillHdr string
|
||||
// Column separator, last line of header
|
||||
BottomColSepHdr string
|
||||
// Last char, last line of header
|
||||
BottomRightHdr string
|
||||
// Rows...
|
||||
// Left-most line (border).
|
||||
Left string
|
||||
// Fill the cell lines for values
|
||||
Fill string
|
||||
// Separate line/cell border columns
|
||||
LineColSep string
|
||||
// "In-between" rows; left-most.
|
||||
LineLeft string
|
||||
// "In-between" rows, right-most.
|
||||
LineRight string
|
||||
// Separate value columns
|
||||
ColSep string
|
||||
// Right-most line (border)
|
||||
Right string
|
||||
// Last lines get special treatment as they are also a border.
|
||||
LastLeft string
|
||||
LastFill string
|
||||
LastSep string
|
||||
LastRight string
|
||||
// This is mostly for experimentation for Plain output, but...
|
||||
SuppressLineSep bool
|
||||
NoUpperTitle bool
|
||||
NoBoldTitle bool
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user