Skip to main content

ipnetwork/
lib.rs

1//! The `ipnetwork` crate provides a set of APIs to work with IP CIDRs in
2//! Rust.
3#![crate_type = "lib"]
4#![deny(
5    missing_debug_implementations,
6    unsafe_code,
7    unused_extern_crates,
8    unused_import_braces
9)]
10
11use std::{
12    convert::TryFrom,
13    fmt,
14    net::{IpAddr, Ipv4Addr, Ipv6Addr},
15    str::FromStr,
16};
17
18mod error;
19mod ipv4;
20mod ipv6;
21mod parse;
22mod size;
23
24pub use crate::{
25    error::{IpNetworkError, NetworkSizeError},
26    ipv4::{ipv4_mask_to_prefix, ipv4_mask_to_prefix_checked, Ipv4Network, Ipv4NetworkIterator},
27    ipv6::{ipv6_mask_to_prefix, ipv6_mask_to_prefix_checked, Ipv6Network, Ipv6NetworkIterator},
28    size::NetworkSize,
29};
30
31/// Represents a generic network range. This type can have two variants:
32/// the v4 and the v6 case.
33#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
34pub enum IpNetwork {
35    V4(Ipv4Network),
36    V6(Ipv6Network),
37}
38
39#[cfg(feature = "serde")]
40impl<'de> serde::Deserialize<'de> for IpNetwork {
41    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
42    where
43        D: serde::Deserializer<'de>,
44    {
45        let s = <String>::deserialize(deserializer)?;
46        IpNetwork::from_str(&s).map_err(serde::de::Error::custom)
47    }
48}
49
50#[cfg(feature = "serde")]
51impl serde::Serialize for IpNetwork {
52    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
53    where
54        S: serde::Serializer,
55    {
56        serializer.collect_str(self)
57    }
58}
59
60#[cfg(feature = "schemars")]
61impl schemars::JsonSchema for IpNetwork {
62    fn schema_name() -> std::borrow::Cow<'static, str> {
63        std::borrow::Cow::Borrowed("IpNetwork")
64    }
65
66    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
67        schemars::json_schema!({
68            "oneOf": [
69                {
70                    "title": "v4",
71                    "allOf": [generator.subschema_for::<Ipv4Network>()]
72                },
73                {
74                    "title": "v6",
75                    "allOf": [generator.subschema_for::<Ipv6Network>()]
76                }
77            ],
78            "x-rust-type": "ipnetwork::IpNetwork"
79        })
80    }
81}
82
83impl IpNetwork {
84    /// Constructs a new `IpNetwork` from a given `IpAddr` and a prefix denoting the
85    /// network size. If the prefix is larger than 32 (for IPv4) or 128 (for IPv6), this
86    /// will raise an `IpNetworkError::InvalidPrefix` error. Support for IPv6 is not
87    /// complete yet.
88    pub fn new(ip: IpAddr, prefix: u8) -> Result<IpNetwork, IpNetworkError> {
89        match ip {
90            IpAddr::V4(a) => Ok(IpNetwork::V4(Ipv4Network::new(a, prefix)?)),
91            IpAddr::V6(a) => Ok(IpNetwork::V6(Ipv6Network::new(a, prefix)?)),
92        }
93    }
94
95    /// Constructs a new `IpNetwork` from a network address and a network mask.
96    ///
97    /// If the netmask is not valid this will return an `IpNetworkError::InvalidPrefix`.
98    pub fn with_netmask(netaddr: IpAddr, netmask: IpAddr) -> Result<Self, IpNetworkError> {
99        let prefix = ip_mask_to_prefix(netmask)?;
100        Self::new(netaddr, prefix)
101    }
102
103    /// Returns the IP part of a given `IpNetwork`
104    pub const fn ip(&self) -> IpAddr {
105        match *self {
106            IpNetwork::V4(ref a) => IpAddr::V4(a.ip()),
107            IpNetwork::V6(ref a) => IpAddr::V6(a.ip()),
108        }
109    }
110
111    /// Returns the prefix of the given `IpNetwork`
112    ///
113    /// # Example
114    /// ```
115    /// use ipnetwork::IpNetwork;
116    ///
117    /// assert_eq!(IpNetwork::V4("10.9.0.1".parse().unwrap()).prefix(), 32u8);
118    /// assert_eq!(IpNetwork::V4("10.9.0.32/16".parse().unwrap()).prefix(), 16u8);
119    ///
120    /// assert_eq!(IpNetwork::V6("ff01::0".parse().unwrap()).prefix(), 128u8);
121    /// assert_eq!(IpNetwork::V6("ff01::0/32".parse().unwrap()).prefix(), 32u8);
122    /// ```
123    pub const fn prefix(&self) -> u8 {
124        match *self {
125            IpNetwork::V4(ref a) => a.prefix(),
126            IpNetwork::V6(ref a) => a.prefix(),
127        }
128    }
129
130    /// Returns the address of the network denoted by this `IpNetwork`.
131    /// This means the lowest possible IP address inside of the network.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use std::net::{Ipv4Addr, Ipv6Addr};
137    /// use ipnetwork::IpNetwork;
138    ///
139    /// let net: IpNetwork = "10.1.9.32/16".parse().unwrap();
140    /// assert_eq!(net.network(), Ipv4Addr::new(10, 1, 0, 0));
141    /// let net: IpNetwork = "2001:db8::/96".parse().unwrap();
142    /// assert_eq!(net.network(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
143    /// ```
144    pub const fn network(&self) -> IpAddr {
145        match *self {
146            IpNetwork::V4(ref a) => IpAddr::V4(a.network()),
147            IpNetwork::V6(ref a) => IpAddr::V6(a.network()),
148        }
149    }
150
151    /// Returns the broadcasting address of this `IpNetwork`.
152    /// This means the highest possible IP address inside of the network.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// use std::net::Ipv4Addr;
158    /// use ipnetwork::{IpNetwork, Ipv4Network};
159    ///
160    /// let net: Ipv4Network = "10.9.0.32/16".parse().unwrap();
161    /// assert_eq!(net.broadcast(), Ipv4Addr::new(10, 9, 255, 255));
162    /// ```
163    pub const fn broadcast(&self) -> IpAddr {
164        match *self {
165            IpNetwork::V4(ref a) => IpAddr::V4(a.broadcast()),
166            IpNetwork::V6(ref a) => IpAddr::V6(a.broadcast()),
167        }
168    }
169
170    /// Returns the mask for this `IpNetwork`.
171    /// That means the `prefix` most significant bits will be 1 and the rest 0
172    ///
173    /// # Example
174    ///
175    /// ```
176    /// use ipnetwork::IpNetwork;
177    /// use std::net::{Ipv4Addr, Ipv6Addr};
178    ///
179    /// let v4_net: IpNetwork = "10.9.0.1".parse().unwrap();
180    /// assert_eq!(v4_net.mask(), Ipv4Addr::new(255, 255, 255, 255));
181    /// let v4_net: IpNetwork = "10.9.0.32/16".parse().unwrap();
182    /// assert_eq!(v4_net.mask(), Ipv4Addr::new(255, 255, 0, 0));
183    ///
184    /// let v6_net: IpNetwork = "ff01::0".parse().unwrap();
185    /// assert_eq!(v6_net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff));
186    /// let v6_net: IpNetwork = "ff01::0/32".parse().unwrap();
187    /// assert_eq!(v6_net.mask(), Ipv6Addr::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0));
188    /// ```
189    pub const fn mask(&self) -> IpAddr {
190        match *self {
191            IpNetwork::V4(ref a) => IpAddr::V4(a.mask()),
192            IpNetwork::V6(ref a) => IpAddr::V6(a.mask()),
193        }
194    }
195
196    /// Returns true if the IP in this `IpNetwork` is a valid IPv4 address,
197    /// false if it's a valid IPv6 address.
198    ///
199    /// # Example
200    ///
201    ///```
202    /// use ipnetwork::IpNetwork;
203    ///
204    /// let v4: IpNetwork = IpNetwork::V4("10.9.0.32/16".parse().unwrap());
205    /// assert_eq!(v4.is_ipv4(), true);
206    /// assert_eq!(v4.is_ipv6(), false);
207    ///```
208    pub const fn is_ipv4(&self) -> bool {
209        match *self {
210            IpNetwork::V4(_) => true,
211            IpNetwork::V6(_) => false,
212        }
213    }
214
215    /// Returns true if the IP in this `IpNetwork` is a valid IPv6 address,
216    /// false if it's a valid IPv4 address.
217    ///
218    /// # Example
219    ///
220    ///```
221    /// use ipnetwork::IpNetwork;
222    ///
223    /// let v6: IpNetwork = IpNetwork::V6("ff01::0/32".parse().unwrap());
224    /// assert_eq!(v6.is_ipv6(), true);
225    /// assert_eq!(v6.is_ipv4(), false);
226    ///```
227    pub const fn is_ipv6(&self) -> bool {
228        match *self {
229            IpNetwork::V4(_) => false,
230            IpNetwork::V6(_) => true,
231        }
232    }
233
234    // TODO(abhishek) when TryFrom is stable, implement it for IpNetwork to
235    // variant conversions. Then use that to implement a generic is_subnet_of
236    // is_supernet_of, overlaps
237
238    /// Checks if a given `IpAddr` is in this `IpNetwork`
239    ///
240    /// # Examples
241    ///
242    /// ```
243    /// use std::net::IpAddr;
244    /// use ipnetwork::IpNetwork;
245    ///
246    /// let net: IpNetwork = "127.0.0.0/24".parse().unwrap();
247    /// let ip1: IpAddr = "127.0.0.1".parse().unwrap();
248    /// let ip2: IpAddr = "172.0.0.1".parse().unwrap();
249    /// let ip4: IpAddr = "::1".parse().unwrap();
250    /// assert!(net.contains(ip1));
251    /// assert!(!net.contains(ip2));
252    /// assert!(!net.contains(ip4));
253    /// ```
254    #[inline]
255    pub const fn contains(&self, ip: IpAddr) -> bool {
256        match (*self, ip) {
257            (IpNetwork::V4(net), IpAddr::V4(ip)) => net.contains(ip),
258            (IpNetwork::V6(net), IpAddr::V6(ip)) => net.contains(ip),
259            _ => false,
260        }
261    }
262
263    /// Returns the number of possible host addresses in this `IpAddr`
264    ///
265    /// # Examples
266    ///
267    /// ```
268    /// use ipnetwork::{IpNetwork, NetworkSize};
269    ///
270    ///
271    /// let net: IpNetwork = "127.0.0.0/24".parse().unwrap();
272    /// assert_eq!(net.size(), NetworkSize::V4(256))
273    /// ```
274    pub fn size(&self) -> NetworkSize {
275        match *self {
276            IpNetwork::V4(ref ip) => NetworkSize::V4(ip.size()),
277            IpNetwork::V6(ref ip) => NetworkSize::V6(ip.size()),
278        }
279    }
280
281    /// Returns an iterator over the addresses contained in the network.
282    ///
283    /// This lists all the addresses in the network range, in ascending order.
284    pub fn iter(&self) -> IpNetworkIterator {
285        let inner = match self {
286            IpNetwork::V4(ip) => IpNetworkIteratorInner::V4(ip.iter()),
287            IpNetwork::V6(ip) => IpNetworkIteratorInner::V6(ip.iter()),
288        };
289        IpNetworkIterator {
290            inner,
291        }
292    }
293}
294
295/// Tries to parse the given string into a `IpNetwork`. Will first try to parse
296/// it as an `Ipv4Network` and if that fails as an `Ipv6Network`. If both
297/// fails it will return an `InvalidAddr` error.
298///
299/// # Examples
300///
301/// ```
302/// use std::net::Ipv4Addr;
303/// use ipnetwork::{IpNetwork, Ipv4Network};
304///
305/// let expected = IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(10, 1, 9, 32), 16).unwrap());
306/// let from_cidr: IpNetwork = "10.1.9.32/16".parse().unwrap();
307/// assert_eq!(expected, from_cidr);
308/// ```
309impl FromStr for IpNetwork {
310    type Err = IpNetworkError;
311    fn from_str(s: &str) -> Result<Self, Self::Err> {
312        if let Ok(net) = Ipv4Network::from_str(s) {
313            Ok(IpNetwork::V4(net))
314        } else if let Ok(net) = Ipv6Network::from_str(s) {
315            Ok(IpNetwork::V6(net))
316        } else {
317            Err(IpNetworkError::InvalidAddr(s.to_string()))
318        }
319    }
320}
321
322impl TryFrom<&str> for IpNetwork {
323    type Error = IpNetworkError;
324
325    fn try_from(s: &str) -> Result<Self, Self::Error> {
326        IpNetwork::from_str(s)
327    }
328}
329
330impl From<Ipv4Network> for IpNetwork {
331    fn from(v4: Ipv4Network) -> IpNetwork {
332        IpNetwork::V4(v4)
333    }
334}
335
336impl From<Ipv6Network> for IpNetwork {
337    fn from(v6: Ipv6Network) -> IpNetwork {
338        IpNetwork::V6(v6)
339    }
340}
341
342impl From<Ipv4Addr> for IpNetwork {
343    fn from(addr: Ipv4Addr) -> IpNetwork {
344        IpNetwork::V4(Ipv4Network::from(addr))
345    }
346}
347
348impl From<Ipv6Addr> for IpNetwork {
349    fn from(addr: Ipv6Addr) -> IpNetwork {
350        IpNetwork::V6(Ipv6Network::from(addr))
351    }
352}
353
354impl From<IpAddr> for IpNetwork {
355    fn from(addr: IpAddr) -> IpNetwork {
356        match addr {
357            IpAddr::V4(a) => IpNetwork::from(a),
358            IpAddr::V6(a) => IpNetwork::from(a),
359        }
360    }
361}
362
363impl fmt::Display for IpNetwork {
364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365        match *self {
366            IpNetwork::V4(net) => net.fmt(f),
367            IpNetwork::V6(net) => net.fmt(f),
368        }
369    }
370}
371
372#[derive(Clone, Debug)]
373enum IpNetworkIteratorInner {
374    V4(Ipv4NetworkIterator),
375    V6(Ipv6NetworkIterator),
376}
377
378#[derive(Clone, Debug)]
379pub struct IpNetworkIterator {
380    inner: IpNetworkIteratorInner,
381}
382
383impl Iterator for IpNetworkIterator {
384    type Item = IpAddr;
385    fn next(&mut self) -> Option<IpAddr> {
386        match &mut self.inner {
387            IpNetworkIteratorInner::V4(iter) => iter.next().map(IpAddr::V4),
388            IpNetworkIteratorInner::V6(iter) => iter.next().map(IpAddr::V6),
389        }
390    }
391    fn size_hint(&self) -> (usize, Option<usize>) {
392        match &self.inner {
393            IpNetworkIteratorInner::V4(iter) => iter.size_hint(),
394            IpNetworkIteratorInner::V6(iter) => iter.size_hint(),
395        }
396    }
397}
398
399impl IntoIterator for &'_ IpNetwork {
400    type IntoIter = IpNetworkIterator;
401    type Item = IpAddr;
402    fn into_iter(self) -> IpNetworkIterator {
403        self.iter()
404    }
405}
406
407/// Converts a `IpAddr` network mask into a prefix.
408/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
409pub fn ip_mask_to_prefix(mask: IpAddr) -> Result<u8, IpNetworkError> {
410    match mask {
411        IpAddr::V4(mask) => ipv4_mask_to_prefix(mask),
412        IpAddr::V6(mask) => ipv6_mask_to_prefix(mask),
413    }
414}
415
416/// Converts a `IpAddr` network mask into a prefix.
417///
418/// If the mask is invalid this will return `None`. This is useful in const contexts where
419/// [`Option::unwrap`] may be called to trigger a compile-time error if the prefix is invalid.
420pub const fn ip_mask_to_prefix_checked(mask: IpAddr) -> Option<u8> {
421    match mask {
422        IpAddr::V4(mask) => ipv4_mask_to_prefix_checked(mask),
423        IpAddr::V6(mask) => ipv6_mask_to_prefix_checked(mask),
424    }
425}
426
427#[cfg(test)]
428mod test {
429    #[test]
430    #[cfg(feature = "serde")]
431    fn deserialize_from_serde_json_value() {
432        use super::*;
433        let network = IpNetwork::from_str("0.0.0.0/0").unwrap();
434        let val: serde_json::value::Value = serde_json::from_str(&serde_json::to_string(&network).unwrap()).unwrap();
435        let _deser: IpNetwork =
436            serde_json::from_value(val).expect("Fails to deserialize from json_value::value::Value");
437    }
438}