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 13 commits
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
46 changes: 45 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,53 @@ This also works with IPv6 CIDR ranges, for example:

```
$ cidr overlaps 2001:db8:1111:2222:1::/80 2001:db8:1111:2222:1:1::/96
true

Phaze228 marked this conversation as resolved.
Show resolved Hide resolved
```

### CIDR division

To divide a CIDR range into N distinct networks:
## IPV4
```
$ cidr divide 10.0.0.0/16 9
[Networks]
10.0.0.0/20
10.0.16.0/20
10.0.32.0/20
10.0.48.0/20
10.0.64.0/20
10.0.80.0/20
10.0.96.0/20
10.0.112.0/20
10.0.128.0/20
```

## IPV6
```
$ cidr divide 2001:db8:1111:2222:1::/80 9
[Networks]
2001:db8:1111:2222:1::/84
2001:db8:1111:2222:1:1000::/84
2001:db8:1111:2222:1:2000::/84
2001:db8:1111:2222:1:3000::/84
2001:db8:1111:2222:1:4000::/84
2001:db8:1111:2222:1:5000::/84
2001:db8:1111:2222:1:6000::/84
2001:db8:1111:2222:1:7000::/84
2001:db8:1111:2222:1:8000::/84

```

You can also use the `-u` flag to divide the network based on the number of desired host addresses. This calculation includes a broadcast and gateway address, so you only need to consider the host addresses.
The command below shows how to divide a CIDR range to accommodate a minimum of 32 hosts, 30 hosts, and 12 hosts. It also provides the total possible hosts available for a subnet.
```
$ cidr d 192.168.0.0/24 -u 32,30,12
[Networks] [Used] [Total]
192.168.0.0/26 32 62
192.168.0.64/27 30 30
192.168.0.96/28 12 14

```
## Contributing

Contributions are highly appreciated and always welcome.
Expand Down
43 changes: 43 additions & 0 deletions cmd/cmd_tests/count_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Bruno Schaatsbergen
// SPDX-License-Identifier: MIT

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)
}
}
}
126 changes: 126 additions & 0 deletions cmd/cmd_tests/divide_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (c) Bruno Schaatsbergen
// SPDX-License-Identifier: MIT

package cmd_test

import (
"fmt"
"net"
"testing"
"github.com/bschaatsbergen/cidr/pkg/core"
)

func TestDivideCidr(t *testing.T) {
testCases := []struct {
cidr string
divisor int64
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 := core.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())
}
}
}
}

func TestDivideCidrHosts(t *testing.T) {
testCases := []struct {
inputCIDR string
desiredUsers []int64
expectedNets []string
expectedErr error
}{
{"192.168.0.0/24", []int64{20, 10, 30}, []string{"192.168.0.0/27", "192.168.0.32/28", "192.168.0.48/27"}, nil},
{"10.0.0.0/16", []int64{1000, 500}, []string{"10.0.0.0/22", "10.0.4.0/23"}, nil},
{"2001:db8::/32", []int64{50000}, []string{"2001:db8::/112"}, nil},
{"192.168.0.0/24", []int64{}, []string{}, nil}, // Edge case: empty desiredUsers
{"192.168.0.0/24", []int64{257}, nil, fmt.Errorf("Total address space is: 256 but desired Hosts requires 512 addresses\n")}, // Edge case: not enough addresses
}

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

validatedHosts, err := core.ValidateHostSpace(ipNet, tc.desiredUsers)
if tc.expectedErr != nil {
if err == nil || err.Error() != tc.expectedErr.Error() {
t.Errorf("A: For %s, users %v: Expected error '%v', got '%v'", tc.inputCIDR, tc.desiredUsers, tc.expectedErr, err)
}
continue
}

networks, err := core.DivideCidrHosts(ipNet, validatedHosts)

if tc.expectedErr != nil {
if err == nil || err.Error() != tc.expectedErr.Error() {
t.Errorf("A: For %s, users %v: Expected error '%v', got '%v'", tc.inputCIDR, tc.desiredUsers, tc.expectedErr, err)
}
continue
}

if err != nil {
t.Errorf("B: For %s, users %v: Unexpected error '%v'", tc.inputCIDR, tc.desiredUsers, err)
continue
}

netStrings := make([]string, len(networks))
for i, n := range networks {
netStrings[i] = n.String()
}

if !equalStringSlices(netStrings, tc.expectedNets) {
fmt.Println(networks)
t.Errorf("C: For %s, users %v: Expected subnets '%v', got '%v'", tc.inputCIDR, tc.desiredUsers, tc.expectedNets, netStrings)
}
}
}

func equalStringSlices(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
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
}
Loading
Loading