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
|
:source-highlighter: rouge
|
||||||
:docinfo: shared
|
:docinfo: shared
|
||||||
:rfc: https://datatracker.ietf.org/doc/html/rfc
|
:rfc: https://datatracker.ietf.org/doc/html/rfc
|
||||||
|
:arin_r: https://www.arin.net/participate/policy/nrpm/
|
||||||
|
|
||||||
[id="wat"]
|
[id="wat"]
|
||||||
== What is it?
|
== What is it?
|
||||||
@ -23,6 +24,10 @@ A tool to assist in design of segregate/segment/split/subnet networks.
|
|||||||
[id="out"]
|
[id="out"]
|
||||||
== Output
|
== 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^]).
|
* `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 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^]).
|
** 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 := . -}}
|
{{- $opts := . -}}
|
||||||
{{- $numRows := 0 -}}
|
{{- $numRows := 0 -}}
|
||||||
{{- if not $opts.NoIpv4 }}
|
{{- if not $opts.NoIpv4 }}
|
||||||
IPv4:
|
{{- if $opts.Plain }}
|
||||||
{{- if $opts.Legacy -}}
|
IPV4:
|
||||||
{{- $legacyspec := legacy4 }}
|
{{- else }}
|
||||||
{{- $numRows = len $legacyspec.Rows }}
|
{{- bold "IPv4:"}}
|
||||||
|
{{- end }}
|
||||||
|
{{- if $opts.Legacy }}
|
||||||
|
{{- if $opts.Plain }}
|
||||||
LEGACY:
|
LEGACY:
|
||||||
{{ $legacyspec.Sizer.Hdr "" $opts.Plain }}
|
{{- else }}
|
||||||
{{- range $idx, $row := $legacyspec.Rows }}
|
{{ bold "Legacy:" }}
|
||||||
{{- $row.Row $legacyspec.Sizer "\t" $opts.Plain -}}
|
|
||||||
{{- $legacyspec.Sizer.Line "\t" $opts.Plain $idx $numRows }}
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{ legacy4 "\t" $opts.Plain }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if not $opts.NoV4Mask }}
|
{{- if not $opts.NoV4Mask }}
|
||||||
{{- $masks := mask4 }}
|
{{- if $opts.Plain }}
|
||||||
NETMASKS:
|
NETMASKS:
|
||||||
{{ $masks.Sizer.Hdr "\t" $opts.Plain }}
|
{{- else }}
|
||||||
{{- range $idx, $row := $masks.Rows }}
|
{{ bold "Netmasks:" }}
|
||||||
{{- $row.Row $masks.Sizer "\t" $opts.Plain }}
|
|
||||||
{{- $masks.Sizer.Line "\t" $opts.Plain $idx $numRows }}
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{ mask4 "\t" $opts.Plain }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
|
{{- if $opts.Plain }}
|
||||||
|
|
||||||
CIDR:
|
CIDR:
|
||||||
{{- $pfxs := addrs 4 }}
|
{{- else }}
|
||||||
{{- $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 }}
|
|
||||||
|
|
||||||
|
{{ 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 }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if not $opts.NoIpv6 }}
|
{{- if not $opts.NoIpv6 }}
|
||||||
|
|
||||||
IPv6:
|
{{- if $opts.Plain }}
|
||||||
|
IPV6:
|
||||||
CIDR:
|
{{- else }}
|
||||||
{{- $pfxs := addrs 6 }}
|
{{ bold "IPv6:"}}
|
||||||
{{- $numRows = len $pfxs.Rows }}
|
{{- end }}
|
||||||
{{- $pfxs.Sizer.Hdr "\t" $opts.Plain }}
|
{{- if $opts.Plain }}
|
||||||
{{- range $idx, $row := $pfxs.Rows }}
|
CIDR:
|
||||||
{{- $row.Row $pfxs.Sizer "\t" $opts.Plain }}
|
{{- else }}
|
||||||
{{- $pfxs.Sizer.Line "\t" $opts.Plain $idx $numRows }}
|
{{ bold "CIDR:" }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{ prefixes 6 "\t" $opts.Plain }}
|
||||||
|
|
||||||
|
|
||||||
|
{{- if $opts.Notes }}
|
||||||
|
{{- if $opts.Plain }}
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
{{- else }}
|
||||||
|
|
||||||
|
{{ bold "Notes:" }}
|
||||||
|
{{- end }}
|
||||||
|
{{ notes 6 "\t" $opts.Plain }}
|
||||||
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
type Args struct {
|
type Args struct {
|
||||||
Version bool `short:"v" long:"version" description:"Print the version and exit."`
|
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."`
|
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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
ExplicitNetwork XNetArgs `command:"net" alias:"n" description:"Print information about an explicit network address." validate:"omitempty"`
|
||||||
Table TableArgs `command:"table" alias:"t" alias:"tab" alias:"tbl" description:"Show prefix summaries (by default both IPv4 and IPv6)." 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 {
|
type outputOpts struct {
|
||||||
SuppressRemaining bool `short:"r" long:"no-remaining" description:"Don't show leftover/unallocated/remaining space.'"`
|
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)."`
|
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."`
|
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."`
|
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 {
|
type TableArgs struct {
|
||||||
tableOpts
|
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 {
|
type VLSMArgs struct {
|
||||||
@ -62,5 +70,5 @@ type VLSMArgs struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Net 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
|
tplDir embed.FS
|
||||||
tblTpl *template.Template = template.Must(template.New("").Funcs(
|
tblTpl *template.Template = template.Must(template.New("").Funcs(
|
||||||
template.FuncMap{
|
template.FuncMap{
|
||||||
"legacy4": tplClass4Iter,
|
"bold": tplFmtBold,
|
||||||
"addrs": tplAddrIter,
|
"legacy4": tplTableLegacy4,
|
||||||
"mask4": tplMaskIter4,
|
"mask4": tplTableMasks4,
|
||||||
|
"notes": tplTableNotes,
|
||||||
|
"prefixes": tplTablePrefixes,
|
||||||
},
|
},
|
||||||
).ParseFS(tplDir, "_tpl/*.tpl"))
|
).ParseFS(tplDir, "_tpl/*.tpl"))
|
||||||
)
|
)
|
||||||
@ -41,9 +43,19 @@ var (
|
|||||||
var (
|
var (
|
||||||
// Primarily output formatting stuff in this block.
|
// Primarily output formatting stuff in this block.
|
||||||
sectSepCnt = 48
|
sectSepCnt = 48
|
||||||
sectSep1 = strings.Repeat("=", sectSepCnt)
|
// sectFmts contains a lookup of map[<is plain>][]string{<level 1>, <level 2>, <level 3>}
|
||||||
sectSep2 = strings.Repeat("-", sectSepCnt)
|
sectFmts map[bool][]string = map[bool][]string{
|
||||||
sectSep3 = strings.Repeat(".", sectSepCnt)
|
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 contains a lookup of map[<is plain>]*tableFormatter.
|
||||||
tblFmts map[bool]*tableFormatter = map[bool]*tableFormatter{
|
tblFmts map[bool]*tableFormatter = map[bool]*tableFormatter{
|
||||||
// Plaintext/ASCII-only
|
// 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 invertedMask net.IPMask
|
||||||
var res *netsplit.StructuredResults
|
var res *netsplit.StructuredResults
|
||||||
var verb = -1
|
var verb = -1
|
||||||
|
var fmts []string
|
||||||
|
var sectSep1 string
|
||||||
|
var sectSep2 string
|
||||||
|
// var sectSep3 string
|
||||||
|
|
||||||
if orig == nil {
|
if orig == nil {
|
||||||
return
|
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 {
|
if args.outputOpts.Verbose != nil {
|
||||||
verb = 0
|
verb = 0
|
||||||
@ -311,7 +319,7 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, n := range nets {
|
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 {
|
if verb >= 1 {
|
||||||
@ -335,7 +343,7 @@ func printNets(orig *netip.Prefix, origNet *net.IPNet, nets []*netip.Prefix, rem
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, n := range remaining.Prefixes() {
|
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 {
|
if verb >= 1 {
|
||||||
|
@ -6,13 +6,17 @@ import (
|
|||||||
"strings"
|
"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 pfx netip.Prefix
|
||||||
var bullet string = "+"
|
var bullet string = "+"
|
||||||
var sb *strings.Builder = new(strings.Builder)
|
var sb *strings.Builder = new(strings.Builder)
|
||||||
var pre string = strings.Repeat(indentStr, indent)
|
var pre string = strings.Repeat(indentStr, indent)
|
||||||
var pre2 string = strings.Repeat(indentStr, indent+1)
|
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 {
|
if s == nil {
|
||||||
return
|
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`
|
`subnetter/netsplit`
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// renderHdr renders a header. Note that the first line does *not* include the indent, but subsequent ones do.
|
||||||
tplClass4Iter should only be called if legacy info is enabled.
|
func renderHdr(titles []string, colSizes []uint8, indent string, plain bool, sb *strings.Builder) {
|
||||||
It returns a tableLegacy4Sizer and a slice of tableLegacy4.
|
|
||||||
|
|
||||||
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.
|
// This whole thing feels dirty.
|
||||||
// It's like adding a microcontroller to a rock.
|
// It's like adding a microcontroller to a rock.
|
||||||
// But it works.
|
// 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 pfx *net.IPNet
|
||||||
|
var row tableLegacy4
|
||||||
|
var rows []tableLegacy4
|
||||||
|
var rowVal reflect.Value
|
||||||
var classNets []*netip.Prefix
|
var classNets []*netip.Prefix
|
||||||
var netRange netipx.IPRange
|
var netRange netipx.IPRange
|
||||||
|
var sb *strings.Builder = new(strings.Builder)
|
||||||
var v *netsplit.VLSMSplitter = &netsplit.VLSMSplitter{
|
var v *netsplit.VLSMSplitter = &netsplit.VLSMSplitter{
|
||||||
Ascending: false,
|
Ascending: false,
|
||||||
PrefixLengths: []uint8{
|
PrefixLengths: []uint8{
|
||||||
@ -48,53 +215,124 @@ func tplClass4Iter() (legacySpec *tableLegacy4Ret, err error) {
|
|||||||
if classNets, _, err = v.Split(); err != nil {
|
if classNets, _, err = v.Split(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
rows = make([]tableLegacy4, 5)
|
||||||
|
|
||||||
legacySpec = &tableLegacy4Ret{
|
for idx, cls = range []string{
|
||||||
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",
|
"A", "B", "C", "D", "E",
|
||||||
} {
|
} {
|
||||||
legacySpec.Rows[idx] = tableLegacy4{
|
rows[idx] = tableLegacy4{
|
||||||
Class: cls,
|
Class: cls,
|
||||||
CIDR: classNets[idx].String(),
|
CIDR: classNets[idx].String(),
|
||||||
NetCIDR: *classNets[idx],
|
NetCIDR: *classNets[idx],
|
||||||
}
|
}
|
||||||
netRange = netipx.RangeOfPrefix(legacySpec.Rows[idx].NetCIDR)
|
netRange = netipx.RangeOfPrefix(rows[idx].NetCIDR)
|
||||||
legacySpec.Rows[idx].NetStart = netRange.From()
|
rows[idx].NetStart = netRange.From()
|
||||||
legacySpec.Rows[idx].NetEnd = netRange.To()
|
rows[idx].NetEnd = netRange.To()
|
||||||
legacySpec.Rows[idx].Start = legacySpec.Rows[idx].NetStart.String()
|
rows[idx].Start = rows[idx].NetStart.String()
|
||||||
legacySpec.Rows[idx].End = legacySpec.Rows[idx].NetEnd.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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
tplAddrIter takes a 4 or 6 for inet family/version and returns a tableAddrSizer and
|
tplTableMasks4 renders a table of netmasks for IPv4.
|
||||||
slice of tableAddr.
|
IPv6 doesn't really use netmasks, instead relying almost entirely upon CIDR.
|
||||||
tableAddr is sorted from smallest prefix/largest network to largest prefix/smallest network.
|
|
||||||
|
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 dummyNet *net.IPNet
|
||||||
var l int
|
var dummyAddr netip.Addr
|
||||||
|
var rowVal reflect.Value
|
||||||
|
var sb *strings.Builder = new(strings.Builder)
|
||||||
|
|
||||||
addrs = &tableAddrRet{
|
if dummyAddr, err = netip.ParseAddr("0.0.0.0"); err != nil {
|
||||||
Sizer: &tableAddrSizer{
|
return
|
||||||
Prefix: 6, // "PREFIX"
|
|
||||||
Bits: 4, // "BITS"
|
|
||||||
Addresses: 9, // "ADDRESSES"
|
|
||||||
Hosts: 5, // "HOSTS"
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
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 {
|
switch ipVer {
|
||||||
case 4:
|
case 4:
|
||||||
@ -109,339 +347,185 @@ func tplAddrIter(ipVer uint8) (addrs *tableAddrRet, err error) {
|
|||||||
err = errBadNet
|
err = errBadNet
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
rows = make([]tablePrefixNote, 0, dummyAddr.BitLen()+1)
|
||||||
|
|
||||||
// Before we size, we generate the tableAddrs.
|
for idx = 0; idx <= dummyAddr.BitLen(); idx++ {
|
||||||
addrs.Rows = make([]tableAddr, dummyAddr.BitLen()+1)
|
if note, ok = netNotes[ipVer][uint8(idx)]; !ok {
|
||||||
for i := 0; i <= dummyAddr.BitLen(); i++ {
|
continue
|
||||||
addrs.Rows[i] = tableAddr{
|
|
||||||
Prefix: uint8(i),
|
|
||||||
Bits: uint8(dummyAddr.BitLen() - i),
|
|
||||||
}
|
}
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
dummyNet = netipx.PrefixIPNet(addrs.Rows[i].NetPrefix.Masked())
|
rows = append(rows, row)
|
||||||
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.
|
colFields, colTitles, colSizes = sizeStructs(rows)
|
||||||
for _, addr := range addrs.Rows {
|
|
||||||
// I *abhor* walrus operators in anything but loops.
|
// Header
|
||||||
l = len(strconv.Itoa(int(addr.Prefix)))
|
renderHdr(colTitles, colSizes, indent, plain, sb)
|
||||||
if int(addrs.Sizer.Prefix) < l {
|
|
||||||
addrs.Sizer.Prefix = uint8(l)
|
// Rows
|
||||||
}
|
for idx, row = range rows {
|
||||||
l = len(strconv.Itoa(int(addr.Bits)))
|
rowVal = reflect.ValueOf(row)
|
||||||
if int(addrs.Sizer.Bits) < l {
|
isLast = idx == len(rows)-1
|
||||||
addrs.Sizer.Bits = uint8(l)
|
renderRow(rowVal, colFields, colSizes, indent, plain, isLast, sb)
|
||||||
}
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out = sb.String()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
tplMaskIter4 returns a slice of IPv4 netmasks and returns a slice of tableMask4.
|
tplTablePrefixes renders a table of prefixes for IP version/inet family ipVer (4 or 6).
|
||||||
Sorted from smallest prefix/largest network to largest prefix/smallest network.
|
|
||||||
|
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 idx int
|
||||||
var pfx netip.Prefix
|
var isLast bool
|
||||||
|
var colFields []int
|
||||||
|
var colSizes []uint8
|
||||||
|
var colTitles []string
|
||||||
|
var row tablePrefix
|
||||||
|
var rows []tablePrefix
|
||||||
var dummyNet *net.IPNet
|
var dummyNet *net.IPNet
|
||||||
var l int
|
var dummyAddr netip.Addr
|
||||||
|
var rowVal reflect.Value
|
||||||
|
var sb *strings.Builder = new(strings.Builder)
|
||||||
|
|
||||||
masks = &tableMask4Ret{
|
switch ipVer {
|
||||||
Sizer: &tableMask4Sizer{
|
case 4:
|
||||||
Prefix: 6, // "PREFIX"
|
if dummyAddr, err = netip.ParseAddr("0.0.0.0"); err != nil {
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
dummyNet = netipx.PrefixIPNet(pfx.Masked())
|
case 6:
|
||||||
masks.Rows[i] = tableMask4{
|
if dummyAddr, err = netip.ParseAddr("::"); err != nil {
|
||||||
Prefix: uint8(i),
|
return
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
default:
|
||||||
|
err = errBadNet
|
||||||
// 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
|
return
|
||||||
}
|
}
|
||||||
|
rows = make([]tablePrefix, dummyAddr.BitLen()+1)
|
||||||
|
|
||||||
val = hdrVal
|
for idx = 0; idx <= dummyAddr.BitLen(); idx++ {
|
||||||
valType = val.Type()
|
rows[idx] = tablePrefix{
|
||||||
lastField = valType.NumField() - 1
|
Prefix: fmt.Sprintf("/%d", idx),
|
||||||
|
Bits: uint8(dummyAddr.BitLen() - idx),
|
||||||
for i := val.NumField(); i >= 0; i-- {
|
|
||||||
field = valType.Field(i - 1)
|
|
||||||
if field.Tag.Get("render") == "-" {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
lastField = i
|
if rows[idx].NetPrefix, err = dummyAddr.Prefix(idx); err != nil {
|
||||||
break
|
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)
|
colFields, colTitles, colSizes = sizeStructs(rows)
|
||||||
if isLastLine {
|
|
||||||
sb.WriteString(tfmt.LastLeft)
|
// Header
|
||||||
} else {
|
renderHdr(colTitles, colSizes, indent, plain, sb)
|
||||||
sb.WriteString(tfmt.LineLeft)
|
|
||||||
|
// 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()
|
out = sb.String()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not include in template funcs; used externally
|
// rows should be a slice of structs/struct ptrs of all the same type (you're going to get errors/panics otherwise).
|
||||||
func rowRender(val reflect.Value, sizerVal reflect.Value, indent string, plain bool) (out string) {
|
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 field reflect.StructField
|
||||||
var fieldVal reflect.Value
|
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)
|
// Here be dragons. This is *NOT SAFE* for generic (heh) consumption.
|
||||||
for i := 0; i < val.NumField(); i++ {
|
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)
|
field = valType.Field(i)
|
||||||
if field.Tag.Get("render") == "-" {
|
if field.Tag.Get("render") == "-" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sb.WriteString(tfmt.Left)
|
title = field.Tag.Get("renderTitle")
|
||||||
fieldVal = val.Field(i)
|
if title == "" {
|
||||||
sizerName = field.Tag.Get("renderSizeName")
|
title = field.Name
|
||||||
if sizerName == "" {
|
|
||||||
sizerName = field.Name
|
|
||||||
}
|
}
|
||||||
sizerField = sizerVal.FieldByName(sizerName)
|
size = len(title) + (fixedPad * len(padChars))
|
||||||
colLen = uint8(sizerField.Uint()) + uint8(fixedPad)
|
colTitles = append(colTitles, title)
|
||||||
switch fieldVal.Kind() {
|
colSizes = append(colSizes, uint8(size))
|
||||||
// This is tailored specifically to this implementation.
|
colFieldIdx = append(colFieldIdx, i)
|
||||||
case reflect.String:
|
}
|
||||||
sb.WriteString(fieldVal.String())
|
// colTitles is done.
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
sb.WriteString(padStr(fmt.Sprintf("%d", fieldVal.Int()), colLen))
|
// check each field in each row to see if its value's size is greater than the current.
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
for _, row := range rowVals {
|
||||||
sb.WriteString(padStr(fmt.Sprintf("%d", fieldVal.Uint()), colLen))
|
val = reflect.Indirect(row)
|
||||||
case reflect.Ptr:
|
if val.Kind() != reflect.Struct {
|
||||||
// It's a *big.Int.
|
continue
|
||||||
if fieldVal.IsNil() {
|
}
|
||||||
sb.WriteString(padStr(strings.Repeat(padChars, int(colLen)), colLen))
|
for colIdx, fieldIdx = range colFieldIdx {
|
||||||
} else {
|
fieldVal = val.Field(fieldIdx)
|
||||||
// TIL you can even *do* this in reflection.
|
// 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]
|
fieldVal = fieldVal.MethodByName("String").Call(nil)[0]
|
||||||
callVal = fieldVal.String()
|
valStr = fieldVal.String()
|
||||||
sb.WriteString(padStr(callVal, colLen))
|
}
|
||||||
|
valLen = len(valStr) + (fixedPad * len(padChars))
|
||||||
|
if valLen > int(colSizes[colIdx]) {
|
||||||
|
colSizes[colIdx] = uint8(valLen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteString("\n")
|
|
||||||
|
|
||||||
out = sb.String()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,36 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch parser.Active.Name {
|
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":
|
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)
|
buf = new(bytes.Buffer)
|
||||||
if err = tblTpl.ExecuteTemplate(buf, "table.tpl", args.Table.tableOpts); err != nil {
|
if err = tblTpl.ExecuteTemplate(buf, "table.tpl", args.Table.tableOpts); err != nil {
|
||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
type subnetResult netip.Prefix
|
type subnetResult netip.Prefix
|
||||||
|
|
||||||
type tableOpts struct {
|
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."`
|
Plain bool `short:"p" long:"plain" description:"Show plain table output."`
|
||||||
Legacy bool `short:"l" long:"legacy" description:"Include legacy/obsolete/deprecated information."`
|
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."`
|
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)."`
|
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.
|
// tableLegacy4 contains a spec for a class in the legacy "classed" IPv4 networking.
|
||||||
type tableLegacy4 struct {
|
type tableLegacy4 struct {
|
||||||
Class string
|
Class string
|
||||||
@ -78,12 +29,31 @@ type tableLegacy4 struct {
|
|||||||
NetCIDR netip.Prefix `render:"-"`
|
NetCIDR netip.Prefix `render:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// tableLegacy4Sizer is used to size tableLegacy4 entries.
|
// tableMask4 is used to hold string representation of netmask information.
|
||||||
type tableLegacy4Sizer struct {
|
type tableMask4 struct {
|
||||||
Class uint8
|
Prefix string
|
||||||
CIDR uint8
|
Netmask string
|
||||||
Start uint8
|
Hex string
|
||||||
End uint8
|
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.
|
// tableFormatter is used for "rendering" table output.
|
||||||
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ toolchain go1.23.5
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/TwiN/go-color v1.4.1
|
github.com/TwiN/go-color v1.4.1
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/go-playground/validator/v10 v10.24.0
|
github.com/go-playground/validator/v10 v10.24.0
|
||||||
github.com/goccy/go-yaml v1.15.16
|
github.com/goccy/go-yaml v1.15.16
|
||||||
github.com/jessevdk/go-flags v1.6.1
|
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
|
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