ip_cidr/
lib.rs

1//! IP matching utilities
2
3#![no_std]
4#![warn(missing_docs)]
5#![allow(clippy::style)]
6
7mod parser;
8pub use parser::{parse_ip, ParseError};
9pub mod base;
10pub mod v4;
11pub mod v6;
12
13use core::{fmt, net};
14
15#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
16///CIDR representation
17pub enum Cidr {
18    ///IPv4 block
19    V4(v4::Cidr),
20    ///IPv6 block
21    V6(v6::Cidr),
22}
23
24impl Cidr {
25    ///Number of bits within ipv4 address
26    pub const IPV4_BITS: u8 = v4::BITS_LEN;
27    ///Number of bits within ipv6 address
28    pub const IPV6_BITS: u8 = v6::BITS_LEN;
29
30    #[inline(always)]
31    ///Constructs new CIDR verifying that `prefix` fits provided `addrs`
32    ///
33    ///Returns `None` if `prefix` is greater than address length
34    pub const fn new(addr: net::IpAddr, prefix: u8) -> Option<Self> {
35        match addr {
36            net::IpAddr::V4(addr) => Self::new_v4(addr, prefix),
37            net::IpAddr::V6(addr) => Self::new_v6(addr, prefix),
38        }
39    }
40
41    #[inline]
42    ///Constructs new CIDR verifying that `prefix` fits provided `addrs`
43    ///
44    ///Returns `None` if `prefix` is greater than address length
45    pub const fn new_v4(addr: net::Ipv4Addr, prefix: u8) -> Option<Self> {
46        match v4::Cidr::new(addr, prefix) {
47            Some(cidr) => Some(Self::V4(cidr)),
48            None => None,
49        }
50    }
51
52    #[inline]
53    ///Constructs new CIDR verifying that `prefix` fits provided `addrs`
54    ///
55    ///Returns `None` if `prefix` is greater than address length
56    pub const fn new_v6(addr: net::Ipv6Addr, prefix: u8) -> Option<Self> {
57        match v6::Cidr::new(addr, prefix) {
58            Some(cidr) => Some(Self::V6(cidr)),
59            None => None,
60        }
61    }
62
63    #[inline(always)]
64    ///Returns address
65    pub const fn addr(&self) -> net::IpAddr {
66        match self {
67            Self::V4(cidr) => net::IpAddr::V4(cidr.addr()),
68            Self::V6(cidr) => net::IpAddr::V6(cidr.addr()),
69        }
70    }
71
72    #[inline(always)]
73    ///Returns prefix
74    pub const fn prefix(&self) -> u8 {
75        match self {
76            Self::V4(cidr) => cidr.prefix(),
77            Self::V6(cidr) => cidr.prefix(),
78        }
79    }
80
81    #[inline(always)]
82    ///Computes network address from provided `addr` and `prefix`, which is lowest possible address within CIDR block
83    pub const fn network_addr(&self) -> net::IpAddr {
84        match self {
85            Self::V4(cidr) => net::IpAddr::V4(cidr.network_addr()),
86            Self::V6(cidr) => net::IpAddr::V6(cidr.network_addr()),
87        }
88    }
89
90    #[inline(always)]
91    ///Computes network address from provided `addr` and `prefix`, which is highest possible address within CIDR block
92    pub const fn broadcast_addr(&self) -> net::IpAddr {
93        match self {
94            Self::V4(cidr) => net::IpAddr::V4(cidr.broadcast_addr()),
95            Self::V6(cidr) => net::IpAddr::V6(cidr.broadcast_addr()),
96        }
97    }
98
99    #[inline(always)]
100    ///Returns maximum number of addresses within the block
101    pub const fn size(&self) -> u128 {
102        match self {
103            Self::V4(cidr) => cidr.size() as _,
104            Self::V6(cidr) => cidr.size(),
105        }
106    }
107
108    #[inline(always)]
109    ///Checks if a given `addr` is contained within `self`
110    pub const fn contains(&self, addr: net::IpAddr) -> bool {
111        match (self, addr) {
112            (Self::V4(cidr), net::IpAddr::V4(addr)) => cidr.contains(addr),
113            (Self::V6(cidr), net::IpAddr::V6(addr)) => cidr.contains(addr),
114            _ => false,
115        }
116    }
117
118    #[inline(always)]
119    ///Attempts to fetch address by `idx` within the block `self`
120    pub const fn get(&self, idx: u128) -> Option<net::IpAddr> {
121        match self {
122            Self::V4(cidr) => match cidr.get(idx as u32) {
123                Some(ip) => Some(net::IpAddr::V4(ip)),
124                None => None,
125            }
126            Self::V6(cidr) => match cidr.get(idx) {
127                Some(ip) => Some(net::IpAddr::V6(ip)),
128                None => None,
129            }
130        }
131    }
132
133    #[inline(always)]
134    ///Returns address corresponding `idx` without checking size according to the prefix
135    ///
136    ///This is safe in a sense as it is only wrapping math operation, but it should be only used
137    ///when you know need to iterate over possible addresses by pre-computing size
138    pub const fn get_unchecked(&self, idx: u128) -> net::IpAddr {
139        match self {
140            Self::V4(cidr) => net::IpAddr::V4(cidr.get_unchecked(idx as u32)),
141            Self::V6(cidr) => net::IpAddr::V6(cidr.get_unchecked(idx)),
142        }
143    }
144}
145
146impl fmt::Display for Cidr {
147    #[inline(always)]
148    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
149        match self {
150            Self::V4(cidr) => fmt::Display::fmt(cidr, fmt),
151            Self::V6(cidr) => fmt::Display::fmt(cidr, fmt),
152        }
153    }
154}
155
156#[inline]
157///Parses [Cidr](enum.Cidr.html) from the input `text`
158///
159///Returning `Err` if string contains invalid IP or CIDR's prefix
160///
161///Returns `Ok(None)` if `text` is valid CIDR but `prefix` overflows
162///
163///If `prefix` is missing, prefix is assumed to be only for single IP:
164///- In case of IPv4 it means prefix is assumed to be 32
165///- In case of IPv6 it means prefix is assumed to be 128
166pub const fn parse_cidr(text: &str) -> Result<Option<Cidr>, parser::ParseError<'_>> {
167    match parse_ip(text) {
168        Ok((net::IpAddr::V4(addr), None)) => Ok(Some(Cidr::V4(v4::Cidr::new_single(addr)))),
169        Ok((net::IpAddr::V4(addr), Some(prefix))) => Ok(Cidr::new_v4(addr, prefix)),
170        Ok((net::IpAddr::V6(addr), None)) => Ok(Some(Cidr::V6(v6::Cidr::new_single(addr)))),
171        Ok((net::IpAddr::V6(addr), Some(prefix))) => Ok(Cidr::new_v6(addr, prefix)),
172        Err(error) => Err(error)
173    }
174}