Functional Weave
Code in TypeScript

net.cidr@1.0.0

README.md

1,839 bytes · view raw

# 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).