Skip to main content

bgp_packet/
ip_prefix.rs

1use std::fmt::Display;
2use std::net::{Ipv4Addr, Ipv6Addr};
3use std::str::FromStr;
4
5use bytes::{Buf, BufMut, BytesMut};
6use eyre::{Result, bail};
7use serde::{Deserialize, Serialize};
8
9use crate::constants::AddressFamilyId;
10use crate::parser::{ParserContext, ToWireError};
11
12/// IpPrefix represents some IP address prefix, for a specific AddressFamilyId.
13#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
14pub struct IpPrefix {
15    pub address_family: AddressFamilyId,
16    pub prefix: Vec<u8>,
17    pub length: u8,
18}
19
20impl IpPrefix {
21    pub fn new(address_family: AddressFamilyId, prefix: Vec<u8>, length: u8) -> Result<Self> {
22        // Ensure that the prefix we are given contains the right number of bytes corresponding to the prefix length.
23        if prefix.len() < ((length + 7) / 8).into() {
24            bail!(
25                "Mismatched prefix {:?} for given prefix length: {}",
26                prefix,
27                length
28            );
29        }
30        Ok(Self {
31            address_family,
32            prefix,
33            length,
34        })
35    }
36
37    pub fn to_wire(&self, _: &ParserContext, out: &mut BytesMut) -> Result<(), ToWireError> {
38        // Verify that there is enough space to write the IpPrefix.
39        if out.remaining() < (self.prefix.len() + 1) {
40            Err(ToWireError::OutBufferOverflow)?;
41        }
42
43        // Write length and prefix.
44        out.put_u8(self.length);
45        out.put(self.prefix.as_slice());
46
47        Ok(())
48    }
49}
50
51impl TryFrom<&str> for IpPrefix {
52    type Error = eyre::Error;
53
54    fn try_from(value: &str) -> Result<Self, Self::Error> {
55        let parts: Vec<&str> = value.split("/").collect();
56
57        if parts.len() != 2 {
58            bail!(
59                "Expected IpPrefix in format prefix/length but got: {}",
60                value
61            );
62        }
63
64        let length: u8 = u8::from_str_radix(parts[1], 10).map_err(eyre::Error::from)?;
65        let mut octets;
66        let afi: AddressFamilyId;
67
68        if parts[0].contains(":") {
69            afi = AddressFamilyId::Ipv6;
70            let addr: Ipv6Addr = Ipv6Addr::from_str(parts[0]).map_err(eyre::Error::from)?;
71            octets = addr.octets().to_vec();
72        } else if parts[0].contains(".") {
73            afi = AddressFamilyId::Ipv4;
74            let addr: Ipv4Addr = Ipv4Addr::from_str(parts[0]).map_err(eyre::Error::from)?;
75            octets = addr.octets().to_vec();
76        } else {
77            bail!("Could not figure out address type")
78        }
79
80        // Truncate the octets we have to the right number of bytes to match the prefix length..
81        if length % 8 == 0 {
82            // We can cleanly truncate the number of bytes since we are at a byte boundary.
83            octets.truncate((length / 8).into());
84        } else {
85            // We need to keep length % 8 bits of the last byte.
86            let num_bytes = (length / 8) + 1;
87            let mask = u8::MAX << (8 - (length % 8));
88            octets.truncate(num_bytes.into());
89            // Fix up the last byte.
90            let last_pos = octets.len() - 1;
91            octets[last_pos] &= mask;
92        }
93
94        Ok(IpPrefix {
95            address_family: afi,
96            prefix: octets,
97            length,
98        })
99    }
100}
101
102impl Display for IpPrefix {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        match self.address_family {
105            AddressFamilyId::Ipv4 => {
106                // Pad the prefix with 0 bytes if it's less than 4 bytes long.
107                let bytes = &mut self.prefix.clone();
108                if bytes.len() < 4 {
109                    bytes.extend(std::iter::repeat(0).take(4 - bytes.len()));
110                }
111                let four_bytes: [u8; 4] = bytes
112                    .as_slice()
113                    .try_into()
114                    .map_err(|_| std::fmt::Error {})?;
115                let ipv4_addr = Ipv4Addr::from(four_bytes);
116                write!(f, "{}/{}", ipv4_addr, self.length)
117            }
118            AddressFamilyId::Ipv6 => {
119                let bytes = &mut self.prefix.clone();
120                if bytes.len() < 16 {
121                    bytes.extend(std::iter::repeat(0).take(16 - bytes.len()));
122                }
123                let sixteen_bytes: [u8; 16] = bytes
124                    .as_slice()
125                    .try_into()
126                    .map_err(|_| std::fmt::Error {})?;
127                let ipv6_addr = Ipv6Addr::from(sixteen_bytes);
128                write!(f, "{}/{}", ipv6_addr, self.length)
129            }
130        }
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use crate::constants::AddressFamilyId;
137
138    use super::IpPrefix;
139
140    macro_rules! verify_roundtrip {
141        ($name:ident, $prefix_str:expr, $afi:expr, $bytes:expr, $length:expr) => {
142            #[test]
143            fn $name() {
144                let ip_prefix = IpPrefix::try_from($prefix_str).unwrap();
145
146                assert_eq!(ip_prefix.address_family, $afi);
147                assert_eq!(ip_prefix.prefix, $bytes);
148                assert_eq!(ip_prefix.length, $length);
149
150                let to_str: &str = &ip_prefix.to_string();
151                assert_eq!(IpPrefix::try_from(to_str).unwrap(), ip_prefix);
152            }
153        };
154    }
155
156    verify_roundtrip!(
157        verify_roundtrip_ipv4_24,
158        "10.1.2.0/24",
159        AddressFamilyId::Ipv4,
160        vec![10, 1, 2],
161        24
162    );
163
164    // Verify truncation.
165    verify_roundtrip!(
166        verify_roundtrip_ipv4_19,
167        "10.245.123.0/19",
168        AddressFamilyId::Ipv4,
169        vec![10, 245, 96],
170        19
171    );
172
173    // Verify truncation.
174    verify_roundtrip!(
175        verify_roundtrip_ipv4_3,
176        "192.168.1.0/3",
177        AddressFamilyId::Ipv4,
178        vec![192],
179        3
180    );
181
182    // Verify default address.
183    verify_roundtrip!(
184        verify_roundtrip_ipv4_0,
185        "0.0.0.0/0",
186        AddressFamilyId::Ipv4,
187        vec![],
188        0
189    );
190
191    verify_roundtrip!(
192        verify_roundtrip_ipv6_48,
193        "2001:db8:cafe::/48",
194        AddressFamilyId::Ipv6,
195        vec![32, 1, 13, 184, 202, 254],
196        48
197    );
198
199    // Verify truncation.
200    verify_roundtrip!(
201        verify_roundtrip_ipv6_32,
202        "2001:db8:cafe::/32",
203        AddressFamilyId::Ipv6,
204        vec![32, 1, 13, 184],
205        32
206    );
207
208    verify_roundtrip!(
209        verify_roundtrip_ipv6_0,
210        "::/0",
211        AddressFamilyId::Ipv6,
212        vec![],
213        0
214    );
215}