v9.1.1
Menu

CIDR Subnet Report

easyFREENetworking & AddressingSystems & InfrastructureML Systems & Infrastructure 1

Problem Statement

Before a VPC exists you have to size its subnets, and a /24 that "looks big enough" is how a training cluster runs out of addresses mid-scale-up. Given a CIDR block, derive its network address, broadcast address, netmask and usable host count, and say which of a list of IPs fall inside it.

Background

An IPv4 address is 32 bits. A CIDR block A.B.C.D/p fixes the leading p bits (the network part) and leaves 32 - p host bits free. The netmask is p ones followed by 32 - p zeros.

  • Network address: the address with all host bits 0 — ip & mask. Note the address you are given need not be the network address: 10.0.5.37/22 still describes the block 10.0.4.0/22.
  • Broadcast address: all host bits 1.
  • Membership: ip is inside the block exactly when ip & mask == network.
  • Usable hosts: 2 ** (32 - p) - 2 for p <= 30, since the network and broadcast addresses are not assignable. Two special cases: a /31 has 2 usable addresses (RFC 3021 point-to-point links) and a /32 has 1. Blindly subtracting 2 gives 0 and -1 there.

Solve this with integer bit arithmetic — do not use the ipaddress module.

Your Task

Implement:

def subnet_report(cidr, ips):

Return a dict with keys, in this order: "network", "broadcast", "netmask" (all dotted-quad strings), "usable_hosts" (int), and "in_subnet" (list of bools, one per entry of ips, in order).

Input Format

  • cidr: string like "10.0.5.37/22".
  • ips: list of dotted-quad strings to test.

Output Format

  • The dict described above.

Sample

print(subnet_report("10.0.5.37/22", ["10.0.4.9", "10.0.8.1", "10.0.7.255"]))

Output:

{'network': '10.0.4.0', 'broadcast': '10.0.7.255', 'netmask': '255.255.252.0', 'usable_hosts': 1022, 'in_subnet': [True, False, True]}

A /22 borrows 2 bits from the third octet, so the block spans 10.0.4.0-10.0.7.255: 1024 addresses, 1022 assignable.

Example

Input
print(subnet_report("10.0.5.37/22", ["10.0.4.9", "10.0.8.1", "10.0.7.255"]))
Output
{'network': '10.0.4.0', 'broadcast': '10.0.7.255', 'netmask': '255.255.252.0', 'usable_hosts': 1022, 'in_subnet': [True, False, True]}
Reasoning
  • cidr is "10.0.5.37/22": the prefix p=22p = 22 fixes the first 22 of the 32 bits as the network part, leaving 32−22=1032 - 22 = 10 host bits (2 in the third octet, 8 in the fourth).
  • The netmask is 22 ones then 10 zeros. The first two octets are 255, the third is 111111002=25211111100_2 = 252, the fourth is 0, giving 255.255.252.0.
  • Zero the host bits of 10.0.5.37: the third octet 5=0000010125 = 00000101_2 keeps only its top 6 bits, becoming 000001002=400000100_2 = 4, and the fourth octet becomes 0. The network is 10.0.4.0.
  • Set every host bit to 1 instead: the third octet becomes 4+3=74 + 3 = 7 and the fourth 255255, so the broadcast is 10.0.7.255.
  • The block holds 210=10242^{10} = 1024 addresses; removing network and broadcast leaves 1022.
  • Membership compares the network bits: 10.0.4.9 and 10.0.7.255 both reduce to third octet 4 (inside), but 10.0.8.1 has 8=0000100028 = 00001000_2, which stays 8, a different block. Notice the broadcast address itself still counts as inside, giving [True, False, True].

Constraints

  • cidr is a valid IPv4 block, prefix length 0 to 32
  • The given address may have host bits set; normalise it to the network address
  • Usable hosts: 2 ** (32 - p) - 2 for p <= 30, 2 for a /31, 1 for a /32
  • 0 <= len(ips) <= 100
  • Use integer bit arithmetic, not the ipaddress module

Learn this in

More from ML Systems & Infrastructure 1

solution.py

Test results

Sign in to run your code against the test cases.