Zero-dependency Rust library for IPv4/IPv6 networks and MAC addresses — with first-class support for non-contiguous subnet masks.
Most networking libraries assume subnet masks are contiguous (a run of 1-bits followed by 0-bits, like 255.255.255.0). Real-world network hardware — routers, firewalls, packet classifiers — often uses non-contiguous masks like 255.0.255.0 or even 170.85.170.85 to match on arbitrary bit patterns.
netip treats every network as a (address, mask) pair where the mask can be any bit pattern. All operations — containment, intersection, difference, iteration — work correctly with non-contiguous masks.
If your masks happen to be contiguous, the Contiguous<T> wrapper gives you a compile-time guarantee and CIDR prefix access.
- Non-contiguous mask support — the only open-source Rust library that handles arbitrary subnet masks
- Full set algebra —
contains,intersection,difference,merge,is_adjacenton networks as sets of addresses - Correct iteration — iterate over addresses in a non-contiguous network in host-index order
- Zero dependencies — only
core, nothing else - Performance-first —
const fnwhere possible,#[inline]on hot paths, minimal instruction count - Type-safe —
Contiguous<T>enforces contiguous masks at parse time; mismatched IPv4/IPv6 operations are compile errors
Add to your Cargo.toml:
[dependencies]
netip = "0.3"use netip::{Ipv4Network, IpNetwork};
// Standard CIDR notation.
let net: Ipv4Network = Ipv4Network::parse("10.0.0.0/8").unwrap();
assert_eq!(net.prefix(), Some(8));
// Dotted-mask notation — including non-contiguous masks.
let net = Ipv4Network::parse("192.168.0.0/255.0.255.0").unwrap();
assert!(!net.is_contiguous());
// IpNetwork parses both IPv4 and IPv6.
let net: IpNetwork = IpNetwork::parse("2001:db8::/32").unwrap();Networks are sets of addresses. netip gives you the algebra:
use netip::Ipv4Network;
let a = Ipv4Network::parse("10.0.0.0/255.0.0.0").unwrap();
let b = Ipv4Network::parse("10.1.0.0/255.255.0.0").unwrap();
// Containment: is every address in b also in a?
assert!(a.contains(&b));
// Intersection: the set of addresses in both networks.
let ab = a.intersection(&b).unwrap();
assert_eq!(ab, b);
// Difference: addresses in a but not in b — returns an iterator of networks.
for net in a.difference(&b) {
println!("{net}"); // each chunk of the remaining address space
}This is where netip shines. Non-contiguous masks let you express bit-pattern matching that CIDR cannot:
use netip::Ipv4Network;
// Match all addresses where the first and third octets are 10 and 0.
// Second and fourth octets can be anything.
let pattern = Ipv4Network::parse("10.0.0.0/255.0.255.0").unwrap();
let specific = Ipv4Network::parse("10.42.0.99/255.255.255.255").unwrap();
assert!(pattern.contains(&specific)); // 10.42.0.99 matches 10.*.0.*
// Intersection of two non-contiguous networks — always a single network.
let a = Ipv4Network::parse("10.0.0.0/255.0.255.0").unwrap();
let b = Ipv4Network::parse("10.0.5.0/255.255.0.255").unwrap();
let ab = a.intersection(&b).unwrap();
assert_eq!(ab.to_string(), "10.0.5.0/255.255.255.255");
// Iterate over all addresses in a non-contiguous network.
let small = Ipv4Network::parse("192.168.0.0/255.255.255.252").unwrap();
let addrs: Vec<_> = small.addrs().collect();
assert_eq!(addrs.len(), 4);When you need the CIDR guarantee, Contiguous<T> enforces it at parse time:
use netip::Contiguous;
use netip::Ipv4Network;
let net: Contiguous<Ipv4Network> = Contiguous::parse("10.0.0.0/8").unwrap();
assert_eq!(net.prefix(), 8); // guaranteed to succeed — no Option
// Non-contiguous mask is rejected at parse time.
assert!(Contiguous::<Ipv4Network>::parse("10.0.0.0/255.0.255.0").is_err());
// Contiguous<T> derefs to T — all network methods are available.
assert!(net.contains(&Ipv4Network::parse("10.1.2.3/32").unwrap()));use netip::MacAddr;
let mac = MacAddr::parse("aa:bb:cc:dd:ee:ff").unwrap();
assert_eq!(mac.to_string(), "aa:bb:cc:dd:ee:ff");| Type | Description |
|---|---|
Ipv4Network |
IPv4 network — (address, mask) pair, arbitrary masks |
Ipv6Network |
IPv6 network — (address, mask) pair, arbitrary masks |
IpNetwork |
Enum over Ipv4Network / Ipv6Network |
Contiguous<T> |
Newtype that enforces contiguous masks at parse time |
MacAddr |
MAC address (EUI-48), stored as lower 48 bits of u64 |
netip is on the path to a stable 1.0. Current version: 0.3.0.
v0.3 — API cleanup:✓#[must_use],#[deny(missing_docs)]- v0.4 — Complete set algebra:
complement,Contiguous<T>merge/adjacent - v0.5 — Testing hardening: fuzz targets, exhaustive non-contiguous edge cases
- v0.6+ — Documentation polish, API review, CHANGELOG
- v1.0 — Stable release
Apache-2.0