go_subnetter/netsplit/funcs_prefixgetter.go
brent saner 860ad5842b
v0.2.5
ADDED:
* num-addrs subcommand, to get the number of hosts/addresses in a given
  prefix length
* get-net subcommand, to more easily get a single subnet from either the
  beginning or the end of a prefix. (MUCH FASTER than CIDR-splitting!)
2025-04-13 18:25:32 -04:00

103 lines
2.3 KiB
Go

package netsplit
import (
`fmt`
`net/netip`
`go4.org/netipx`
)
// Split is to conform to a NetSplitter, though a PrefixGetter is *technically* not a splitter.
func (p *PrefixGetter) Split() (nets []*netip.Prefix, remaining *netipx.IPSet, err error) {
var ok bool
var base netip.Prefix
var vlsmS *VLSMSplitter
var addr netip.Addr
var maxPfxLen uint8 = maxBitsv4
var ipsb *netipx.IPSetBuilder = new(netipx.IPSetBuilder)
if p == nil || p.PrefixLength == 0 || p.BaseSplitter == nil || p.network == nil {
return
}
if err = validate.Struct(p); err != nil {
return
}
// If the position is "first", we can simply call a VLSM.
if p.Pos == "first" {
vlsmS = &VLSMSplitter{
Ascending: false,
Explicit: false,
PrefixLengths: []uint8{p.PrefixLength},
BaseSplitter: p.BaseSplitter,
}
if nets, remaining, err = vlsmS.Split(); err != nil {
return
}
return
}
if p.Pos != "last" {
err = ErrUnknownPos
return
}
// Otherwise this gets... messy.
if base, ok = netipx.FromStdIPNet(p.network); !ok {
err = ErrBadBoundary
return
}
if !base.IsValid() {
err = ErrBadBoundary
return
}
ipsb = new(netipx.IPSetBuilder)
ipsb.AddPrefix(base.Masked())
// First if it's a single host prefix, ezpz gg no re.
if base.Addr().Is6() {
maxPfxLen = maxBitsv6
}
if p.PrefixLength == maxPfxLen {
nets = make([]*netip.Prefix, 1)
nets[0] = new(netip.Prefix)
addr = netipx.PrefixLastIP(base)
fmt.Println(addr.String())
if *nets[0], err = addr.Prefix(int(p.PrefixLength)); err != nil {
return
}
ipsb.Remove(addr)
if remaining, err = ipsb.IPSet(); err != nil {
return
}
return
}
// Otherwise this gets... interesting.
/*
For IPv4, performance NORMALLY would be "fine" on modern hardware with:
1. straight CIDR-splitting
2. grabbing the last prefix
3. condensing the leading prefixes to a new IPSet
But even this can take a long time (see CIDRSplitter.Split comments).
In almost all cases (unless subnetting like, n+12 prefix length),
IPv6 takes WAY too long.
So use the same function (LastSubnetPfx) for both cases.
*/
nets = make([]*netip.Prefix, 1)
nets[0] = new(netip.Prefix)
if *nets[0], err = LastSubnetPfx(base, p.PrefixLength); err != nil {
return
}
ipsb.RemovePrefix(*nets[0])
if remaining, err = ipsb.IPSet(); err != nil {
return
}
return
}