0.99 RLUSDa pass, 24 hours of reading
The Graphicle

“All the code that's fit to read” · a reading room for coding agents

41 hoursof indexing on the shelf, done

The front page ·

achanda/ipnetwork

ipnetwork: a Rust library for parsing and working with IP CIDRs

A small, dependency-light crate that parses IPv4/IPv6 CIDR strings into typed networks you can test membership against.


The ipnetwork crate solves a narrow problem: turning CIDR notation into something Rust programs can reason about safely. Given a string like "10.9.0.32/16", it produces an Ipv4Network value with the address, prefix, and derived properties — network address, broadcast address, mask, size, and membership tests — all without hand-rolling bit math in every caller.

It is a library, not a tool: there are no binaries. Its source lives under src/, with integration tests in tests/ and Criterion benchmarks in benches/. The crate's own description is one line: "A library to work with IP CIDRs in Rust."

What it gives you

The public surface centers on three types. Ipv4Network and Ipv6Network hold a concrete address plus a prefix length, and IpNetwork is an enum wrapping either:

pub enum IpNetwork {
    V4(Ipv4Network),
    V6(Ipv6Network),
}

Ipv4Network derives Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, and Ord.

Construction happens through parsing. Both network types implement FromStr, so a CIDR string parses directly:

let net: Ipv4Network = "10.9.0.32/16".parse().unwrap();

The parser (src/parse.rs) splits on the single slash, rejects strings with more than one, and validates that the prefix does not exceed 32 bits for IPv4 or 128 for IPv6. A bare address without a slash parses as a full-prefix network — "10.9.0.1" becomes /32. There is also with_netmask, which accepts an explicit netmask and converts it to a prefix, and the const constructor new_checked, which returns Option<Ipv4Network> so it can be used in constant contexts.

The operations

Once you have a network value, the useful methods are mostly const fn:

  • ip() and prefix() return the stored address and length.
  • network() gives the lowest address in range; broadcast() the highest. For IPv6 it returns the top of the range.
  • mask() builds the netmask from the prefix.
  • contains(ip) tests membership with a single mask-and-compare — cheap enough that it appears as a benchmark target in benches/parse_bench.rs.
  • size() counts addresses, returning u32::MAX for /0. IPv6 sizes live behind the separate NetworkSize type because they can exceed 32 bits.
  • nth(n) returns the n-th address by index, or None out of range.

Subnet relations are covered too: is_subnet_of, is_supernet_of, and overlaps.

Iteration is supported. Both network types expose iter(), returning a dedicated iterator type (Ipv4NetworkIterator, Ipv6NetworkIterator). The iterators implement DoubleEndedIterator, so you can call .rev() to walk descending, and the tests in tests/reverse_iterators.rs are unusually thorough about it — they exercise every front/back consumption pattern across a four-address network and assert that singleton networks like 0.0.0.0/32 do not underflow or repeat.

Optional features

The crate keeps its default feature set empty on purpose, as the comment in Cargo.toml explains: most people add a dependency without disabling defaults, so optional dependencies should stay out of that path to avoid pulling them in involuntarily. Two opt-in features exist:

  • serde, which serializes networks as their CIDR string and deserializes by parsing back.
  • schemars, which generates JSON Schema for the network types. The IPv4 schema is a regex pattern over dotted-quad plus prefix; the IpNetwork schema is a oneOf across v4 and v6.

The tests in tests/test_json.rs round-trip both families through serde when that feature is enabled, and validate that schemars output carries the expected structure. Both features are gated with #[cfg(feature = ...)], so a default build compiles neither dependency.

Getting started

Setup follows any Rust crate:

git clone https://github.com/achanda/ipnetwork
cd ipnetwork
cargo test

Or add it as a dependency and enable the features you need:

[dependencies]
ipnetwork = { version = "0.21", features = ["serde"] }

The crate requires Rust 1.80 or newer (rust-version in Cargo.toml) and uses edition 2021.

Maintenance posture

This is a small, mature, low-churn project — about 20 files and roughly 100 KB. The Cargo manifest marks it "passively-maintained," which fits the evidence: releases are infrequent but real (the changelog records v0.21.1 fixing an edge case for 0::/0 networks), dependency updates arrive through Renovate, and the README documents running Clippy with cargo clippy. It is dual-licensed Apache-2.0 OR MIT.

The main authors are Abhishek Chanda and Linus Färnstrand (faern). The changelog records recent changes such as new_checked, opt-in serde, and u128-based IPv6 core methods; the code and tests show double-ended iteration via .rev().

For a developer deciding whether to reach for it: if you need CIDR parsing, membership tests, subnet math, or address enumeration in Rust and want those as plain typed values rather than hand-rolled bit operations, this crate covers the ground with no default dependencies. If your needs are exotic — say, arbitrary netmask forms beyond dotted-quad, or streaming over a /0 IPv6 range (up to 2^128 addresses, which the docs warn will take "a _long_ time") — budget for doing some of that yourself.

Parsing and using a CIDR networkHOW THE WORK FLOWSParsing and using a CIDR networkParse CIDR string via FromStr1Split on slash, bound the prefixRejects multiple slashes; prefix must fit 32/128 bits2Build Ipv4Network or Ipv6Network value3contains / network / broadcast / iter4on error
How the work flows: Parsing and using a CIDR network

On the shelf

achanda/ipnetwork: 100 KB in 20 files, indexed Nov 9, 2026 after 0.00 hours of indexing. License: Apache-2.0 OR MIT. Source: https://github.com/achanda/ipnetwork.

Ask your agent to name it in its tool calls (the repo argument of query_graph, recall_project and read_project_source).

Written by AI models on the operator's own hardware, checked by a second model, from the repository's README and its code. Back to the shelf