212 lines
5.2 KiB
Go
212 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
`fmt`
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/netip"
|
|
"os"
|
|
"strings"
|
|
|
|
"go4.org/netipx"
|
|
"subnetter/netsplit"
|
|
`subnetter/version`
|
|
|
|
"github.com/jessevdk/go-flags"
|
|
"r00t2.io/sysutils/paths"
|
|
)
|
|
|
|
func main() {
|
|
|
|
var err error
|
|
var b []byte
|
|
var pfx *net.IPNet
|
|
var resPfx *netip.Prefix
|
|
var origPfx netip.Prefix
|
|
var splitter netsplit.NetSplitter
|
|
var cmnArgs common
|
|
var nets []*netip.Prefix
|
|
var remaining *netipx.IPSet
|
|
var buf *bytes.Buffer
|
|
var res *netsplit.StructuredResults
|
|
var noStrict bool
|
|
var strictErr error
|
|
var splitErr *netsplit.SplitErr = new(netsplit.SplitErr)
|
|
var parser *flags.Parser = flags.NewParser(args, flags.Default)
|
|
|
|
if _, err = parser.Parse(); err != nil {
|
|
switch flagsErr := err.(type) {
|
|
case *flags.Error:
|
|
switch flagsErr.Type {
|
|
case flags.ErrHelp, flags.ErrCommandRequired, flags.ErrRequired: // These print their relevant messages by themselves.
|
|
return
|
|
default:
|
|
log.Panicln(err)
|
|
}
|
|
default:
|
|
log.Panicln(err)
|
|
}
|
|
}
|
|
|
|
if version.Ver, err = version.Version(); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
|
|
switch parser.Active.Name {
|
|
case "version":
|
|
if args.Version.DetailVersion {
|
|
fmt.Println(version.Ver.Detail())
|
|
return
|
|
} else {
|
|
fmt.Println(version.Ver.Short())
|
|
return
|
|
}
|
|
case "net":
|
|
if origPfx, err = netip.ParsePrefix(args.ExplicitNetwork.Network.Network); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
pfx = netipx.PrefixIPNet(origPfx.Masked())
|
|
cmnArgs = args.ExplicitNetwork.common
|
|
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 "reserved":
|
|
// TODO
|
|
case "table":
|
|
// Account for a weird redundant CLI condition.
|
|
if args.Table.NoIpv4 && args.Table.NoIpv6 {
|
|
args.Table.NoIpv6 = false
|
|
args.Table.NoIpv4 = false
|
|
}
|
|
buf = new(bytes.Buffer)
|
|
if err = tblTpl.ExecuteTemplate(buf, "table.tpl", args.Table); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
os.Stdout.Write(buf.Bytes())
|
|
return
|
|
case "parse":
|
|
if strings.TrimSpace(args.Parse.InFile) == "-" {
|
|
buf = new(bytes.Buffer)
|
|
if _, err = io.Copy(buf, os.Stdin); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
b = buf.Bytes()
|
|
} else {
|
|
if err = paths.RealPath(&args.Parse.InFile); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
if b, err = os.ReadFile(args.Parse.InFile); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
}
|
|
if res, err = netsplit.Parse(b); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
if resPfx, nets, remaining, splitter, err = res.Uncontain(); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
if resPfx != nil {
|
|
origPfx = *resPfx
|
|
}
|
|
pfx = netipx.PrefixIPNet(origPfx.Masked())
|
|
cmnArgs = args.Parse.common
|
|
if err = printNets(&origPfx, pfx, nets, remaining, &cmnArgs, res.GetSplitter()); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
return
|
|
default:
|
|
// Actually subnet (and print results).
|
|
/*
|
|
A netsplit.NetSplitter is needed, along with:
|
|
* prefix
|
|
* verbosity
|
|
* disable showing remaining
|
|
* formatter
|
|
These are all handily-dandily enclosed in a `common` struct type.
|
|
*/
|
|
switch parser.Active.Name {
|
|
case "split-hosts":
|
|
if err = validate.Struct(args.SplitHost); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
cmnArgs = args.SplitHost.common
|
|
splitter = &netsplit.HostSplitter{
|
|
NumberHosts: args.SplitHost.Hosts,
|
|
Strict: args.SplitHost.Strict,
|
|
BaseSplitter: new(netsplit.BaseSplitter),
|
|
}
|
|
noStrict = !args.SplitHost.Strict
|
|
strictErr = netsplit.ErrBadNumHosts
|
|
case "split-nets":
|
|
if err = validate.Struct(args.SplitSubnets); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
cmnArgs = args.SplitSubnets.common
|
|
splitter = &netsplit.SubnetSplitter{
|
|
Strict: args.SplitSubnets.Strict,
|
|
NumberSubnets: args.SplitSubnets.NumNets,
|
|
BaseSplitter: new(netsplit.BaseSplitter),
|
|
}
|
|
noStrict = !args.SplitSubnets.Strict
|
|
strictErr = netsplit.ErrNoNetSpace
|
|
case "split-cidr":
|
|
if err = validate.Struct(args.SplitCIDR); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
cmnArgs = args.SplitCIDR.common
|
|
splitter = &netsplit.CIDRSplitter{
|
|
PrefixLength: args.SplitCIDR.Prefix,
|
|
BaseSplitter: new(netsplit.BaseSplitter),
|
|
}
|
|
case "vlsm":
|
|
if err = validate.Struct(args.VLSM); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
cmnArgs = args.VLSM.common
|
|
splitter = &netsplit.VLSMSplitter{
|
|
Ascending: args.VLSM.Asc,
|
|
PrefixLengths: args.VLSM.Sizes,
|
|
BaseSplitter: new(netsplit.BaseSplitter),
|
|
}
|
|
default:
|
|
err = flags.ErrCommandRequired
|
|
log.Println(err)
|
|
os.Exit(1)
|
|
}
|
|
if origPfx, err = netip.ParsePrefix(cmnArgs.Network.Network); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
// This can be a direct conversion. We have to make sure we mask off the host bits to avoid errors, though.
|
|
/*
|
|
if _, pfx, err = net.ParseCIDR(cmnArgs.network.network); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
*/
|
|
pfx = netipx.PrefixIPNet(origPfx.Masked())
|
|
splitter.SetParent(*pfx)
|
|
if nets, remaining, err = splitter.Split(); err != nil {
|
|
if errors.As(err, &splitErr) {
|
|
if noStrict && errors.Is(splitErr.Wrapped, strictErr) {
|
|
err = nil
|
|
} else {
|
|
printSplitErr(splitErr)
|
|
os.Exit(1)
|
|
}
|
|
} else {
|
|
log.Panicln(err)
|
|
}
|
|
}
|
|
if err = printNets(&origPfx, pfx, nets, remaining, &cmnArgs, splitter); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
}
|
|
|
|
}
|