cidr-utils
A Rust crate for combining, separating, and iterating IPv4 and IPv6 CIDR blocks without hand-rolled bit math.
Repository Health
Technical Analysis
cidr-utils extends the cidr crate’s Ipv4Cidr/Ipv6Cidr types with three focused capabilities: merging adjacent networks into their smallest common supernetwork, evenly slicing a network into a target number of subnetworks (or into subnetworks of a target prefix length), and iterating every address a CIDR block contains as raw bytes, integers, or Ipv4Addr/Ipv6Addr values.
The crate is organized as three independently feature-gated modules — combiner, separator, and iterator — so a consumer that only needs one capability doesn’t pull in the others. Ipv4CidrCombiner and Ipv6CidrCombiner keep a sorted, non-overlapping vector of CIDRs and merge them incrementally as blocks are pushed, using prefix-XOR checks to detect and fold sibling blocks into their parent. Ipv4CidrSeparator/Ipv6CidrSeparator handle the inverse problem: dividing a block into a requested count of roughly-equal pieces, or into fixed-size subnetworks at a given prefix length.
IPv6 support uses num-bigint’s BigUint for size and arithmetic since a /0 IPv6 network’s address count overflows any native integer type, while IPv4 sizes stay in a plain u64. The crate has been stable on crates.io since 2019 and is used as a building block by networking and firewall-adjacent Rust tooling that needs correct CIDR aggregation and subnetting without re-deriving the bit manipulation from scratch.
What You Get
Ipv4CidrCombiner/Ipv6CidrCombiner— push individual CIDRs or addresses and get back the minimal sorted set of supernetworks that cover them, with automatic sibling-block mergingIpv4CidrSeparator::divide_by/Ipv6CidrSeparator::divide_by— split a CIDR into a requested number of roughly equal-sized subnetwork groupsIpv4CidrSeparator::sub_networks/Ipv6CidrSeparator::sub_networks— split a CIDR into fixed-size subnetworks at an exact target prefix lengthIpv4CidrIterator,Ipv4CidrIpv4AddrIterator, and their IPv6 counterparts — double-ended iterators over every address in a block, yieldingu32/u128, raw byte arrays, orIpv4Addr/Ipv6AddrIpv4CidrSize/Ipv6CidrSizetraits — compute the exact address count of a CIDR (u64for IPv4,BigUintfor IPv6) directly off thecidrcrate’s types- Three independent Cargo features (
combiner,separator,iterator) so unused capability isn’t compiled in
Common Use Cases
- Aggregating a list of individual allowed/blocked IPs or small ranges into the smallest possible firewall or ACL rule set
- Splitting a company’s assigned IP block into equal-sized subnets for per-team or per-region allocation
- Enumerating every address in a CIDR block for scanning, provisioning, or test-fixture generation
- Computing exact host counts for capacity planning across large IPv6 allocations where native integers overflow
- Building network configuration tooling (VPC/subnet planners, DHCP range generators) that needs correct CIDR math instead of manual bit-shifting
Under The Hood
Architecture
cidr-utils is organized as three independently feature-gated modules layered on top of the external cidr crate’s Ipv4Cidr/Ipv6Cidr types rather than defining its own CIDR representation: combiner (src/combiner/{v4,v6}.rs) wraps a sorted Vec<Cidr> and merges adjacent blocks on push via prefix-XOR sibling detection; separator (src/separator/{v4,v6}.rs) computes even splits or fixed-prefix subnetting and, per the separator feature’s dependency on combiner, returns Ipv4CidrCombiner/Ipv6CidrCombiner collections from divide_by; iterator (src/iterator/{v4,v6}.rs) provides low-level double-ended byte/int/addr iterators reused by both address-count and enumeration call sites. traits.rs defines the Ipv4CidrSize/Ipv6CidrSize extension traits implemented directly on the cidr crate’s types, which is the one piece of core logic every other module depends on — a change to how size is computed there would ripple through combiner merging math, separator chunk-size calculations, and iterator bounds alike.
Tech Stack
Pure Rust, edition 2021, MSRV 1.81, with a minimal dependency footprint: cidr (0.3) for the underlying IPv4Cidr/Ipv6Cidr types and parsing, num-traits (0.2.11) and num-bigint (0.4) for arbitrary-precision arithmetic on IPv6 address counts. No async runtime, no I/O, no external services — this is a pure computation library intended to be linked into other crates. CI (GitHub Actions, ci.yml/ci-version.yml) runs the full matrix of feature-flag combinations (default, --no-default-features, --features combiner, --features separator) across stable and nightly toolchains on Linux, macOS, and Windows, plus a dedicated MSRV job pinned to 1.81, and runs cargo doc to validate the doctest examples embedded in the README and lib.rs module docs.
Code Quality
Testing is split between integration tests in tests/ (ipv4_cidr_combiner.rs, ipv4_cidr_separator.rs, ipv6_cidr_combiner.rs, ipv6_cidr_separator.rs) exercising combiner/separator behavior against known CIDR inputs and expected outputs, and doctests embedded directly in lib.rs and traits.rs that double as usage documentation and correctness checks — no test files exist yet for the standalone iterator module, an explicit gap. Error handling favors Option-returning APIs (divide_by, sub_networks, nth_u64) over panics for out-of-range or overflow inputs, and several hot paths use unsafe blocks (in the iterator’s next_unchecked/next_back_unchecked) guarded by index bounds checks in the safe wrapper methods that call them. rustfmt.toml is checked in for consistent formatting, and CI enforces the build across the full feature-flag matrix rather than relying on a single default-features test run.
What Makes It Unique
The crate’s main technical distinction is the split between u64-based IPv4 arithmetic and BigUint-based IPv6 arithmetic, driven by the practical fact that IPv6’s 128-bit address space means even moderately-sized CIDR blocks can exceed native integer range — most CIDR-handling code either ignores this and silently overflows on IPv6 or hardcodes u128 and still fails at /0. The Ipv4CidrCombiner’s incremental push-and-merge algorithm, which folds sibling blocks together in-place using prefix-XOR bit tricks rather than re-sorting and re-scanning the whole set on every insertion, is also a more deliberate approach than the naive full-recompute aggregation found in many ad hoc CIDR-merging scripts.