Skip to content

Commit

Permalink
Merge pull request #121 from ipinfo/umar/splitcidr
Browse files Browse the repository at this point in the history
splitcidr
  • Loading branch information
UmanShahzad authored Sep 28, 2022
2 parents d69239d + 03d0a4d commit f5bc3be
Show file tree
Hide file tree
Showing 22 changed files with 534 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ cidr2ip/dist/
range2cidr/dist/
range2ip/dist/
randip/dist/
splitcidr/dist/
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ Replace `<path>` with the required location.
## Additional CLIs

The `ipinfo` CLI has some subcommands like `grepip`, `prips`, `cidr2range`, `cidr2ip`,
`range2cidr`, `range2ip` and `randip` which are also shipped as standalone binaries.
`range2cidr`, `range2ip`, `splitcidr` and `randip` which are also shipped as standalone binaries.

These binaries are available via all the **same installation methods** as
mentioned above for `ipinfo`, except you must change only the name to the name
Expand All @@ -194,6 +194,7 @@ Currently these subcommands are separately shipped:
| range2cidr | [1.3.0](https://github.com/ipinfo/cli/releases/tag/range2cidr-1.3.0) |
| range2ip | [1.0.0](https://github.com/ipinfo/cli/releases/tag/range2ip-1.0.0) |
| randip | [1.1.0](https://github.com/ipinfo/cli/releases/tag/randip-1.1.0) |
| splitcidr | [1.0.0](https://github.com/ipinfo/cli/releases/tag/splitcidr-1.0.0) |

## Quick Start

Expand Down
1 change: 1 addition & 0 deletions ipinfo/cmd_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Commands:
range2cidr convert IP ranges to CIDRs.
range2ip convert IP ranges to individual IPs within those ranges.
randip Generates random IPs.
splitcidr splits a larger CIDR into smaller CIDRs.
cache manage the cache.
config manage the config.
login save an API token session.
Expand Down
40 changes: 40 additions & 0 deletions ipinfo/cmd_splitcidr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"fmt"

"github.com/ipinfo/cli/lib"
"github.com/ipinfo/cli/lib/complete"
"github.com/ipinfo/cli/lib/complete/predict"
"github.com/spf13/pflag"
)

var completionsSplitCIDR = &complete.Command{
Flags: map[string]complete.Predictor{
"-h": predict.Nothing,
"--help": predict.Nothing,
},
}

func printHelpSplitCIDR() {
fmt.Printf(
`Usage: %s splitcidr <cidr> <split>
Description:
Splits a larger CIDR into smaller CIDRs.
$ %[1]s splitcidr 8.8.8.0/24 25
Options:
--help, -h
show help.
`, progBase)
}

func cmdSplitCIDR() error {
f := lib.CmdSplitCIDRFlags{}
f.Init()
pflag.Parse()

return lib.CmdSplitCIDR(f, pflag.Args()[1:], printHelpSplitCIDR)
}
1 change: 1 addition & 0 deletions ipinfo/completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var completions = &complete.Command{
"range2cidr": completionsRange2CIDR,
"range2ip": completionsRange2IP,
"randip": completionsRandIP,
"splitcidr": completionsSplitCIDR,
"cache": completionsCache,
"login": completionsLogin,
"logout": completionsLogout,
Expand Down
2 changes: 2 additions & 0 deletions ipinfo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ func main() {
err = cmdRange2IP()
case cmd == "randip":
err = cmdRandIP()
case cmd == "splitcidr":
err = cmdSplitCIDR()
case cmd == "cache":
err = cmdCache()
case cmd == "config":
Expand Down
84 changes: 84 additions & 0 deletions lib/cmd_splitcidr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package lib

import (
"fmt"
"net"
"strconv"

"github.com/spf13/pflag"
)

type CmdSplitCIDRFlags struct {
Help bool
}

// Init initializes the common flags available to CmdSplitCIDR with sensible
// defaults.
//
// pflag.Parse() must be called to actually use the final flag values.
func (f *CmdSplitCIDRFlags) Init() {
pflag.BoolVarP(
&f.Help,
"help", "h", false,
"show help.",
)
}

// CmdSplitCIDR is the common core logic for the splitcidr command.
func CmdSplitCIDR(
f CmdSplitCIDRFlags,
args []string,
printHelp func(),
) error {
if f.Help {
printHelp()
return nil
}

if len(args) != 2 {
printHelp()
return nil
}

cidrString := args[0]
splitString := args[1]
ip, _, err := net.ParseCIDR(cidrString)
if err != nil {
return err
}

split, err := strconv.Atoi(splitString)
if err != nil {
return nil
}

if ip.To4() != nil {
ipsubnet, err := IPSubnetFromCidr(cidrString)
if err != nil {
return err
}

subs, err := ipsubnet.SplitCIDR(split)
if err != nil {
return err
}

for _, s := range subs {
fmt.Println(s.ToCIDR())
}
} else {
ipsubnet, err := IP6SubnetFromCidr(cidrString)
if err != nil {
return err
}
subs, err := ipsubnet.SplitCIDR(split)
if err != nil {
return err
}
for _, s := range subs {
fmt.Println(s.ToCIDR())
}
}

return nil
}
74 changes: 74 additions & 0 deletions lib/ip6_subnet.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package lib

import (
"encoding/binary"
"fmt"
"math"
"math/big"
"net"
"strconv"
)

Expand Down Expand Up @@ -60,3 +65,72 @@ func CIDRsFromIP6RangeStrRaw(rStr string) ([]string, error) {

return r.ToCIDRs(), nil
}

// IP6SubnetFromCidr converts a CIDR notation to IP6Subnet.
func IP6SubnetFromCidr(cidr string) (IP6Subnet, error) {
_, network, err := net.ParseCIDR(cidr)
if err != nil {
return IP6Subnet{}, err
}

ones, _ := network.Mask.Size()
netMask, hostMask := NetAndHostMasks6(uint32(ones))
starthi := binary.BigEndian.Uint64(network.IP[:8])
startlo := binary.BigEndian.Uint64(network.IP[8:])
start := NewIP6(starthi, startlo)
ip6subnet := IP6Subnet{
HostBitCnt: uint32(128 - ones),
HostMask: hostMask,
NetBitCnt: uint32(ones),
NetMask: netMask,
LoIP: IP6FromU128(start.N.And(netMask)),
HiIP: IP6FromU128(start.N.And(netMask).Or(hostMask)),
}

return ip6subnet, nil
}

// SplitCIDR returns a list of smaller IPSubnet after splitting a larger CIDR
// into `split`.
func (s IP6Subnet) SplitCIDR(split int) ([]IP6Subnet, error) {
bitshifts := int(uint32(split) - s.NetBitCnt)
if bitshifts < 0 || bitshifts > 128 || int(s.NetBitCnt)+bitshifts > 128 {
return nil, fmt.Errorf("wrong split string")
}

hostBits := (128 - s.NetBitCnt) - uint32(bitshifts)
netMask, hostMask := NetAndHostMasks6(uint32(split))
subnetCount := math.Pow(2, float64(bitshifts))
subnetCountBig := big.NewFloat(subnetCount)
hostCount := math.Pow(2, float64(hostBits))
hostCountbig := big.NewFloat(hostCount)

var ipsubnets []IP6Subnet
for i := big.NewFloat(0); i.Cmp(subnetCountBig) < 0; i.Add(i, big.NewFloat(1)) {
// calculating new LoIP by `LoIP + i*(hostCount)`
hostCountMul := new(big.Float)
hostCountMul.Mul(i, hostCountbig)
newIP := new(big.Int)
newIP.SetBytes(s.LoIP.To16ByteSlice())
hostCountAdd := new(big.Int)
result := new(big.Int)
hostCountMul.Int(result)
hostCountAdd.Add(newIP, result)

// converting `bigint` to `IP6`
ipArr := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
copy(ipArr[16-len(hostCountAdd.Bytes()):], hostCountAdd.Bytes())
startIP := NewIP6(binary.BigEndian.Uint64(ipArr[:8]), binary.BigEndian.Uint64(ipArr[8:]))

subnet := IP6Subnet{
HostBitCnt: uint32(128 - split),
HostMask: hostMask,
NetBitCnt: uint32(split),
LoIP: IP6FromU128(startIP.N.And(netMask)),
HiIP: IP6FromU128(startIP.N.And(netMask).Or(hostMask)),
}
ipsubnets = append(ipsubnets, subnet)
}

return ipsubnets, nil
}
50 changes: 50 additions & 0 deletions lib/ip_subnet.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package lib

import (
"encoding/binary"
"fmt"
"net"
"strconv"
)

Expand Down Expand Up @@ -64,3 +67,50 @@ func CIDRsFromIPRangeStrRaw(rStr string) ([]string, error) {

return r.ToCIDRs(), nil
}

// IPSubnetFromCidr converts a CIDR notation to IPSubnet.
func IPSubnetFromCidr(cidr string) (IPSubnet, error) {
_, network, err := net.ParseCIDR(cidr)
if err != nil {
return IPSubnet{}, err
}

ones, _ := network.Mask.Size()
netMask, hostMask := NetAndHostMasks(uint32(ones))
start := binary.BigEndian.Uint32(network.IP)
ipsubnet := IPSubnet{
HostBitCnt: uint32(32 - ones),
HostMask: hostMask,
NetBitCnt: uint32(ones),
NetMask: netMask,
LoIP: IP(uint32(start) & netMask),
HiIP: IP((uint32(start) & netMask) | hostMask),
}

return ipsubnet, nil
}

// SplitCIDR returns a list of smaller IPSubnet after splitting a larger CIDR
// into `split`.
func (s IPSubnet) SplitCIDR(split int) ([]IPSubnet, error) {
bitshifts := int(uint32(split) - s.NetBitCnt)
if bitshifts < 0 || bitshifts > 31 || int(s.NetBitCnt)+bitshifts > 32 {
return nil, fmt.Errorf("wrong split string")
}

hostBits := (32 - s.NetBitCnt) - uint32(bitshifts)
netMask, hostMask := NetAndHostMasks(uint32(split))
ipsubnets := make([]IPSubnet, 1<<bitshifts)
for i := range ipsubnets {
start := uint32(s.LoIP) + uint32(i*(1<<hostBits))
ipsubnets[i] = IPSubnet{
HostBitCnt: uint32(32 - split),
HostMask: hostMask,
NetBitCnt: uint32(split),
LoIP: IP(uint32(start) & netMask),
HiIP: IP((uint32(start) & netMask) | hostMask),
}
}

return ipsubnets, nil
}
1 change: 1 addition & 0 deletions scripts/fmt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ gofmt -w \
$ROOT/range2cidr \
$ROOT/range2ip \
$ROOT/cidr2ip \
$ROOT/splitcidr \
$ROOT/randip
1 change: 1 addition & 0 deletions scripts/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ golint \
./range2cidr/... \
./range2ip/... \
./cidr2ip/... \
./splitcidr/... \
./randip/...
6 changes: 6 additions & 0 deletions splitcidr/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# syntax=docker/dockerfile:1
FROM alpine:latest

WORKDIR /splitcidr
COPY build/splitcidr ./
ENTRYPOINT ["/splitcidr/splitcidr"]
12 changes: 12 additions & 0 deletions splitcidr/build-all-platforms.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

# Build binary for all platforms for version $1.

set -e

DIR=`dirname $0`
ROOT=$DIR/..

VSN=$1

$ROOT/scripts/build-all-platforms.sh "splitcidr" $VSN
10 changes: 10 additions & 0 deletions splitcidr/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

# Build local binary.

set -e

DIR=`dirname $0`
ROOT=$DIR/..

$ROOT/scripts/build.sh "splitcidr"
23 changes: 23 additions & 0 deletions splitcidr/completions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"github.com/ipinfo/cli/lib/complete"
"github.com/ipinfo/cli/lib/complete/predict"
)

var completions = &complete.Command{
Flags: map[string]complete.Predictor{
"-v": predict.Nothing,
"--version": predict.Nothing,
"-h": predict.Nothing,
"--help": predict.Nothing,
"--completions-install": predict.Nothing,
"--completions-bash": predict.Nothing,
"--completions-zsh": predict.Nothing,
"--completions-fish": predict.Nothing,
},
}

func handleCompletions() {
completions.Complete(progBase)
}
Loading

0 comments on commit f5bc3be

Please sign in to comment.