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#[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 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 if out.remaining() < (self.prefix.len() + 1) {
40 Err(ToWireError::OutBufferOverflow)?;
41 }
42
43 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 if length % 8 == 0 {
82 octets.truncate((length / 8).into());
84 } else {
85 let num_bytes = (length / 8) + 1;
87 let mask = u8::MAX << (8 - (length % 8));
88 octets.truncate(num_bytes.into());
89 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 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_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_roundtrip!(
175 verify_roundtrip_ipv4_3,
176 "192.168.1.0/3",
177 AddressFamilyId::Ipv4,
178 vec![192],
179 3
180 );
181
182 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_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}