Skip to content

Commit ae71189

Browse files
feat: separate host address count from address count
1 parent 5092e50 commit ae71189

File tree

5 files changed

+58
-24
lines changed

5 files changed

+58
-24
lines changed

README.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,20 @@ To get more information on a CIDR range:
2525
```
2626
$ cidr explain 10.0.0.0/16
2727
Base Address: 10.0.0.0
28-
Usable Address Range: 10.0.0.1 to 10.0.255.254
28+
Usable Address Range: 10.0.0.1 to 10.0.255.254 (65,534)
2929
Broadcast Address: 10.0.255.255
30-
Address Count: 65,534
30+
Addresses: 65,536
3131
Netmask: 255.255.0.0 (/16 bits)
3232
```
3333

3434
This also works with IPv6 CIDR ranges, for example:
3535

3636
```
37-
$ cidr explain 2001:db8:1234:1a00::/64
37+
$ cidr explain 2001:db8:1234:1a00::/110
3838
Base Address: 2001:db8:1234:1a00::
39-
Usable Address Range: 2001:db8:1234:1a00:: to 2001:db8:1234:1a00:ffff:ffff:ffff:ffff
40-
Address Count: 18,446,744,073,709,551,614
41-
Netmask: ffff:ffff:ffff:ffff:: (/64 bits)
39+
Usable Address Range: 2001:db8:1234:1a00:: to 2001:db8:1234:1a00::3:ffff (262,142)
40+
Addresses: 262,144
41+
Netmask: ffff:ffff:ffff:ffff:ffff:ffff:fffc:0 (/110 bits)
4242
```
4343

4444
### Check whether an address belongs to a CIDR range
@@ -57,20 +57,20 @@ $ cidr contains 2001:db8:1234:1a00::/106 2001:db8:1234:1a00::
5757
true
5858
```
5959

60-
### Count distinct host addresses
60+
### Count
6161

62-
To get all distinct host addresses part of a given CIDR range:
62+
To get a count of all addresses in a CIDR range:
6363

6464
```
6565
$ cidr count 10.0.0.0/16
66-
65534
66+
65536
6767
```
6868

6969
This also works with a IPv6 CIDR range, for example:
7070

7171
```
7272
$ cidr count 2001:db8:1234:1a00::/106
73-
4194302
73+
4194304
7474
```
7575

7676
Or with a large prefix like a point-to-point link CIDR range:

cmd/count.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ import (
1111
)
1212

1313
const (
14-
countExample = "# Return the count of all distinct host addresses within a given IPv4 CIDR range\n" +
14+
countExample = "# Return the count of all addresses within a given IPv4 CIDR range\n" +
1515
"cidr count 10.0.0.0/16\n" +
1616
"\n" +
17-
"# Return the count of all distinct host addresses within a given IPv6 CIDR range\n" +
17+
"# Return the count of all distinct within a given IPv6 CIDR range\n" +
1818
"cidr count 2001:db8:1234:1a00::/106"
1919
)
2020

2121
var (
2222
countCmd = &cobra.Command{
2323
Use: "count",
24-
Short: "Return the count of all distinct host addresses in a given CIDR range",
24+
Short: "Return the count of all addresses in a given CIDR range",
2525
Example: countExample,
2626
Run: func(cmd *cobra.Command, args []string) {
2727
if len(args) != 1 {

cmd/explain.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type networkDetailsToDisplay struct {
5555
PrefixLength int
5656
BaseAddress net.IP
5757
Count string
58+
HostCount string
5859
UsableAddressRangeHasError bool
5960
FirstUsableIPAddress string
6061
LastUsableIPAddress string
@@ -94,6 +95,11 @@ func getNetworkDetails(network *net.IPNet) *networkDetailsToDisplay {
9495
// Format the count as a human-readable string and store it in the details struct.
9596
details.Count = helper.FormatNumber(count.String())
9697

98+
// Obtain the total count of distinct host addresses in the network.
99+
hostCount := core.GetHostAddressCount(network)
100+
// Format the count as a human-readable string and store it in the details struct.
101+
details.HostCount = helper.FormatNumber(hostCount.String())
102+
97103
// Obtain the first and last usable IP addresses, handling errors if they occur.
98104
firstUsableIP, err := core.GetFirstUsableIPAddress(network)
99105
if err != nil {
@@ -120,17 +126,20 @@ func explain(details *networkDetailsToDisplay) {
120126
var lengthIndicator string
121127

122128
fmt.Printf(color.BlueString("Base Address:\t\t ")+"%s\n", details.BaseAddress)
129+
123130
if !details.UsableAddressRangeHasError {
124-
fmt.Printf(color.BlueString("Usable Address Range:\t ")+"%s to %s\n", details.FirstUsableIPAddress, details.LastUsableIPAddress)
131+
fmt.Printf(color.BlueString("Usable Address Range:\t ")+"%s to %s (%s)\n", details.FirstUsableIPAddress, details.LastUsableIPAddress, details.HostCount)
125132
} else {
126133
fmt.Printf(color.RedString("Usable Address Range:\t ")+"%s\n", "unable to calculate usable address range")
127134
}
135+
128136
if !details.BroadcastAddressHasError && details.IsIPV4Network {
129137
fmt.Printf(color.BlueString("Broadcast Address:\t ")+"%s\n", details.BroadcastAddress)
130138
} else if details.BroadcastAddressHasError && details.IsIPV4Network {
131139
fmt.Printf(color.RedString("Broadcast Address:\t ")+"%s\n", details.BroadcastAddress)
132140
}
133-
fmt.Printf(color.BlueString("Host Addresses:\t\t ")+"%s\n", details.Count)
141+
142+
fmt.Printf(color.BlueString("Addresses:\t\t ")+"%s\n", details.Count)
134143

135144
if details.PrefixLength > 1 {
136145
lengthIndicator = "bits"

pkg/core/core.go

+30-5
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,48 @@ func ParseCIDR(network string) (*net.IPNet, error) {
1818
return ip, err
1919
}
2020

21-
// GetAddressCount returns the number of addresses in the given IP network.
22-
// It considers the network type (IPv4 or IPv6) and handles edge cases for specific prefix lengths.
23-
// The result excludes the network address and broadcast address.
21+
// GetAddressCount returns the total number of addresses in the given IP network.
22+
// It accounts for both IPv4 and IPv6 networks, and handles specific cases for certain prefix lengths.
2423
func GetAddressCount(network *net.IPNet) *big.Int {
2524
prefixLen, bits := network.Mask.Size()
2625

26+
// Handle specific cases for IPv4 prefix lengths.
27+
if network.IP.To4() != nil {
28+
switch prefixLen {
29+
case 32:
30+
// A /32 prefix contains a single address.
31+
return big.NewInt(1)
32+
case 31:
33+
// A /31 prefix is used for point-to-point links and contains two addresses.
34+
return big.NewInt(2)
35+
}
36+
}
37+
38+
// Calculate the total number of addresses based on the prefix length.
39+
return new(big.Int).Lsh(big.NewInt(1), uint(bits-prefixLen))
40+
}
41+
42+
// GetHostAddressCount returns the number of distinct host addresses in the given IP network.
43+
// It considers the network type (IPv4 or IPv6) and handles edge cases for specific prefix lengths.
44+
// The result excludes the network address and the broadcast address, if applicable.
45+
func GetHostAddressCount(network *net.IPNet) *big.Int {
46+
prefixLen, bits := network.Mask.Size()
47+
2748
// Handle edge cases for specific IPv4 prefix lengths.
28-
if network.Mask != nil && network.IP.To4() != nil {
49+
if network.IP.To4() != nil {
2950
switch prefixLen {
3051
case 32:
52+
// Single IP address for /32 (e.g., point-to-point link).
3153
return big.NewInt(1)
3254
case 31:
55+
// Two IP addresses for /31 (point-to-point link).
3356
return big.NewInt(2)
3457
}
3558
}
3659

37-
return big.NewInt(0).Lsh(big.NewInt(1), uint(bits-prefixLen))
60+
// Calculate the total number of addresses and subtract 2 (network and broadcast addresses).
61+
totalAddresses := new(big.Int).Exp(big.NewInt(2), big.NewInt(int64(bits-prefixLen)), nil)
62+
return totalAddresses.Sub(totalAddresses, big.NewInt(2))
3863
}
3964

4065
// ContainsAddress checks if the given IP network contains the specified IP address.

pkg/core/core_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,22 @@ func TestGetAddressCount(t *testing.T) {
4040
expectedCount *big.Int
4141
}{
4242
{
43-
name: "Return the count of all distinct host addresses in a common IPv4 CIDR",
43+
name: "Return the count of all addresses in a common IPv4 CIDR",
4444
cidr: IPv4CIDR,
4545
expectedCount: big.NewInt(65536),
4646
},
4747
{
48-
name: "Return the count of all distinct host addresses in a common IPv6 CIDR",
48+
name: "Return the count of all addresses in a common IPv6 CIDR",
4949
cidr: IPv6CIDR,
5050
expectedCount: big.NewInt(4194304),
5151
},
5252
{
53-
name: "Return the count of all distinct host addresses in an uncommon (large prefix) IPv4 CIDR",
53+
name: "Return the count of all addresses in an uncommon (large prefix) IPv4 CIDR",
5454
cidr: largeIPv4PrefixCIDR,
5555
expectedCount: big.NewInt(2),
5656
},
5757
{
58-
name: "Return the count of all distinct host addresses in an uncommon (largest prefix) IPv4 CIDR",
58+
name: "Return the count of all addresses in an uncommon (largest prefix) IPv4 CIDR",
5959
cidr: largestIPv4PrefixCIDR,
6060
expectedCount: big.NewInt(1),
6161
},

0 commit comments

Comments
 (0)