erbium_net/
lib.rs

1/*   Copyright 2023 Perry Lorier
2 *
3 *  Licensed under the Apache License, Version 2.0 (the "License");
4 *  you may not use this file except in compliance with the License.
5 *  You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 *  Unless required by applicable law or agreed to in writing, software
10 *  distributed under the License is distributed on an "AS IS" BASIS,
11 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 *  See the License for the specific language governing permissions and
13 *  limitations under the License.
14 *
15 *  SPDX-License-Identifier: Apache-2.0
16 */
17pub mod addr;
18pub mod netinfo;
19pub mod packet;
20pub mod raw;
21pub mod socket;
22pub mod udp;
23
24/* TODO: only erbium-net should use nix and not expose this as an external API, but we've not got
25 * there yet, so export the version we use here so that everything is always consistent.
26 */
27pub use nix;
28pub use nix::sys::socket::sockopt::*;
29
30// TODO: Write better Debug or to_string() method.
31#[derive(Clone, Copy, Debug)]
32pub struct Ipv4Subnet {
33    pub addr: std::net::Ipv4Addr,
34    pub prefixlen: u8,
35}
36
37#[derive(Debug)]
38pub enum Error {
39    InvalidSubnet,
40}
41
42impl std::fmt::Display for Error {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        match self {
45            Self::InvalidSubnet => write!(f, "Invalid Subnet"),
46        }
47    }
48}
49
50impl std::fmt::Display for Ipv4Subnet {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        write!(f, "{}/{}", self.addr, self.prefixlen)
53    }
54}
55
56impl Ipv4Subnet {
57    pub fn new(addr: std::net::Ipv4Addr, prefixlen: u8) -> Result<Self, Error> {
58        let ret = Self { addr, prefixlen };
59        /* If the prefix is too short, then return an error */
60        if u32::from(ret.addr) & !u32::from(ret.netmask()) != 0 {
61            Err(Error::InvalidSubnet)
62        } else {
63            Ok(ret)
64        }
65    }
66    pub fn network(&self) -> std::net::Ipv4Addr {
67        (u32::from(self.addr) & u32::from(self.netmask())).into()
68    }
69    pub fn netmask(&self) -> std::net::Ipv4Addr {
70        (!(0xffff_ffff_u64 >> self.prefixlen) as u32).into()
71    }
72    pub fn contains(&self, ip: std::net::Ipv4Addr) -> bool {
73        u32::from(ip) & u32::from(self.netmask()) == u32::from(self.addr)
74    }
75    pub fn broadcast(&self) -> std::net::Ipv4Addr {
76        (u32::from(self.network()) | !u32::from(self.netmask())).into()
77    }
78}
79
80#[test]
81fn test_netmask() -> Result<(), Error> {
82    assert_eq!(
83        Ipv4Subnet::new("0.0.0.0".parse().unwrap(), 0)?.netmask(),
84        "0.0.0.0".parse::<std::net::Ipv4Addr>().unwrap()
85    );
86    assert_eq!(
87        Ipv4Subnet::new("0.0.0.0".parse().unwrap(), 8)?.netmask(),
88        "255.0.0.0".parse::<std::net::Ipv4Addr>().unwrap()
89    );
90    assert_eq!(
91        Ipv4Subnet::new("0.0.0.0".parse().unwrap(), 16)?.netmask(),
92        "255.255.0.0".parse::<std::net::Ipv4Addr>().unwrap()
93    );
94    assert_eq!(
95        Ipv4Subnet::new("0.0.0.0".parse().unwrap(), 24)?.netmask(),
96        "255.255.255.0".parse::<std::net::Ipv4Addr>().unwrap()
97    );
98    assert_eq!(
99        Ipv4Subnet::new("0.0.0.0".parse().unwrap(), 25)?.netmask(),
100        "255.255.255.128".parse::<std::net::Ipv4Addr>().unwrap()
101    );
102    assert_eq!(
103        Ipv4Subnet::new("0.0.0.0".parse().unwrap(), 32)?.netmask(),
104        "255.255.255.255".parse::<std::net::Ipv4Addr>().unwrap()
105    );
106    Ok(())
107}
108
109#[test]
110fn test_prefix() {
111    let subnet = Ipv4Subnet::new("192.0.2.112".parse().unwrap(), 28).unwrap();
112    assert_eq!(
113        subnet.broadcast(),
114        "192.0.2.127".parse::<std::net::Ipv4Addr>().unwrap()
115    );
116    assert_eq!(
117        subnet.netmask(),
118        "255.255.255.240".parse::<std::net::Ipv4Addr>().unwrap()
119    );
120}
121
122#[test]
123fn test_contains() {
124    assert_eq!(
125        Ipv4Subnet::new("192.168.0.128".parse().unwrap(), 25)
126            .unwrap()
127            .contains("192.168.0.200".parse().unwrap()),
128        true
129    );
130    assert_eq!(
131        Ipv4Subnet::new("192.168.0.128".parse().unwrap(), 25)
132            .unwrap()
133            .contains("192.168.0.100".parse().unwrap()),
134        false
135    );
136}