
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!)
103 lines
2.3 KiB
Go
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
|
|
}
|