almost done ackshually
This commit is contained in:
12
netsplit/errs.go
Normal file
12
netsplit/errs.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package netsplit
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrBadBoundary error = errors.New("subnet does not align on bit boundary")
|
||||
ErrBadPrefix error = errors.New("prefix is invalid")
|
||||
ErrBadPrefixLen error = errors.New("prefix length exceeds maximum possible for prefix's inet family")
|
||||
ErrBadSplitter error = errors.New("invalid or unknown splitter when containing")
|
||||
ErrBigPrefix error = errors.New("prefix length exceeds remaining network space")
|
||||
ErrNoNetSpace error = errors.New("reached end of network space before splitting finished")
|
||||
)
|
||||
356
netsplit/funcs.go
Normal file
356
netsplit/funcs.go
Normal file
@@ -0,0 +1,356 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/goccy/go-yaml"
|
||||
"go4.org/netipx"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
AddrExpand expands a netip.Addr's string format.
|
||||
Like netip.Addr.StringExpanded() but for IPv4 too.
|
||||
*/
|
||||
func AddrExpand(ip netip.Addr) (s string) {
|
||||
|
||||
var sb *strings.Builder
|
||||
|
||||
if ip.IsUnspecified() || !ip.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
if ip.Is6() {
|
||||
s = ip.StringExpanded()
|
||||
} else {
|
||||
// IPv4 we have to do by hand.
|
||||
sb = new(strings.Builder)
|
||||
for idx, b := range ip.AsSlice() {
|
||||
sb.WriteString(fmt.Sprintf("%03d", b))
|
||||
if idx != net.IPv4len-1 {
|
||||
sb.WriteString(".")
|
||||
}
|
||||
}
|
||||
s = sb.String()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
AddrCompress returns the shortest possible CIDR representation as a string from a netip.Prefix.
|
||||
Note that IPv6 netip.Prefix.String() already does this automatically, as IPv6 has special condensing rules.
|
||||
*/
|
||||
func AddrCompress(pfx *netip.Prefix) (s string) {
|
||||
|
||||
var sl []string
|
||||
var lastNonzero int
|
||||
|
||||
if pfx == nil || !pfx.IsValid() || !pfx.Addr().IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
if pfx.Addr().Is6() {
|
||||
s = pfx.String()
|
||||
return
|
||||
}
|
||||
|
||||
sl = strings.Split(pfx.Addr().String(), ".")
|
||||
|
||||
for idx, oct := range sl {
|
||||
if oct != "0" {
|
||||
lastNonzero = idx
|
||||
}
|
||||
}
|
||||
|
||||
s = fmt.Sprintf("%s/%d", strings.Join(sl[:lastNonzero+1], "."), pfx.Bits())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
AddrFmt provides a string representation for an IP (as a netip.Addr).
|
||||
|
||||
`f` is the string formatter to use (without the %). For IPv4, you generally want `d`,
|
||||
for IPv6, you generally want `x`.
|
||||
|
||||
`sep` indicates a character to insert every `every` bytes of the mask.
|
||||
For IPv4, you probably want `.`,
|
||||
for IPv6 there isn't really a standard representation; CIDR notation is preferred.
|
||||
Thus for IPv6 you probably want to set sep as blank and/or set `every` to 0.
|
||||
|
||||
`segSep` indicates a character sequence to use for segmenting the string.
|
||||
Specify as an empty string and/or set `everySeg` to 0 to disable.
|
||||
|
||||
`every` indicates how many bytes should pass before sep is inserted.
|
||||
For IPv4, this should be 1.
|
||||
For IPv6, there isn't really a standard indication but it's recommended to do 2.
|
||||
Set as 0 or `sep` to an empty string to do no separation characters.
|
||||
|
||||
`everySeg` indicates how many *seperations* should pass before segSep is inserted.
|
||||
Set as 0 or `segSep` to an empty string to do no string segmentation.
|
||||
*/
|
||||
func AddrFmt(ip netip.Addr, f, sep, segSep string, every, everySeg uint) (s string) {
|
||||
|
||||
var numSegs int
|
||||
var doSep bool = every > 0
|
||||
var fs string = "%" + f
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
if ip.IsUnspecified() || !ip.IsValid() {
|
||||
return
|
||||
}
|
||||
for idx, b := range ip.AsSlice() {
|
||||
if doSep && idx > 0 {
|
||||
if idx%int(every) == 0 {
|
||||
sb.WriteString(sep)
|
||||
numSegs++
|
||||
}
|
||||
if everySeg > 0 {
|
||||
if numSegs >= int(everySeg) {
|
||||
sb.WriteString(segSep)
|
||||
numSegs = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(sb, fs, b)
|
||||
}
|
||||
|
||||
s = strings.TrimSpace(sb.String())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
AddrInvert returns an inverted form of netip.Addr as another netip.Addr.
|
||||
|
||||
Note that it doesn't really make sense to use this for IPv6.
|
||||
*/
|
||||
func AddrInvert(ip netip.Addr) (inverted netip.Addr) {
|
||||
|
||||
var b []byte
|
||||
|
||||
if !ip.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
b = make([]byte, len([]byte(ip.AsSlice())))
|
||||
|
||||
for idx, i := range []byte(ip.AsSlice()) {
|
||||
b[idx] = ^i
|
||||
}
|
||||
|
||||
inverted, _ = netip.AddrFromSlice(b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Contain takes the results of a NetSplitter and returns a StructuredResults.
|
||||
func Contain(origPfx *netip.Prefix, nets []*netip.Prefix, remaining *netipx.IPSet, splitter NetSplitter) (s *StructuredResults, err error) {
|
||||
|
||||
var rem []netip.Prefix
|
||||
var sr StructuredResults = StructuredResults{
|
||||
Original: origPfx,
|
||||
}
|
||||
|
||||
if origPfx == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if origPfx.Addr() != origPfx.Masked().Addr() {
|
||||
sr.Canonical = new(netip.Prefix)
|
||||
*sr.Canonical = origPfx.Masked()
|
||||
sr.HostAddr = new(netip.Addr)
|
||||
*sr.HostAddr = origPfx.Addr()
|
||||
}
|
||||
|
||||
if splitter != nil {
|
||||
sr.Splitter = new(SplitOpts)
|
||||
switch t := splitter.(type) {
|
||||
case *CIDRSplitter:
|
||||
sr.Splitter.CIDR = t
|
||||
case *HostSplitter:
|
||||
sr.Splitter.Host = t
|
||||
case *SubnetSplitter:
|
||||
sr.Splitter.Subnet = t
|
||||
case *VLSMSplitter:
|
||||
sr.Splitter.VLSM = t
|
||||
default:
|
||||
err = ErrBadSplitter
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if nets != nil {
|
||||
sr.Allocated = make([]*ContainedResult, len(nets))
|
||||
for idx, n := range nets {
|
||||
sr.Allocated[idx] = &ContainedResult{
|
||||
Network: n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if remaining != nil {
|
||||
rem = remaining.Prefixes()
|
||||
sr.Unallocated = make([]*ContainedResult, len(rem))
|
||||
for idx, i := range rem {
|
||||
sr.Unallocated[idx] = &ContainedResult{
|
||||
Network: new(netip.Prefix),
|
||||
}
|
||||
*sr.Unallocated[idx].Network = i
|
||||
}
|
||||
}
|
||||
|
||||
s = &sr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
MaskExpand expands a net.IPMask's string format.
|
||||
Like AddrExpand but for netmasks.
|
||||
*/
|
||||
func MaskExpand(mask net.IPMask, isIpv6 bool) (s string) {
|
||||
|
||||
var sb *strings.Builder
|
||||
|
||||
// IPv6 is always expanded in string format, but not split out.
|
||||
if isIpv6 {
|
||||
s = MaskFmt(mask, "02x", ":", "", 2, 0)
|
||||
return
|
||||
}
|
||||
|
||||
sb = new(strings.Builder)
|
||||
for idx, b := range mask {
|
||||
sb.WriteString(fmt.Sprintf("%03d", b))
|
||||
if idx != net.IPv4len-1 {
|
||||
sb.WriteString(".")
|
||||
}
|
||||
}
|
||||
s = sb.String()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
MaskFmt provides a string representation for a netmask (as a net.IPMask).
|
||||
|
||||
Its parameters hold the same significance as in AddrFmt.
|
||||
*/
|
||||
func MaskFmt(mask net.IPMask, f, sep, segSep string, every, everySeg uint) (s string) {
|
||||
|
||||
var numSegs int
|
||||
var doSep bool = every > 0
|
||||
var fs string = "%" + f
|
||||
var sb *strings.Builder = new(strings.Builder)
|
||||
|
||||
if mask == nil || len(mask) == 0 {
|
||||
return
|
||||
}
|
||||
for idx, b := range mask {
|
||||
if doSep && idx > 0 {
|
||||
if idx%int(every) == 0 {
|
||||
sb.WriteString(sep)
|
||||
numSegs++
|
||||
}
|
||||
if everySeg > 0 {
|
||||
if numSegs >= int(everySeg) {
|
||||
sb.WriteString(segSep)
|
||||
numSegs = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(sb, fs, b)
|
||||
}
|
||||
|
||||
s = strings.TrimSpace(sb.String())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
MaskInvert returns an inverted form of net.IPMask as another net.IPMask.
|
||||
|
||||
Note that it doesn't really make sense to use this for IPv6.
|
||||
*/
|
||||
func MaskInvert(mask net.IPMask) (inverted net.IPMask) {
|
||||
|
||||
var b []byte
|
||||
|
||||
b = make([]byte, len([]byte(mask)))
|
||||
|
||||
for idx, i := range []byte(mask) {
|
||||
b[idx] = ^i
|
||||
}
|
||||
|
||||
inverted = net.IPMask(b)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Parse parses b for JSON/XML/YAML and tries to return a StructuredResults from it.
|
||||
func Parse(b []byte) (s *StructuredResults, err error) {
|
||||
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(b, &s); err != nil {
|
||||
if err = xml.Unmarshal(b, &s); err != nil {
|
||||
if err = yaml.Unmarshal(b, &s); err != nil {
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ValidateSizes ensures that none of the prefix lengths in sizes exceeds the maximum possible in pfx.
|
||||
No-ops with nil error if pfx is nil, sizes is nil, or sizes is empty.
|
||||
|
||||
err is also nil if validation succeeds.
|
||||
If validation fails on a prefix length size, the error will be a SplitErr
|
||||
with only Wrapped and RequestedPrefixLen fields populated *for the first failing size only*.
|
||||
*/
|
||||
func ValidateSizes(pfx *net.IPNet, sizes ...uint8) (err error) {
|
||||
|
||||
var ok bool
|
||||
var addr netip.Addr
|
||||
var familyMax uint8
|
||||
|
||||
if pfx == nil || sizes == nil || len(sizes) == 0 {
|
||||
return
|
||||
}
|
||||
if addr, ok = netipx.FromStdIP(pfx.IP); !ok {
|
||||
err = ErrBadPrefix
|
||||
return
|
||||
}
|
||||
if addr.Is4() {
|
||||
familyMax = 32
|
||||
} else {
|
||||
familyMax = 128
|
||||
}
|
||||
for _, size := range sizes {
|
||||
if size > familyMax {
|
||||
err = &SplitErr{
|
||||
Wrapped: ErrBadPrefixLen,
|
||||
Nets: nil,
|
||||
Remaining: nil,
|
||||
LastSubnet: nil,
|
||||
RequestedPrefixLen: size,
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
51
netsplit/funcs_basesplitter.go
Normal file
51
netsplit/funcs_basesplitter.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// SetParent sets the net.IPNet for a Splitter.
|
||||
func (b *BaseSplitter) SetParent(pfx net.IPNet) {
|
||||
|
||||
b.network = &pfx
|
||||
|
||||
}
|
||||
|
||||
// MarshalText lets a BaseSplitter conform to an encoding.TextMarshaler.
|
||||
func (b *BaseSplitter) MarshalText() (text []byte, err error) {
|
||||
|
||||
if b == nil || b.network == nil {
|
||||
return
|
||||
}
|
||||
|
||||
text = []byte(b.network.String())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
UnmarshalText lets a BaseSplitter conform to an encoding.TextUnmarshaler.
|
||||
|
||||
This is a potentially lossy operation! Any host bits set in the prefix's address will be lost.
|
||||
They will not be set if the output was originally generated by `subnetter`.
|
||||
*/
|
||||
func (b *BaseSplitter) UnmarshalText(text []byte) (err error) {
|
||||
|
||||
var s string
|
||||
var n *net.IPNet
|
||||
|
||||
if text == nil {
|
||||
return
|
||||
}
|
||||
s = string(text)
|
||||
|
||||
if _, n, err = net.ParseCIDR(s); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
*b = BaseSplitter{
|
||||
network: n,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
14
netsplit/funcs_cidrsplitter.go
Normal file
14
netsplit/funcs_cidrsplitter.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
"go4.org/netipx"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// Split splits the network defined in a CIDRSplitter alongside its configuration and performs the subnetting.
|
||||
func (c *CIDRSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, err error) {
|
||||
|
||||
// TODO
|
||||
|
||||
return
|
||||
}
|
||||
14
netsplit/funcs_hostsplitter.go
Normal file
14
netsplit/funcs_hostsplitter.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
"go4.org/netipx"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// Split splits the network defined in a HostSplitter alongside its configuration and performs the subnetting.
|
||||
func (h *HostSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, err error) {
|
||||
|
||||
// TODO
|
||||
|
||||
return
|
||||
}
|
||||
14
netsplit/funcs_spliterr.go
Normal file
14
netsplit/funcs_spliterr.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package netsplit
|
||||
|
||||
// Error makes a SplitErr conform to error.
|
||||
func (s *SplitErr) Error() (errStr string) {
|
||||
|
||||
if s == nil {
|
||||
errStr = "(error unknown; nil error)"
|
||||
return
|
||||
}
|
||||
|
||||
errStr = s.Wrapped.Error()
|
||||
|
||||
return
|
||||
}
|
||||
73
netsplit/funcs_structuredresults.go
Normal file
73
netsplit/funcs_structuredresults.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
"go4.org/netipx"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
/*
|
||||
GetSplitter returns the first (should be *only*) non-nill NetSplitter on a StructuredResults.
|
||||
|
||||
If none is found, splitter will be nil but no panic/error will occur.
|
||||
*/
|
||||
func (s *StructuredResults) GetSplitter() (splitter NetSplitter) {
|
||||
|
||||
if s == nil || s.Splitter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
TODO(?): It'd be nice if I could just reflect .Interface() this
|
||||
to a NetSplitter but I think I'd then have to typeswitch
|
||||
into the real type regardless, which is lame.
|
||||
*/
|
||||
|
||||
if s.Splitter.CIDR != nil {
|
||||
splitter = s.Splitter.CIDR
|
||||
} else if s.Splitter.Host != nil {
|
||||
splitter = s.Splitter.Host
|
||||
} else if s.Splitter.Subnet != nil {
|
||||
splitter = s.Splitter.Subnet
|
||||
} else if s.Splitter.VLSM != nil {
|
||||
splitter = s.Splitter.VLSM
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Uncontain returns a set of values that "unstructure" a StructuredResults.
|
||||
|
||||
(Essentially the opposite procedure of Contain().)
|
||||
*/
|
||||
func (s *StructuredResults) Uncontain() (origPfx *netip.Prefix, nets []*netip.Prefix, remaining *netipx.IPSet, splitter NetSplitter, err error) {
|
||||
|
||||
var ipsb *netipx.IPSetBuilder
|
||||
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
origPfx = s.Original
|
||||
if s.Allocated != nil {
|
||||
nets = make([]*netip.Prefix, len(s.Allocated))
|
||||
for idx, i := range s.Allocated {
|
||||
nets[idx] = i.Network
|
||||
}
|
||||
}
|
||||
if s.Unallocated != nil {
|
||||
ipsb = new(netipx.IPSetBuilder)
|
||||
for _, i := range s.Unallocated {
|
||||
if i.Network != nil {
|
||||
ipsb.AddPrefix(*i.Network)
|
||||
}
|
||||
}
|
||||
if remaining, err = ipsb.IPSet(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
splitter = s.GetSplitter()
|
||||
|
||||
return
|
||||
}
|
||||
14
netsplit/funcs_subnetsplitter.go
Normal file
14
netsplit/funcs_subnetsplitter.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
"go4.org/netipx"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// Split splits the network defined in a SubnetSplitter alongside its configuration and performs the subnetting.
|
||||
func (s *SubnetSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, err error) {
|
||||
|
||||
// TODO
|
||||
|
||||
return
|
||||
}
|
||||
97
netsplit/funcs_vlsmsplitter.go
Normal file
97
netsplit/funcs_vlsmsplitter.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
"go4.org/netipx"
|
||||
"net/netip"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Split splits the network defined in a VLSMSplitter alongside its configuration and performs the subnetting.
|
||||
func (v *VLSMSplitter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, err error) {
|
||||
|
||||
var ok bool
|
||||
var pfxLen int
|
||||
var pfxLen8 uint8
|
||||
var base netip.Prefix
|
||||
var sub netip.Prefix
|
||||
var subPtr *netip.Prefix
|
||||
var ipsb *netipx.IPSetBuilder = new(netipx.IPSetBuilder)
|
||||
|
||||
if err = ValidateSizes(v.network, v.PrefixLengths...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
I thought about using the following:
|
||||
|
||||
* https://pkg.go.dev/net/netip
|
||||
* https://pkg.go.dev/github.com/sacloud/packages-go/cidr
|
||||
* https://pkg.go.dev/github.com/projectdiscovery/mapcidr
|
||||
* https://pkg.go.dev/github.com/EvilSuperstars/go-cidrman
|
||||
|
||||
But, as I expected, netipx ftw again.
|
||||
*/
|
||||
|
||||
if v == nil || v.PrefixLengths == nil || len(v.PrefixLengths) == 0 || v.BaseSplitter == nil || v.network == nil {
|
||||
return
|
||||
}
|
||||
|
||||
sort.SliceStable(
|
||||
v.PrefixLengths,
|
||||
func(i, j int) (isBefore bool) { // We use a reverse sorting by default so we get larger prefixes at the beginning.
|
||||
if v.Ascending {
|
||||
isBefore = v.PrefixLengths[i] > v.PrefixLengths[j]
|
||||
} else {
|
||||
isBefore = v.PrefixLengths[i] < v.PrefixLengths[j]
|
||||
}
|
||||
return
|
||||
},
|
||||
)
|
||||
|
||||
pfxLen, _ = v.network.Mask.Size()
|
||||
pfxLen8 = uint8(pfxLen)
|
||||
|
||||
if base, ok = netipx.FromStdIPNet(v.network); !ok {
|
||||
err = ErrBadBoundary
|
||||
return
|
||||
}
|
||||
if !base.IsValid() {
|
||||
err = ErrBadBoundary
|
||||
return
|
||||
}
|
||||
|
||||
ipsb.AddPrefix(base)
|
||||
if remaining, err = ipsb.IPSet(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, size := range v.PrefixLengths {
|
||||
if size < pfxLen8 {
|
||||
err = &SplitErr{
|
||||
Wrapped: ErrBigPrefix,
|
||||
Nets: nets,
|
||||
Remaining: remaining,
|
||||
LastSubnet: &sub,
|
||||
RequestedPrefixLen: size,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if sub, remaining, ok = remaining.RemoveFreePrefix(size); !ok {
|
||||
err = &SplitErr{
|
||||
Wrapped: ErrNoNetSpace,
|
||||
Nets: nets,
|
||||
Remaining: remaining,
|
||||
LastSubnet: &sub,
|
||||
RequestedPrefixLen: size,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
subPtr = new(netip.Prefix)
|
||||
*subPtr = sub
|
||||
nets = append(nets, subPtr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
112
netsplit/types.go
Normal file
112
netsplit/types.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package netsplit
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"go4.org/netipx"
|
||||
"net"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
// SplitErr is used to wrap an error with context surrounding when/how that error was encountered.
|
||||
type SplitErr struct {
|
||||
// Wrapped is the originating error during a split (or other parsing operation).
|
||||
Wrapped error
|
||||
// Nets are the subnets parsed out/collected so far.
|
||||
Nets []*netip.Prefix
|
||||
// Remaining is an IPSet of subnets/addresses that haven't been, or were unable to be, split out.
|
||||
Remaining *netipx.IPSet
|
||||
// LastSubnet is the most recently split out subnet.
|
||||
LastSubnet *netip.Prefix
|
||||
// RequestedPrefixLen is the network prefix length size, if relevant, that was attempted to be split out of Remaining.
|
||||
RequestedPrefixLen uint8
|
||||
}
|
||||
|
||||
// NetSplitter is used to split a network into multiple nets (and any remaining prefixes/addresses that didn't fit).
|
||||
type NetSplitter interface {
|
||||
SetParent(pfx net.IPNet)
|
||||
Split() (nets []*netip.Prefix, remaining *netipx.IPSet, err error)
|
||||
}
|
||||
|
||||
// BaseSplitter is used to encapsulate the "parent" network to be split.
|
||||
type BaseSplitter struct {
|
||||
network *net.IPNet
|
||||
}
|
||||
|
||||
/*
|
||||
CIDRSplitter is used to split a network based on a fixed prefix size.
|
||||
It attemps to split the network into as many networks of size PrefixLength as cleanly as possible.
|
||||
*/
|
||||
type CIDRSplitter struct {
|
||||
// PrefixLength specifies the CIDR/prefix length of the subnets to split out.
|
||||
PrefixLength uint8 `json:"prefix" xml:"prefix,attr" yaml:"network Prefix Length"`
|
||||
*BaseSplitter `json:"net" xml:"net,omitempty" yaml:"network,omitempty"`
|
||||
}
|
||||
|
||||
/*
|
||||
HostSplitter is used to split a network based on total number of hosts.
|
||||
It attempts to evenly distribute addresses amoungs subnets.
|
||||
*/
|
||||
type HostSplitter struct {
|
||||
// NumberHosts is the number of hosts to be placed in each subnet to split out.
|
||||
NumberHosts uint `json:"hosts" xml:"hosts,attr" yaml:"Number of Hosts Per Subnet"`
|
||||
*BaseSplitter `json:"net" xml:"net,omitempty" yaml:"network,omitempty"`
|
||||
}
|
||||
|
||||
/*
|
||||
SubnetSplitter is used to split a network into a specific number of subnets of equal prefix lengths
|
||||
as cleanly as poossible.
|
||||
*/
|
||||
type SubnetSplitter struct {
|
||||
// NumberSubnets indicates the number of subnets to split the network into.
|
||||
NumberSubnets uint `json:"nets" xml:"nets,attr" yaml:"Number of Target Subnets"`
|
||||
*BaseSplitter `json:"net" xml:"net,omitempty" yaml:"network,omitempty"`
|
||||
}
|
||||
|
||||
/*
|
||||
VLSMSplitter is used to split a network via VLSM (Variable-Length Subnet Masks) into multiple PrefixLengths,
|
||||
in which there are multiple desired subnets of varying lengths.
|
||||
*/
|
||||
type VLSMSplitter struct {
|
||||
/*
|
||||
Ascending, if true, will subnet smaller networks/larger prefixes near the beginning
|
||||
(ascending order) instead of larger networks/smaller prefixes (descending order).
|
||||
You almost assuredly do not want to do this.
|
||||
*/
|
||||
Ascending bool
|
||||
// PrefixLengths contains the prefix lengths of each subnet to split out from the network.
|
||||
PrefixLengths []uint8 `json:"prefixes" xml:"prefixes>prefix" yaml:"Prefix Lengths"`
|
||||
*BaseSplitter `json:"net" xml:"net,omitempty" yaml:"network,omitempty"`
|
||||
}
|
||||
|
||||
/*
|
||||
StructuredResults is used for serializing prefixes into a structured/defined data format.
|
||||
*/
|
||||
type StructuredResults struct {
|
||||
XMLName xml.Name `json:"-" xml:"results" yaml:"-"`
|
||||
// Original is the provided parent network/prefix.
|
||||
Original *netip.Prefix `json:"orig" xml:"orig,attr,omitempty" yaml:"Original/Parent network"`
|
||||
// HostAddr is nil if Original falls on a network prefix boundary, otherwise it is the specified host address.
|
||||
HostAddr *netip.Addr `json:"host" xml:"host,attr,omitempty" yaml:"Host Address,omitempty"`
|
||||
// Canonical is the canonical network of Original (e.g. with host bits masked out). It is nil if Original.Addr() falls on the (lower) boundary.
|
||||
Canonical *netip.Prefix `json:"masked" xml:"masked,attr,omitempty" yaml:"Bound Original/Parent network"`
|
||||
// Splitter contains the spplitter and its options used to split the network.
|
||||
Splitter *SplitOpts `json:"splitter" xml:"splitter,omitempty" yaml:"Splitter,omitempty"`
|
||||
// Allocated contains valid subnet(s) in Original per the user-specified subnetting rules.
|
||||
Allocated []*ContainedResult `json:"subnets" xml:"subnets>subnet,omitempty" yaml:"Subnets"`
|
||||
// Unallocated contains subnets from Original that did not meet the splitting criteria or were left over from the split operation.
|
||||
Unallocated []*ContainedResult `json:"remaining" xml:"remaining>subnet,omitempty" yaml:"Remaining/Unallocated/Left Over,omitempty"`
|
||||
}
|
||||
|
||||
type SplitOpts struct {
|
||||
XMLName xml.Name `json:"-" xml:"splitter" yaml:"-"`
|
||||
CIDR *CIDRSplitter `json:"cidr,omitempty" xml:"cidr,omitempty" yaml:"CIDR Splitter,omitempty"`
|
||||
Host *HostSplitter `json:"host,omitempty" xml:"host,omitempty" yaml:"Host Splitter,omitempty"`
|
||||
Subnet *SubnetSplitter `json:"subnet,omitempty" xml:"subnet,omitempty" yaml:"Subnet Splitter,omitempty"`
|
||||
VLSM *VLSMSplitter `json:"vlsm,omitempty" xml:"vlsm,omitempty" yaml:"VLSM Splitter,omitempty"`
|
||||
}
|
||||
|
||||
// ContainedResult is a single Network (either an allocated subnet or a remaining block).
|
||||
type ContainedResult struct {
|
||||
XMLName xml.Name `json:"-" yaml:"-" xml:"subnet"`
|
||||
Network *netip.Prefix `json:"net" xml:"net,attr,omitempty" yaml:"network,omitempty"`
|
||||
}
|
||||
Reference in New Issue
Block a user