package main import ( "bytes" "errors" "fmt" "io" "log" "net" "net/netip" "os" "strings" "go4.org/netipx" "r00t2.io/subnetter/netsplit" "r00t2.io/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 vlsmSizes []uint8 var splitter netsplit.NetSplitter var cmnArgs common var nets []*netip.Prefix var remaining *netipx.IPSet var buf *bytes.Buffer var res *netsplit.StructuredResults var numNets uint var v6Only bool var noStrict bool var strictErr error var reservations map[netip.Prefix]*netsplit.IANAAddrNetResRecord 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 "num-nets": if numNets, v6Only, err = netsplit.NumNets( args.NumNets.Sizes.SubnetSize, args.NumNets.Sizes.NetworkSize, ); err != nil { log.Panicln(err) } if !args.NumNets.Verbose { fmt.Printf("%d\n", numNets) if !args.NumNets.NoV6Check { fmt.Println(v6Only) } } else { fmt.Printf("Network Size:\t\t\t%d\n", args.NumNets.Sizes.NetworkSize) fmt.Printf("Subnet Size:\t\t\t%d\n", args.NumNets.Sizes.SubnetSize) fmt.Printf("Number of Subnets:\t\t%d\n", numNets) if !args.NumNets.NoV6Check { fmt.Printf("Subnetting is IPv6-Only:\t%v\n", v6Only) } } return case "reserved": if origPfx, err = netip.ParsePrefix(args.Check.Network.Network); err != nil { log.Panicln(err) } nets = make([]*netip.Prefix, 1) nets[0] = new(netip.Prefix) *nets[0] = origPfx if err = netsplit.SetCachePath(args.Check.CacheDir); err != nil { log.Panicln(err) } if err = netsplit.EnableCache(args.Check.DoResCache); err != nil { log.Panicln(err) } if reservations, err = netsplit.CheckReserved(nets, !args.Check.NoRevRecursive, !args.Check.NoRecursive, !args.Check.NoPrivate); err != nil { log.Panicln(err) } if err = printReserved(reservations, origPfx, args.Check.Plain, args.Check.Fmt); err != nil { log.Panicln(err) } return 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 = common{ commonBase: args.Parse.commonBase, Network: Net{ Network: res.Original.String(), }, } 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 "split-vlsm": if err = validate.Struct(args.VLSM); err != nil { log.Panicln(err) } cmnArgs = args.VLSM.common if vlsmSizes, err = args.VLSM.AllSizes(); err != nil { log.Panicln(err) } splitter = &netsplit.VLSMSplitter{ Ascending: args.VLSM.Asc, Explicit: args.VLSM.Explicit, PrefixLengths: vlsmSizes, 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) } } }