just needs reserved prefix warnings implemented
This commit is contained in:
parent
3a7ed5973b
commit
30355294c0
@ -15,6 +15,7 @@ Last rendered {localdatetime}
|
||||
:source-highlighter: rouge
|
||||
:docinfo: shared
|
||||
:rfc: https://datatracker.ietf.org/doc/html/rfc
|
||||
:arin_r: https://www.arin.net/participate/policy/nrpm/
|
||||
|
||||
[id="wat"]
|
||||
== What is it?
|
||||
@ -23,6 +24,10 @@ A tool to assist in design of segregate/segment/split/subnet networks.
|
||||
[id="out"]
|
||||
== Output
|
||||
|
||||
* `PTP` refers to "Peer-to-Peer" (e.g. {rfc}3021[RFC 3021^]).
|
||||
* `6rd` refers to "IPv6 Rapid Deployment", a derivation of 6to4 ({rfc}5569[RFC 5569^], {rfc}5969[RFC 5969^]).
|
||||
* `LIR` refers to "Local Internet Registry" ({arin_r}#2-4-local-internet-registry-lir[ARIN^]).
|
||||
* `RIR` refers to "Regional Internet Registry" ({arin_r}#2-2-regional-internet-registry-rir[ARIN^]).
|
||||
* `Unicast` refers to "Global Unicast" ({rfc}1122[RFC 1122^], {rfc}4291#section-2.5.4[RFC 4291 § 2.5.4^], {rfc}4632[RFC 4632^]).
|
||||
** For IPv6 addresses, it will be `true` for ULA (_Unique Local Addresses_) ({rfc}4193[RFC 4193^]) also.
|
||||
** For IPv4 addresses, it will be `true` if the address is routable by external hosts (a unicast address), including private IP addresses ({rfc}1918[RFC 1918^]).
|
||||
|
@ -2,51 +2,73 @@
|
||||
{{- $opts := . -}}
|
||||
{{- $numRows := 0 -}}
|
||||
{{- if not $opts.NoIpv4 }}
|
||||
IPv4:
|
||||
{{- if $opts.Legacy -}}
|
||||
{{- $legacyspec := legacy4 }}
|
||||
{{- $numRows = len $legacyspec.Rows }}
|
||||
|
||||
{{- if $opts.Plain }}
|
||||
IPV4:
|
||||
{{- else }}
|
||||
{{- bold "IPv4:"}}
|
||||
{{- end }}
|
||||
{{- if $opts.Legacy }}
|
||||
{{- if $opts.Plain }}
|
||||
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 }}
|
||||
{{- else }}
|
||||
{{ bold "Legacy:" }}
|
||||
{{- end }}
|
||||
{{ legacy4 "\t" $opts.Plain }}
|
||||
{{- end }}
|
||||
|
||||
{{- if not $opts.NoV4Mask }}
|
||||
{{- $masks := mask4 }}
|
||||
{{- if $opts.Plain }}
|
||||
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 }}
|
||||
{{- else }}
|
||||
{{ bold "Netmasks:" }}
|
||||
{{- end }}
|
||||
{{ mask4 "\t" $opts.Plain }}
|
||||
{{- end }}
|
||||
|
||||
{{- if $opts.Plain }}
|
||||
|
||||
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 }}
|
||||
{{- else }}
|
||||
|
||||
{{ bold "CIDR:" }}
|
||||
{{- end }}
|
||||
{{ prefixes 4 "\t" $opts.Plain }}
|
||||
|
||||
{{- if $opts.Notes }}
|
||||
{{- if $opts.Plain }}
|
||||
|
||||
NOTES:
|
||||
{{- else }}
|
||||
|
||||
{{ bold "Notes:" }}
|
||||
{{- end }}
|
||||
{{ notes 4 "\t" $opts.Plain }}
|
||||
{{- 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 }}
|
||||
|
||||
{{- if $opts.Plain }}
|
||||
IPV6:
|
||||
{{- else }}
|
||||
{{ bold "IPv6:"}}
|
||||
{{- end }}
|
||||
{{- if $opts.Plain }}
|
||||
CIDR:
|
||||
{{- else }}
|
||||
{{ bold "CIDR:" }}
|
||||
{{- end }}
|
||||
{{ prefixes 6 "\t" $opts.Plain }}
|
||||
|
||||
|
||||
{{- if $opts.Notes }}
|
||||
{{- if $opts.Plain }}
|
||||
|
||||
NOTES:
|
||||
{{- else }}
|
||||
|
||||
{{ bold "Notes:" }}
|
||||
{{- end }}
|
||||
{{ notes 6 "\t" $opts.Plain }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
@ -1,19 +1,21 @@
|
||||
package main
|
||||
|
||||
type Args struct {
|
||||
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"`
|
||||
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"`
|
||||
ExplicitNetwork XNetArgs `command:"net" alias:"n" description:"Print information about an explicit network address." 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 if -f/--format=pretty. May be specified multiple times to increase verbosity (up to 3 levels)."`
|
||||
Plain bool `short:"p" long:"plain" description:"Show plain output instead of unicode (only used if -f/--format=pretty)."`
|
||||
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. 'pretty' is not intended to be parseable, either by subnetter or by external tooling."`
|
||||
}
|
||||
@ -51,8 +53,14 @@ type SplitSubnetArgs struct {
|
||||
|
||||
type TableArgs struct {
|
||||
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 XNetArgs struct {
|
||||
Plain bool `short:"p" long:"plain" description:"Show plain output instead of unicode (only used if -f/--format=pretty)."`
|
||||
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. 'pretty' is not intended to be parseable, either by subnetter or by external tooling."`
|
||||
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)."`
|
||||
Network Net `positional-args:"yes" required:"true" description:"The network address to print information about." validate:"required"`
|
||||
}
|
||||
|
||||
type VLSMArgs struct {
|
||||
@ -62,5 +70,5 @@ type VLSMArgs struct {
|
||||
}
|
||||
|
||||
type Net struct {
|
||||
Network string `positional-arg-name:"<network>/<prefix>" description:"network address with prefix. Can be IPv4 or IPv6." validate:"required,cidr"`
|
||||
Network string `positional-arg-name:"<network>/<prefix>" description:"Network address with prefix. Can be IPv4 or IPv6." validate:"required,cidr"`
|
||||
}
|
||||
|
@ -31,9 +31,11 @@ var (
|
||||
tplDir embed.FS
|
||||
tblTpl *template.Template = template.Must(template.New("").Funcs(
|
||||
template.FuncMap{
|
||||
"legacy4": tplClass4Iter,
|
||||
"addrs": tplAddrIter,
|
||||
"mask4": tplMaskIter4,
|
||||
"bold": tplFmtBold,
|
||||
"legacy4": tplTableLegacy4,
|
||||
"mask4": tplTableMasks4,
|
||||
"notes": tplTableNotes,
|
||||
"prefixes": tplTablePrefixes,
|
||||
},
|
||||
).ParseFS(tplDir, "_tpl/*.tpl"))
|
||||
)
|
||||
@ -41,9 +43,19 @@ var (
|
||||
var (
|
||||
// Primarily output formatting stuff in this block.
|
||||
sectSepCnt = 48
|
||||
sectSep1 = strings.Repeat("=", sectSepCnt)
|
||||
sectSep2 = strings.Repeat("-", sectSepCnt)
|
||||
sectSep3 = strings.Repeat(".", sectSepCnt)
|
||||
// sectFmts contains a lookup of map[<is plain>][]string{<level 1>, <level 2>, <level 3>}
|
||||
sectFmts map[bool][]string = map[bool][]string{
|
||||
true: []string{
|
||||
strings.Repeat("=", sectSepCnt),
|
||||
strings.Repeat("-", sectSepCnt),
|
||||
strings.Repeat(".", sectSepCnt),
|
||||
},
|
||||
false: []string{
|
||||
strings.Repeat("━", sectSepCnt),
|
||||
strings.Repeat("─", sectSepCnt),
|
||||
strings.Repeat("╍", sectSepCnt),
|
||||
},
|
||||
}
|
||||
// tblFmts contains a lookup of map[<is plain>]*tableFormatter.
|
||||
tblFmts map[bool]*tableFormatter = map[bool]*tableFormatter{
|
||||
// Plaintext/ASCII-only
|
||||
@ -101,3 +113,43 @@ var (
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
// netNotes is keyed first on IP/inet family version (4/6) and then the prefix size.
|
||||
netNotes map[uint8]map[uint8]string = map[uint8]map[uint8]string{
|
||||
4: map[uint8]string{
|
||||
32: "Host route/single host",
|
||||
31: "PTP link",
|
||||
30: "PTP (legacy/compatibility)",
|
||||
29: "Smallest multi-host network size possible",
|
||||
28: "Typical/common size for small LANs/VLANs",
|
||||
27: "Typical/common size for small LANs/VLANs",
|
||||
26: "Typical/common size for small LANs/VLANs",
|
||||
25: "Typical/common size for large LANs/VLANs",
|
||||
24: "Typical/common size for large LANs/VLANs",
|
||||
22: "Typical/common size for smaller business networks",
|
||||
21: "Typical/common size for larger business networks, smaller ISPs",
|
||||
20: "Typical/common size for larger business networks, smaller ISPs",
|
||||
19: "Typical/common size for enterprise business networks, larger ISPs",
|
||||
18: "Typical/common size for enterprise business networks, larger ISPs",
|
||||
17: "Typical/common size for enterprise business networks, larger ISPs",
|
||||
8: "Largest IANA block allocation size possible",
|
||||
0: "Entire IPv4 Internet address prefix; commonly used to indicate default route",
|
||||
},
|
||||
6: map[uint8]string{
|
||||
128: "Host route/single host, single endpoints, and loopback (::1 explicitly)",
|
||||
127: "Point-to-Point link (inter-router)",
|
||||
64: "Single LAN; default prefix size for SLAAC",
|
||||
60: "Some (very limited) 6rd networks",
|
||||
56: "Minimum end site assignment (RFC 6177)",
|
||||
48: "Typical/common assignment for larger sites",
|
||||
36: "Possible future LIR \"extra-small\" allocation",
|
||||
32: "\"Minimal\" LIR allocation",
|
||||
28: "\"Medium\" LIR allocation",
|
||||
24: "\"Large\" LIR allocation",
|
||||
20: "\"Extra-large\" LIR allocation",
|
||||
12: "RIR allocation from IANA",
|
||||
0: "Entire IPv6 Internet address prefix; commonly used to represent default route",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -219,6 +219,10 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
var invertedMask net.IPMask
|
||||
var res *netsplit.StructuredResults
|
||||
var verb = -1
|
||||
var fmts []string
|
||||
var sectSep1 string
|
||||
var sectSep2 string
|
||||
// var sectSep3 string
|
||||
|
||||
if orig == nil {
|
||||
return
|
||||
@ -231,6 +235,10 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
},
|
||||
}
|
||||
}
|
||||
fmts = sectFmts[args.Plain]
|
||||
sectSep1 = fmts[0]
|
||||
sectSep2 = fmts[1]
|
||||
// sectSep3 = fmts[2]
|
||||
|
||||
if args.outputOpts.Verbose != nil {
|
||||
verb = 0
|
||||
@ -311,7 +319,7 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
}
|
||||
} else {
|
||||
for _, n := range nets {
|
||||
fmt.Print(resFromPfx(n).pretty(verb, 1, args.outputOpts.Seperator, "\t", false))
|
||||
fmt.Print(resFromPfx(n).pretty(verb, 1, args.outputOpts.Seperator, "\t", false, args.Plain))
|
||||
}
|
||||
}
|
||||
if verb >= 1 {
|
||||
@ -335,7 +343,7 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
||||
return
|
||||
}
|
||||
for _, n := range remaining.Prefixes() {
|
||||
fmt.Print(resFromPfx(&n).pretty(verb, 1, args.outputOpts.Seperator, "\t", true))
|
||||
fmt.Print(resFromPfx(&n).pretty(verb, 1, args.outputOpts.Seperator, "\t", true, args.Plain))
|
||||
}
|
||||
}
|
||||
if verb >= 1 {
|
||||
|
@ -6,13 +6,17 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (s *subnetResult) pretty(verb, indent int, sep, indentStr string, isRemaining bool) (out string) {
|
||||
func (s *subnetResult) pretty(verb, indent int, sep, indentStr string, isRemaining, plain 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)
|
||||
var fmts []string = sectFmts[plain]
|
||||
// var sectSep1 string = fmts[0]
|
||||
var sectSep2 string = fmts[1]
|
||||
var sectSep3 string = fmts[2]
|
||||
|
||||
if s == nil {
|
||||
return
|
||||
|
@ -1,54 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
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
|
||||
}
|
@ -15,20 +15,187 @@ import (
|
||||
`subnetter/netsplit`
|
||||
)
|
||||
|
||||
/*
|
||||
tplClass4Iter should only be called if legacy info is enabled.
|
||||
It returns a tableLegacy4Sizer and a slice of tableLegacy4.
|
||||
// renderHdr renders a header. Note that the first line does *not* include the indent, but subsequent ones do.
|
||||
func renderHdr(titles []string, colSizes []uint8, indent string, plain bool, sb *strings.Builder) {
|
||||
|
||||
It takes no input.
|
||||
var idx int
|
||||
var width uint8
|
||||
var title string
|
||||
var lastTitleIdx int
|
||||
var tfmt *tableFormatter = tblFmts[plain]
|
||||
|
||||
if titles == nil || len(titles) == 0 || colSizes == nil || len(colSizes) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
lastTitleIdx = len(titles) - 1
|
||||
|
||||
// Top-most line/border.
|
||||
sb.WriteString(tfmt.TopLeftHdr)
|
||||
for idx, width = range colSizes {
|
||||
sb.WriteString(strings.Repeat(tfmt.TopFillHdr, int(width)))
|
||||
if idx == lastTitleIdx {
|
||||
sb.WriteString(tfmt.TopRightHdr)
|
||||
} else {
|
||||
sb.WriteString(tfmt.TopColSepHdr)
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
// Title names.
|
||||
sb.WriteString(indent)
|
||||
sb.WriteString(tfmt.Left)
|
||||
for idx, title = range titles {
|
||||
width = colSizes[idx]
|
||||
if !tfmt.NoUpperTitle {
|
||||
title = strings.ToUpper(title)
|
||||
}
|
||||
if tfmt.NoBoldTitle {
|
||||
sb.WriteString(padStr(title, width))
|
||||
} else {
|
||||
sb.WriteString(color.InBold(padStr(title, width)))
|
||||
}
|
||||
if idx == lastTitleIdx {
|
||||
sb.WriteString(tfmt.Right)
|
||||
} else {
|
||||
sb.WriteString(tfmt.ColSepHdr)
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
// Bottom header border.
|
||||
sb.WriteString(indent)
|
||||
sb.WriteString(tfmt.BottomLeftHdr)
|
||||
for idx, width = range colSizes {
|
||||
sb.WriteString(strings.Repeat(tfmt.BottomFillHdr, int(width)))
|
||||
if idx == lastTitleIdx {
|
||||
sb.WriteString(tfmt.BottomRightHdr)
|
||||
} else {
|
||||
sb.WriteString(tfmt.BottomColSepHdr)
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// renderRow renders a row. row should be a struct or non-nil struct ptr.
|
||||
func renderRow(row reflect.Value, colFields []int, colSizes []uint8, indent string, plain, isLast bool, sb *strings.Builder) {
|
||||
|
||||
var idx int
|
||||
var width uint8
|
||||
var fieldIdx int
|
||||
var valStr string
|
||||
var lastColIdx int
|
||||
var field reflect.Value
|
||||
var tfmt *tableFormatter = tblFmts[plain]
|
||||
|
||||
if colFields == nil || len(colFields) == 0 || colSizes == nil || len(colSizes) == 0 {
|
||||
return
|
||||
}
|
||||
if row.Kind() == reflect.Ptr && (row.IsNil() || row.Kind() != reflect.Struct) {
|
||||
return
|
||||
}
|
||||
row = reflect.Indirect(row)
|
||||
if row.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
|
||||
lastColIdx = len(colFields) - 1
|
||||
|
||||
sb.WriteString(indent)
|
||||
sb.WriteString(tfmt.Left)
|
||||
for idx, fieldIdx = range colFields {
|
||||
width = colSizes[idx]
|
||||
field = row.Field(fieldIdx)
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
valStr = field.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
valStr = strconv.FormatInt(field.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
valStr = strconv.FormatUint(field.Uint(), 10)
|
||||
case reflect.Ptr:
|
||||
// *big.Int
|
||||
if field.IsNil() {
|
||||
continue
|
||||
}
|
||||
field = field.MethodByName("String").Call(nil)[0]
|
||||
valStr = field.String()
|
||||
}
|
||||
sb.WriteString(padStr(valStr, width))
|
||||
if idx == lastColIdx {
|
||||
sb.WriteString(tfmt.Right)
|
||||
} else {
|
||||
sb.WriteString(tfmt.ColSep)
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
if !tfmt.SuppressLineSep || isLast {
|
||||
sb.WriteString(indent)
|
||||
if isLast {
|
||||
sb.WriteString(tfmt.LastLeft)
|
||||
} else {
|
||||
sb.WriteString(tfmt.LineLeft)
|
||||
}
|
||||
for idx, width = range colSizes {
|
||||
if isLast {
|
||||
sb.WriteString(strings.Repeat(tfmt.LastFill, int(width)))
|
||||
} else {
|
||||
sb.WriteString(strings.Repeat(tfmt.Fill, int(width)))
|
||||
}
|
||||
if idx == lastColIdx {
|
||||
if isLast {
|
||||
sb.WriteString(tfmt.LastRight)
|
||||
} else {
|
||||
sb.WriteString(tfmt.LineRight)
|
||||
}
|
||||
} else {
|
||||
if isLast {
|
||||
sb.WriteString(tfmt.LastSep)
|
||||
} else {
|
||||
sb.WriteString(tfmt.LineColSep)
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// tplFmtBold renders text as bold-faced.
|
||||
func tplFmtBold(text string) (out string) {
|
||||
|
||||
out = color.InBold(text)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
tplTableLegacy4 renders a table of classes in the legacy "classed" IPv4 networking.
|
||||
|
||||
Note that indent is *not* applied to the first line.
|
||||
*/
|
||||
func tplClass4Iter() (legacySpec *tableLegacy4Ret, err error) {
|
||||
func tplTableLegacy4(indent string, plain bool) (out string, err error) {
|
||||
|
||||
// This whole thing feels dirty.
|
||||
// It's like adding a microcontroller to a rock.
|
||||
// But it works.
|
||||
var idx int
|
||||
var cls string
|
||||
var isLast bool
|
||||
var colFields []int
|
||||
var colSizes []uint8
|
||||
var colTitles []string
|
||||
var pfx *net.IPNet
|
||||
var row tableLegacy4
|
||||
var rows []tableLegacy4
|
||||
var rowVal reflect.Value
|
||||
var classNets []*netip.Prefix
|
||||
var netRange netipx.IPRange
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
var v *netsplit.VLSMSplitter = &netsplit.VLSMSplitter{
|
||||
Ascending: false,
|
||||
PrefixLengths: []uint8{
|
||||
@ -48,53 +215,124 @@ func tplClass4Iter() (legacySpec *tableLegacy4Ret, err error) {
|
||||
if classNets, _, err = v.Split(); err != nil {
|
||||
return
|
||||
}
|
||||
rows = make([]tableLegacy4, 5)
|
||||
|
||||
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{
|
||||
for idx, cls = range []string{
|
||||
"A", "B", "C", "D", "E",
|
||||
} {
|
||||
legacySpec.Rows[idx] = tableLegacy4{
|
||||
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()
|
||||
netRange = netipx.RangeOfPrefix(rows[idx].NetCIDR)
|
||||
rows[idx].NetStart = netRange.From()
|
||||
rows[idx].NetEnd = netRange.To()
|
||||
rows[idx].Start = rows[idx].NetStart.String()
|
||||
rows[idx].End = rows[idx].NetEnd.String()
|
||||
}
|
||||
|
||||
colFields, colTitles, colSizes = sizeStructs(rows)
|
||||
|
||||
// Header
|
||||
renderHdr(colTitles, colSizes, indent, plain, sb)
|
||||
|
||||
// Rows
|
||||
for idx, row = range rows {
|
||||
rowVal = reflect.ValueOf(row)
|
||||
isLast = idx == len(rows)-1
|
||||
renderRow(rowVal, colFields, colSizes, indent, plain, isLast, sb)
|
||||
}
|
||||
|
||||
out = sb.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.
|
||||
tplTableMasks4 renders a table of netmasks for IPv4.
|
||||
IPv6 doesn't really use netmasks, instead relying almost entirely upon CIDR.
|
||||
|
||||
Note that indent is *not* applied to the first line.
|
||||
*/
|
||||
func tplAddrIter(ipVer uint8) (addrs *tableAddrRet, err error) {
|
||||
func tplTableMasks4(indent string, plain bool) (out string, err error) {
|
||||
|
||||
var dummyAddr netip.Addr
|
||||
var idx int
|
||||
var isLast bool
|
||||
var colFields []int
|
||||
var colSizes []uint8
|
||||
var colTitles []string
|
||||
var row tableMask4
|
||||
var rows []tableMask4
|
||||
var dummyNet *net.IPNet
|
||||
var l int
|
||||
var dummyAddr netip.Addr
|
||||
var rowVal reflect.Value
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
addrs = &tableAddrRet{
|
||||
Sizer: &tableAddrSizer{
|
||||
Prefix: 6, // "PREFIX"
|
||||
Bits: 4, // "BITS"
|
||||
Addresses: 9, // "ADDRESSES"
|
||||
Hosts: 5, // "HOSTS"
|
||||
},
|
||||
if dummyAddr, err = netip.ParseAddr("0.0.0.0"); err != nil {
|
||||
return
|
||||
}
|
||||
rows = make([]tableMask4, dummyAddr.BitLen()+1)
|
||||
|
||||
for idx = 0; idx <= dummyAddr.BitLen(); idx++ {
|
||||
rows[idx] = tableMask4{
|
||||
Prefix: fmt.Sprintf("/%d", idx),
|
||||
}
|
||||
if rows[idx].NetPrefix, err = dummyAddr.Prefix(idx); err != nil {
|
||||
return
|
||||
}
|
||||
dummyNet = netipx.PrefixIPNet(rows[idx].NetPrefix.Masked())
|
||||
rows[idx].Mask = dummyNet.Mask
|
||||
rows[idx].Netmask = netsplit.MaskFmt(
|
||||
dummyNet.Mask,
|
||||
"d", ".", "",
|
||||
1, 0,
|
||||
)
|
||||
rows[idx].Hex = dummyNet.Mask.String()
|
||||
rows[idx].Dec = binary.BigEndian.Uint32(dummyNet.Mask)
|
||||
rows[idx].Bin = netsplit.MaskFmt(
|
||||
dummyNet.Mask,
|
||||
"08b", ".", "",
|
||||
1, 0,
|
||||
)
|
||||
}
|
||||
|
||||
colFields, colTitles, colSizes = sizeStructs(rows)
|
||||
|
||||
// Header
|
||||
renderHdr(colTitles, colSizes, indent, plain, sb)
|
||||
|
||||
// Rows
|
||||
for idx, row = range rows {
|
||||
rowVal = reflect.ValueOf(row)
|
||||
isLast = idx == len(rows)-1
|
||||
renderRow(rowVal, colFields, colSizes, indent, plain, isLast, sb)
|
||||
}
|
||||
|
||||
out = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
tplTableNotes renders a table of prefix notes IP version/inet family ipVer (4 or 6).
|
||||
|
||||
Note that indent is *not* applied to the first line.
|
||||
*/
|
||||
func tplTableNotes(ipVer uint8, indent string, plain bool) (out string, err error) {
|
||||
|
||||
var idx int
|
||||
var isLast bool
|
||||
var ok bool
|
||||
var note string
|
||||
var colFields []int
|
||||
var colSizes []uint8
|
||||
var colTitles []string
|
||||
var row tablePrefixNote
|
||||
var rows []tablePrefixNote
|
||||
var dummyAddr netip.Addr
|
||||
var rowVal reflect.Value
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
switch ipVer {
|
||||
case 4:
|
||||
@ -109,339 +347,185 @@ func tplAddrIter(ipVer uint8) (addrs *tableAddrRet, err error) {
|
||||
err = errBadNet
|
||||
return
|
||||
}
|
||||
rows = make([]tablePrefixNote, 0, dummyAddr.BitLen()+1)
|
||||
|
||||
// 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),
|
||||
for idx = 0; idx <= dummyAddr.BitLen(); idx++ {
|
||||
if note, ok = netNotes[ipVer][uint8(idx)]; !ok {
|
||||
continue
|
||||
}
|
||||
if addrs.Rows[i].NetPrefix, err = dummyAddr.Prefix(i); err != nil {
|
||||
row = tablePrefixNote{
|
||||
Prefix: fmt.Sprintf("/%d", idx),
|
||||
Note: note,
|
||||
}
|
||||
if row.NetPrefix, err = dummyAddr.Prefix(idx); 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)
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
colFields, colTitles, colSizes = sizeStructs(rows)
|
||||
|
||||
// Header
|
||||
renderHdr(colTitles, colSizes, indent, plain, sb)
|
||||
|
||||
// Rows
|
||||
for idx, row = range rows {
|
||||
rowVal = reflect.ValueOf(row)
|
||||
isLast = idx == len(rows)-1
|
||||
renderRow(rowVal, colFields, colSizes, indent, plain, isLast, sb)
|
||||
}
|
||||
|
||||
out = sb.String()
|
||||
|
||||
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.
|
||||
tplTablePrefixes renders a table of prefixes for IP version/inet family ipVer (4 or 6).
|
||||
|
||||
Note that indent is *not* applied to the first line.
|
||||
*/
|
||||
func tplMaskIter4() (masks *tableMask4Ret, err error) {
|
||||
func tplTablePrefixes(ipVer uint8, indent string, plain bool) (out string, err error) {
|
||||
|
||||
var dummyAddr netip.Addr
|
||||
var pfx netip.Prefix
|
||||
var idx int
|
||||
var isLast bool
|
||||
var colFields []int
|
||||
var colSizes []uint8
|
||||
var colTitles []string
|
||||
var row tablePrefix
|
||||
var rows []tablePrefix
|
||||
var dummyNet *net.IPNet
|
||||
var l int
|
||||
var dummyAddr netip.Addr
|
||||
var rowVal reflect.Value
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
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 {
|
||||
switch ipVer {
|
||||
case 4:
|
||||
if dummyAddr, err = netip.ParseAddr("0.0.0.0"); 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,
|
||||
case 6:
|
||||
if dummyAddr, err = netip.ParseAddr("::"); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
default:
|
||||
err = errBadNet
|
||||
return
|
||||
}
|
||||
rows = make([]tablePrefix, dummyAddr.BitLen()+1)
|
||||
|
||||
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
|
||||
for idx = 0; idx <= dummyAddr.BitLen(); idx++ {
|
||||
rows[idx] = tablePrefix{
|
||||
Prefix: fmt.Sprintf("/%d", idx),
|
||||
Bits: uint8(dummyAddr.BitLen() - idx),
|
||||
}
|
||||
lastField = i
|
||||
break
|
||||
if rows[idx].NetPrefix, err = dummyAddr.Prefix(idx); err != nil {
|
||||
return
|
||||
}
|
||||
dummyNet = netipx.PrefixIPNet(rows[idx].NetPrefix.Masked())
|
||||
rows[idx].Addresses = mapcidr.CountIPsInCIDR(true, true, dummyNet)
|
||||
rows[idx].Hosts = mapcidr.CountIPsInCIDR(false, false, dummyNet)
|
||||
}
|
||||
|
||||
sb.WriteString(indent)
|
||||
if isLastLine {
|
||||
sb.WriteString(tfmt.LastLeft)
|
||||
} else {
|
||||
sb.WriteString(tfmt.LineLeft)
|
||||
colFields, colTitles, colSizes = sizeStructs(rows)
|
||||
|
||||
// Header
|
||||
renderHdr(colTitles, colSizes, indent, plain, sb)
|
||||
|
||||
// Rows
|
||||
for idx, row = range rows {
|
||||
rowVal = reflect.ValueOf(row)
|
||||
isLast = idx == len(rows)-1
|
||||
renderRow(rowVal, colFields, colSizes, indent, plain, isLast, sb)
|
||||
}
|
||||
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) {
|
||||
// rows should be a slice of structs/struct ptrs of all the same type (you're going to get errors/panics otherwise).
|
||||
func sizeStructs(rows any) (colFieldIdx []int, colTitles []string, colSizes []uint8) {
|
||||
|
||||
var title string
|
||||
var size int
|
||||
var numFields int
|
||||
var colIdx int
|
||||
var fieldIdx int
|
||||
var valLen int
|
||||
var valStr string
|
||||
var rowVals []reflect.Value
|
||||
var val reflect.Value
|
||||
var valType reflect.Type
|
||||
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++ {
|
||||
// Here be dragons. This is *NOT SAFE* for generic (heh) consumption.
|
||||
val = reflect.ValueOf(rows)
|
||||
if val.IsNil() || val.Type().Kind() != reflect.Slice || val.Len() == 0 {
|
||||
return
|
||||
}
|
||||
rowVals = make([]reflect.Value, val.Len())
|
||||
for idx := 0; idx < val.Len(); idx++ {
|
||||
rowVals[idx] = val.Index(idx)
|
||||
}
|
||||
|
||||
// I *think* this could potentially cause a panic if rows[0] is nil, BUT this func can't be used externally so it's Fine(TM).
|
||||
// Usage of it is tightly scoped.
|
||||
valType = reflect.Indirect(rowVals[0]).Type()
|
||||
if valType.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
numFields = reflect.Indirect(rowVals[0]).NumField()
|
||||
colTitles = make([]string, 0, numFields)
|
||||
colSizes = make([]uint8, 0, numFields)
|
||||
colFieldIdx = make([]int, 0, numFields)
|
||||
|
||||
// Populate from the struct type first.
|
||||
for i := 0; i < numFields; 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
|
||||
title = field.Tag.Get("renderTitle")
|
||||
if title == "" {
|
||||
title = 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.
|
||||
size = len(title) + (fixedPad * len(padChars))
|
||||
colTitles = append(colTitles, title)
|
||||
colSizes = append(colSizes, uint8(size))
|
||||
colFieldIdx = append(colFieldIdx, i)
|
||||
}
|
||||
// colTitles is done.
|
||||
|
||||
// check each field in each row to see if its value's size is greater than the current.
|
||||
for _, row := range rowVals {
|
||||
val = reflect.Indirect(row)
|
||||
if val.Kind() != reflect.Struct {
|
||||
continue
|
||||
}
|
||||
for colIdx, fieldIdx = range colFieldIdx {
|
||||
fieldVal = val.Field(fieldIdx)
|
||||
// The row struct types only implement these primitives.
|
||||
switch fieldVal.Kind() {
|
||||
case reflect.String:
|
||||
valStr = fieldVal.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
valStr = strconv.FormatInt(fieldVal.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
valStr = strconv.FormatUint(fieldVal.Uint(), 10)
|
||||
case reflect.Ptr:
|
||||
// *big.Int. No need to/don't fieldVal.Indirect(); .String() is a ptr method.
|
||||
// Sidenote, I didn't even know until today that reflection can *do* this.
|
||||
fieldVal = fieldVal.MethodByName("String").Call(nil)[0]
|
||||
callVal = fieldVal.String()
|
||||
sb.WriteString(padStr(callVal, colLen))
|
||||
valStr = fieldVal.String()
|
||||
}
|
||||
valLen = len(valStr) + (fixedPad * len(padChars))
|
||||
if valLen > int(colSizes[colIdx]) {
|
||||
colSizes[colIdx] = uint8(valLen)
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
|
||||
out = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -67,7 +67,36 @@ func main() {
|
||||
}
|
||||
|
||||
switch parser.Active.Name {
|
||||
case "net":
|
||||
if origPfx, err = netip.ParsePrefix(args.ExplicitNetwork.Network.Network); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
pfx = netipx.PrefixIPNet(origPfx.Masked())
|
||||
cmnArgs = common{
|
||||
outputOpts: outputOpts{
|
||||
SuppressRemaining: true,
|
||||
Plain: args.ExplicitNetwork.Plain,
|
||||
Verbose: args.ExplicitNetwork.Verbose,
|
||||
Seperator: args.ExplicitNetwork.Seperator,
|
||||
Fmt: args.ExplicitNetwork.Fmt,
|
||||
},
|
||||
AllowReserved: true,
|
||||
AllowHostNet: true,
|
||||
Network: args.ExplicitNetwork.Network,
|
||||
}
|
||||
nets = make([]*netip.Prefix, 1)
|
||||
nets[0] = new(netip.Prefix)
|
||||
*nets[0] = origPfx.Masked()
|
||||
if err = printNets(&origPfx, pfx, nets, nil, &cmnArgs, nil); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
return
|
||||
case "table":
|
||||
// Account for a weird redundant CLI condition.
|
||||
if args.Table.tableOpts.NoIpv4 && args.Table.tableOpts.NoIpv6 {
|
||||
args.Table.tableOpts.NoIpv6 = false
|
||||
args.Table.tableOpts.NoIpv4 = false
|
||||
}
|
||||
buf = new(bytes.Buffer)
|
||||
if err = tblTpl.ExecuteTemplate(buf, "table.tpl", args.Table.tableOpts); err != nil {
|
||||
log.Panicln(err)
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
type subnetResult netip.Prefix
|
||||
|
||||
type tableOpts struct {
|
||||
Notes bool `short:"n" long:"notes" description:"Include notes about prefixes (as a separate table)."`
|
||||
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."`
|
||||
@ -17,56 +18,6 @@ type tableOpts struct {
|
||||
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
|
||||
@ -78,12 +29,31 @@ type tableLegacy4 struct {
|
||||
NetCIDR netip.Prefix `render:"-"`
|
||||
}
|
||||
|
||||
// tableLegacy4Sizer is used to size tableLegacy4 entries.
|
||||
type tableLegacy4Sizer struct {
|
||||
Class uint8
|
||||
CIDR uint8
|
||||
Start uint8
|
||||
End uint8
|
||||
// tableMask4 is used to hold string representation of netmask information.
|
||||
type tableMask4 struct {
|
||||
Prefix string
|
||||
Netmask string
|
||||
Hex string
|
||||
Dec uint32
|
||||
Bin string
|
||||
Mask net.IPMask `render:"-"`
|
||||
NetPrefix netip.Prefix `render:"-"`
|
||||
}
|
||||
|
||||
// tablePrefixNote is used to hold notes about select prefix sizes.
|
||||
type tablePrefixNote struct {
|
||||
Prefix string
|
||||
Note string
|
||||
NetPrefix netip.Prefix `render:"-"`
|
||||
}
|
||||
|
||||
// tablePrefix is used to hold string representation of network prefixes.
|
||||
type tablePrefix struct {
|
||||
Prefix string
|
||||
Bits uint8
|
||||
Addresses *big.Int
|
||||
Hosts *big.Int
|
||||
NetPrefix netip.Prefix `render:"-"`
|
||||
}
|
||||
|
||||
// tableFormatter is used for "rendering" table output.
|
||||
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ toolchain go1.23.5
|
||||
|
||||
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/goccy/go-yaml v1.15.16
|
||||
github.com/jessevdk/go-flags v1.6.1
|
||||
|
12
netsplit/conts.go
Normal file
12
netsplit/conts.go
Normal file
@ -0,0 +1,12 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
`net/netip`
|
||||
)
|
||||
|
||||
var (
|
||||
ReservedNets map[netip.Prefix]string
|
||||
reservedNetsOrig map[string]string = map[string]string{
|
||||
"": "",
|
||||
}
|
||||
)
|
@ -1,3 +1,20 @@
|
||||
package netsplit
|
||||
|
||||
// TODO?
|
||||
import (
|
||||
`log`
|
||||
`net/netip`
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
var pfx netip.Prefix
|
||||
|
||||
ReservedNets = make(map[netip.Prefix]string)
|
||||
|
||||
for np, reason := range reservedNetsOrig {
|
||||
if pfx, err = netip.ParsePrefix(np); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
ReservedNets[pfx] = reason
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user