Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add divide subcommand #100

Merged
merged 23 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e0d81f7
Fixed IPv6 address overflow// Added next CIDR command // Added Divide…
Phaze228 May 23, 2024
28379bd
Took out unecessary calculations in divideCmd, added -u functionality…
Phaze228 May 25, 2024
c600c32
fixed intcast in next.go; gofmt'd -s things.
Phaze228 May 27, 2024
7d286eb
Merge branch 'main' into add-next-divide
bschaatsbergen May 28, 2024
bdf4af8
fixed linter errors
Phaze228 May 29, 2024
ab73d6e
Merge branch 'add-next-divide' of github.com:Phaze228/cidr into add-n…
Phaze228 May 29, 2024
f8d4852
linter tidy
Phaze228 May 29, 2024
4e2a391
Added license....
Phaze228 May 30, 2024
e623d6c
Apply suggestions from code review
Phaze228 May 30, 2024
cc3b40d
divide: changed -u -> -H, moved some code to Core | explain: Changed …
Phaze228 Jun 1, 2024
eb2ae11
Properly committing formatter function...
Phaze228 Jun 1, 2024
08dbb6b
Merge branch 'main' into add-next-divide
bschaatsbergen Jun 5, 2024
c9959de
Merge branch 'main' into add-next-divide
mmourick Jun 5, 2024
02c2879
Update README.md
Phaze228 Jun 7, 2024
370d337
changed variable, gofmt'd for the linter
Phaze228 Jun 7, 2024
b667f47
Merge branch 'add-next-divide' of github.com:Phaze228/cidr into add-n…
Phaze228 Jun 7, 2024
a05b971
Merge branch 'main' into add-next-divide
bschaatsbergen Jun 7, 2024
e70c249
Merge branch 'main' into add-next-divide
bschaatsbergen Jun 17, 2024
6a8af6e
Merge branch 'main' into add-next-divide
bschaatsbergen Jun 18, 2024
2e1d597
feat: slim down to only feature
bschaatsbergen Jun 29, 2024
ade347f
chore: go mod tidy
bschaatsbergen Jun 29, 2024
f5e809c
docs: make consistent with other examples
bschaatsbergen Jun 29, 2024
c0390a2
chore: remove aliases
bschaatsbergen Jun 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions cmd/cmd_tests/count_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cmd_test

import (
"math/big"
"net"
"testing"

"github.com/bschaatsbergen/cidr/pkg/core"
)


func TestGetAddressCount(t *testing.T) {
testCases := []struct {
cidr string
expected *big.Int
}{
// Common IPv4 cases
{"10.0.0.0/24", big.NewInt(256)},
{"192.168.1.0/28", big.NewInt(16)},
{"172.16.0.0/12", big.NewInt(1048576)},
// IPv4 edge cases
{"192.168.0.0/31", big.NewInt(2)},
{"10.10.10.10/32", big.NewInt(1)},
// IPv6 cases
{"2001:db8::/32", big.NewInt(0).Lsh(big.NewInt(1), 96)}, // [128 - 32 -> 2^96 ]
{"fe80::/64", big.NewInt(0).Lsh(big.NewInt(1), 64)}, // [128 - 64 -> 2^64 ]
{"::1/128", big.NewInt(1)},
}

for _, tc := range testCases {
_, ipNet, err := net.ParseCIDR(tc.cidr)
if err != nil {
t.Fatalf("Failed to parse CIDR %s: %v", tc.cidr, err)
}

actual := core.GetAddressCount(ipNet)
if actual.Cmp(tc.expected) != 0 {
t.Errorf("For CIDR %s, expected address count %d, but got %d", tc.cidr, tc.expected, actual)
}
}
}
58 changes: 58 additions & 0 deletions cmd/cmd_tests/divide_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cmd_test

import (
"net"
"testing"

"github.com/bschaatsbergen/cidr/cmd"
)

func TestDivideCidr(t *testing.T) {
testCases := []struct {
cidr string
divisor uint64
expected []string // Expected CIDRs in string format
shouldErr bool
}{
{"10.0.0.0/16", 2, []string{"10.0.0.0/17", "10.0.128.0/17"}, false},
{"192.168.0.0/24", 4, []string{"192.168.0.0/26", "192.168.0.64/26", "192.168.0.128/26", "192.168.0.192/26"}, false},
{"2001:db8::/32", 3, []string{"2001:db8::/34", "2001:db8:4000::/34", "2001:db8:8000::/34"}, false},
// Error cases
{"10.0.0.0/16", 0, nil, true}, // Divisor is zero
{"2001:db8::/128", 3, nil, true}, // Cannot divide /128
}

for _, tc := range testCases {
_, ipNet, err := net.ParseCIDR(tc.cidr)
if err != nil && !tc.shouldErr {
t.Errorf("Unexpected error parsing CIDR: %s", err)
continue
}

subnets, err := cmd.DivideCidr(ipNet, tc.divisor)


if tc.shouldErr {
if err == nil {
t.Errorf("Expected error for CIDR %s, divisor %d, but got none", tc.cidr, tc.divisor)
}
continue
}

if err != nil {
t.Errorf("Unexpected error for CIDR %s, divisor %d: %s", tc.cidr, tc.divisor, err)
continue
}

if len(subnets) != len(tc.expected) {
t.Errorf("Incorrect number of subnets for CIDR %s, divisor %d: expected %d, got %d", tc.cidr, tc.divisor, len(tc.expected), len(subnets))
continue
}

for i, expectedCIDR := range tc.expected {
if subnets[i].String() != expectedCIDR {
t.Errorf("Incorrect subnet %d for CIDR %s, divisor %d: expected %s, got %s", i, tc.cidr, tc.divisor, expectedCIDR, subnets[i].String())
}
}
}
}
3 changes: 2 additions & 1 deletion cmd/count.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package cmd

import (
"fmt"
"math/big"
"net"
"os"

Expand Down Expand Up @@ -48,7 +49,7 @@ func init() {
rootCmd.AddCommand(countCmd)
}

func count(network *net.IPNet) uint64 {
func count(network *net.IPNet) *big.Int {
Phaze228 marked this conversation as resolved.
Show resolved Hide resolved
count := core.GetAddressCount(network)
return count
}
155 changes: 155 additions & 0 deletions cmd/divide.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package cmd

import (
"fmt"
"math/big"
"net"
"os"
"strconv"

"github.com/bschaatsbergen/cidr/pkg/core"
"github.com/bschaatsbergen/cidr/pkg/helper"
"github.com/spf13/cobra"
)



const (
divideExample = "# Divides the given CIDR range into N distinct networks\n" +
"cidr divide 10.0.0.0/16 9\n" +
"[Networks]\n" +
"10.0.0.0/20\n" +
"10.0.16.0/20\n" +
"10.0.32.0/20\n" +
"10.0.48.0/20\n" +
"10.0.64.0/20\n" +
"10.0.80.0/20\n" +
"10.0.96.0/20\n" +
"10.0.112.0/20\n" +
"10.0.128.0/20\n" +
"-----\n" +
""
divideHelpMessage = "See 'cidr divide -h' for more details"
);

var (
divideCmd=&cobra.Command{
Use: "divide",
Short: "Divides CIDR range into N distinct ranges",
Example: divideExample,
Run: func(cmd *cobra.Command, args []string) {
if len(args) !=2 {
fmt.Printf("[ERROR]: Provide a CIDR range AND a divisor: cidr <CIDR_RANGE> <DIVISOR>\n")
fmt.Println(divideHelpMessage)
os.Exit(1)
}
network, err:= core.ParseCIDR(args[0])
if err != nil {
fmt.Printf("%s\n", err)
fmt.Println(divideHelpMessage)
os.Exit(1)
}
divisor, err := strconv.ParseUint(args[1], 10, 64)
if err != nil || divisor <= 1 {
fmt.Printf("%s\n", err)
fmt.Println(divideHelpMessage)
os.Exit(1)
}
networks, err := DivideCidr(network, divisor)
if err != nil {
fmt.Printf("%s\n", err)
fmt.Println(divideHelpMessage)
os.Exit(1)
}
printNetworkPartitions(networks)
},

}
)

func init() {
rootCmd.AddCommand(divideCmd)
}

// Divides the given network into N smaller networks
// Errors if division is not possible
func DivideCidr(network *net.IPNet, divisor uint64) ([]net.IPNet, error) {
networks := make([]net.IPNet, 0)
isIPv4 := helper.IsIPv4Network(network)
maskSize, _ := network.Mask.Size()
if divisor <= 1 {
return nil, fmt.Errorf("[ERROR] Input a divisor higher than 1\n")
}

if isIPv4 && (maskSize == 32) {
return nil, fmt.Errorf("[ERROR] Cannot divide a %s -- No Space\n", network.String())
}
if maskSize >= 128 {
return nil, fmt.Errorf("[ERROR] Cannot divide a %s -- No Space\n", network.String())
}

addressCount := core.GetAddressCount(network)
cidrWack, err := getPrefix(divisor, addressCount, isIPv4)
if err != nil {
return nil, fmt.Errorf("%s\n", err)
}

nextAddress := new(net.IPNet)
nextAddress.IP = network.IP
nextAddress.Mask = cidrWack
for i := uint64(0); i < divisor ; i++ {
networks = append(networks, *nextAddress)
new, err := core.GetNextAddress(nextAddress, cidrWack)
if err != nil {
return nil, err
}
nextAddress.IP = new.IP
}
return networks, nil
}

// Returns the net.IPMask necessary for the provided divisor.
// Errors if address space if insufficient or divison is not possible.
func getPrefix(divisor uint64, addressCount *big.Int, IPv4 bool) (net.IPMask, error) {
div := big.NewInt(int64(divisor))
Fixed Show fixed Hide fixed
if addressCount.Cmp(div) == -1 || div.Cmp(big.NewInt(0)) == 0 {
return nil, fmt.Errorf("[ERROR] Cannot divide %d Addresses into %d divisions\n",addressCount, div)
}

// Gets address partitions
// If partition is 256
//
// 00000010
// 2 << 8
// 1 00000000
// prefix = 8, then 32 - 8 = /24
addressPartition := new(big.Int).Div(addressCount,div)
two := big.NewInt(2)
exponent := big.NewInt(0)
for two.Cmp(addressPartition) <= 0 {
two.Lsh(two,1)
exponent.Add(exponent, big.NewInt(1))
}
subnetPrefix := int(exponent.Int64())
if IPv4 {
if subnetPrefix > 30 {
return nil, fmt.Errorf("[ERROR] Address Space is insufficient for %d subnets\n", div)
}
return net.CIDRMask(32-subnetPrefix,32),nil
}
if subnetPrefix > 126 {
return nil, fmt.Errorf("[ERROR] Address Space is insufficient for %d subnets\n", div)
}
return net.CIDRMask(128-subnetPrefix,128), nil

}


func printNetworkPartitions(networks []net.IPNet) {
fmt.Println("[Networks]")
for _, network := range networks {
fmt.Println(network.String())
}
}


69 changes: 69 additions & 0 deletions cmd/next.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

package cmd

import (
"fmt"
"net"
"os"
"strconv"

"github.com/bschaatsbergen/cidr/pkg/core"
"github.com/bschaatsbergen/cidr/pkg/helper"
//"github.com/fatih/color"
"github.com/spf13/cobra"
)



const (
nextExample = "# Provides the next available address space with your desired CIDR\n" +
"cidr next 10.0.0.0/16 27\n" +
"Result: 10.1.0.0/27\n"
nextHelpMessage = "See 'cidr next -h' for more details"
);

var (
nextCmd=&cobra.Command{
Use: "next",
Short: "Provides next available address space with your desired CIDR",
Example: nextExample,
Run: func(cmd *cobra.Command, args []string) {
if len(args) !=2 {
fmt.Printf("[ERROR]: Provide a network AND a cidr: cidr <CIDR_NETWORK> <CIDR>\n")
fmt.Println(nextHelpMessage)
os.Exit(1)
}
network, err:= core.ParseCIDR(args[0])
if err != nil {
fmt.Printf("%s\n", err)
fmt.Println(nextHelpMessage)
os.Exit(1)
}
cidrNum, err := strconv.ParseUint(args[1], 10, 64)
var cidr net.IPMask
if err != nil || cidrNum <= 1 {
fmt.Printf("%s\n", err)
fmt.Println(nextHelpMessage)
os.Exit(1)
}
if helper.IsIPv4Network(network) {
cidr = net.CIDRMask(int(cidrNum), 32)
Fixed Show fixed Hide fixed
} else {
cidr = net.CIDRMask(int(cidrNum), 128)
Fixed Show fixed Hide fixed
}
nextNetwork, err := core.GetNextAddress(network, cidr)
if err != nil {
fmt.Printf("%s\n", err)
fmt.Println(nextHelpMessage)
os.Exit(1)
}
fmt.Println(nextNetwork.String())
},

}
)

func init() {
rootCmd.AddCommand(nextCmd)
}

Loading
Loading