# net.cidr
A subnet calculator as five functions that only make sense together:
describe an IPv4 block (`cidrInfo`), test membership for IPv4 or IPv6
(`cidrContains`), check a block without raising (`isCidr`), and convert
between prefix lengths and dotted netmasks.
## Decisions
- **Host bits are allowed and dropped.** `192.168.1.77/24` is how an interface
address is written, and it describes `192.168.1.0/24`; `cidrInfo` returns
the cleaned-up block in `cidr`. (Python's `ipaddress.ip_network` refuses it
unless `strict=False`; this behaves like `strict=False`.)
- **/31 and /32.** A /31 is a point-to-point link: RFC 3021 makes both
addresses usable, so `hostCount` is 2, the hosts are the two addresses and
`broadcast` is null. A /32 is one host, also with no broadcast. Everything
from /0 to /30 has network and broadcast addresses reserved, so
`hostCount` is `addressCount - 2`.
- **Families do not mix.** `cidrContains("10.0.0.0/8", "::ffff:10.0.0.1")` is
false: an IPv4-mapped IPv6 address is a different address as far as a socket
is concerned. Convert it first if you mean the IPv4 address.
- **Strict text.** Addresses follow `net.ipv4` and `net.ipv6` (no leading
zeros, no zone ids); the prefix is ASCII decimal with no leading zero, 0-32
for IPv4 and 0-128 for IPv6.
- `cidrInfo` is IPv4 only: an IPv6 /64 has 2^64 addresses, which no language
here holds in an `int`, and "broadcast" has no IPv6 meaning.
- Netmasks must be contiguous: `255.0.255.0` is refused, and so is a
wildcard mask such as `0.0.0.255` (write the netmask, `255.255.255.0`).
`cidrBlock` (`cidr_block`) is exported for the group's own functions; it is
not part of the contract.
Sources: RFC 4632 (CIDR) section 3.1; RFC 3021 (Using 31-Bit Prefixes on IPv4
Point-to-Point Links); RFC 4291 section 2.3 (IPv6 prefix notation).