Skip to content

Commit

Permalink
feat: separate host address count from address count
Browse files Browse the repository at this point in the history
  • Loading branch information
bschaatsbergen committed Jul 21, 2024
1 parent 5092e50 commit ae71189
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 24 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ To get more information on a CIDR range:
```
$ cidr explain 10.0.0.0/16
Base Address: 10.0.0.0
Usable Address Range: 10.0.0.1 to 10.0.255.254
Usable Address Range: 10.0.0.1 to 10.0.255.254 (65,534)
Broadcast Address: 10.0.255.255
Address Count: 65,534
Addresses: 65,536
Netmask: 255.255.0.0 (/16 bits)
```

This also works with IPv6 CIDR ranges, for example:

```
$ cidr explain 2001:db8:1234:1a00::/64
$ cidr explain 2001:db8:1234:1a00::/110
Base Address: 2001:db8:1234:1a00::
Usable Address Range: 2001:db8:1234:1a00:: to 2001:db8:1234:1a00:ffff:ffff:ffff:ffff
Address Count: 18,446,744,073,709,551,614
Netmask: ffff:ffff:ffff:ffff:: (/64 bits)
Usable Address Range: 2001:db8:1234:1a00:: to 2001:db8:1234:1a00::3:ffff (262,142)
Addresses: 262,144
Netmask: ffff:ffff:ffff:ffff:ffff:ffff:fffc:0 (/110 bits)
```

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

### Count distinct host addresses
### Count

To get all distinct host addresses part of a given CIDR range:
To get a count of all addresses in a CIDR range:

```
$ cidr count 10.0.0.0/16
65534
65536
```

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

```
$ cidr count 2001:db8:1234:1a00::/106
4194302
4194304
```

Or with a large prefix like a point-to-point link CIDR range:
Expand Down
6 changes: 3 additions & 3 deletions cmd/count.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ import (
)

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

var (
countCmd = &cobra.Command{
Use: "count",
Short: "Return the count of all distinct host addresses in a given CIDR range",
Short: "Return the count of all addresses in a given CIDR range",
Example: countExample,
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
Expand Down
13 changes: 11 additions & 2 deletions cmd/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type networkDetailsToDisplay struct {
PrefixLength int
BaseAddress net.IP
Count string
HostCount string
UsableAddressRangeHasError bool
FirstUsableIPAddress string
LastUsableIPAddress string
Expand Down Expand Up @@ -94,6 +95,11 @@ func getNetworkDetails(network *net.IPNet) *networkDetailsToDisplay {
// Format the count as a human-readable string and store it in the details struct.
details.Count = helper.FormatNumber(count.String())

// Obtain the total count of distinct host addresses in the network.
hostCount := core.GetHostAddressCount(network)
// Format the count as a human-readable string and store it in the details struct.
details.HostCount = helper.FormatNumber(hostCount.String())

// Obtain the first and last usable IP addresses, handling errors if they occur.
firstUsableIP, err := core.GetFirstUsableIPAddress(network)
if err != nil {
Expand All @@ -120,17 +126,20 @@ func explain(details *networkDetailsToDisplay) {
var lengthIndicator string

fmt.Printf(color.BlueString("Base Address:\t\t ")+"%s\n", details.BaseAddress)

if !details.UsableAddressRangeHasError {
fmt.Printf(color.BlueString("Usable Address Range:\t ")+"%s to %s\n", details.FirstUsableIPAddress, details.LastUsableIPAddress)
fmt.Printf(color.BlueString("Usable Address Range:\t ")+"%s to %s (%s)\n", details.FirstUsableIPAddress, details.LastUsableIPAddress, details.HostCount)
} else {
fmt.Printf(color.RedString("Usable Address Range:\t ")+"%s\n", "unable to calculate usable address range")
}

if !details.BroadcastAddressHasError && details.IsIPV4Network {
fmt.Printf(color.BlueString("Broadcast Address:\t ")+"%s\n", details.BroadcastAddress)
} else if details.BroadcastAddressHasError && details.IsIPV4Network {
fmt.Printf(color.RedString("Broadcast Address:\t ")+"%s\n", details.BroadcastAddress)
}
fmt.Printf(color.BlueString("Host Addresses:\t\t ")+"%s\n", details.Count)

fmt.Printf(color.BlueString("Addresses:\t\t ")+"%s\n", details.Count)

if details.PrefixLength > 1 {
lengthIndicator = "bits"
Expand Down
35 changes: 30 additions & 5 deletions pkg/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,48 @@ func ParseCIDR(network string) (*net.IPNet, error) {
return ip, err
}

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

// Handle specific cases for IPv4 prefix lengths.
if network.IP.To4() != nil {
switch prefixLen {
case 32:
// A /32 prefix contains a single address.
return big.NewInt(1)
case 31:
// A /31 prefix is used for point-to-point links and contains two addresses.
return big.NewInt(2)
}
}

// Calculate the total number of addresses based on the prefix length.
return new(big.Int).Lsh(big.NewInt(1), uint(bits-prefixLen))
}

// GetHostAddressCount returns the number of distinct host addresses in the given IP network.
// It considers the network type (IPv4 or IPv6) and handles edge cases for specific prefix lengths.
// The result excludes the network address and the broadcast address, if applicable.
func GetHostAddressCount(network *net.IPNet) *big.Int {
prefixLen, bits := network.Mask.Size()

// Handle edge cases for specific IPv4 prefix lengths.
if network.Mask != nil && network.IP.To4() != nil {
if network.IP.To4() != nil {
switch prefixLen {
case 32:
// Single IP address for /32 (e.g., point-to-point link).
return big.NewInt(1)
case 31:
// Two IP addresses for /31 (point-to-point link).
return big.NewInt(2)
}
}

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

// ContainsAddress checks if the given IP network contains the specified IP address.
Expand Down
8 changes: 4 additions & 4 deletions pkg/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,22 @@ func TestGetAddressCount(t *testing.T) {
expectedCount *big.Int
}{
{
name: "Return the count of all distinct host addresses in a common IPv4 CIDR",
name: "Return the count of all addresses in a common IPv4 CIDR",
cidr: IPv4CIDR,
expectedCount: big.NewInt(65536),
},
{
name: "Return the count of all distinct host addresses in a common IPv6 CIDR",
name: "Return the count of all addresses in a common IPv6 CIDR",
cidr: IPv6CIDR,
expectedCount: big.NewInt(4194304),
},
{
name: "Return the count of all distinct host addresses in an uncommon (large prefix) IPv4 CIDR",
name: "Return the count of all addresses in an uncommon (large prefix) IPv4 CIDR",
cidr: largeIPv4PrefixCIDR,
expectedCount: big.NewInt(2),
},
{
name: "Return the count of all distinct host addresses in an uncommon (largest prefix) IPv4 CIDR",
name: "Return the count of all addresses in an uncommon (largest prefix) IPv4 CIDR",
cidr: largestIPv4PrefixCIDR,
expectedCount: big.NewInt(1),
},
Expand Down

0 comments on commit ae71189

Please sign in to comment.