1pub mod attributes;
3pub use crate::attributes::*;
4pub mod nlri;
6pub use crate::nlri::*;
7#[cfg(feature = "flowspec")]
8pub mod flowspec;
10#[cfg(feature = "flowspec")]
11pub use crate::flowspec::*;
12
13use crate::*;
14
15use std::collections::HashMap;
16use std::io::{Cursor, Error, Read};
17use std::net::IpAddr;
18
19#[derive(Clone, Debug)]
21pub struct Update {
22 pub withdrawn_routes: Vec<NLRIEncoding>,
24
25 pub attributes: Vec<PathAttribute>,
27
28 pub announced_routes: Vec<NLRIEncoding>,
30}
31
32impl Update {
33 pub fn parse(
35 header: &Header,
36 stream: &mut impl Read,
37 capabilities: &Capabilities,
38 ) -> Result<Update, Error> {
39 if header.length < 23 {
40 return Err(Error::new(
41 ErrorKind::Other,
42 format!("Header had bogus length {} < 23", header.length),
43 ));
44 }
45 let mut nlri_length: usize = header.length as usize - 23;
46
47 let withdraw_len = stream.read_u16::<BigEndian>()? as usize;
51 if withdraw_len > nlri_length {
52 return Err(Error::new(
53 ErrorKind::Other,
54 format!(
55 "Got bogus withdraw length {} < msg len {}",
56 withdraw_len, nlri_length
57 ),
58 ));
59 }
60 let mut buffer = vec![0; withdraw_len];
61 stream.read_exact(&mut buffer)?;
62 nlri_length -= withdraw_len;
63
64 let mut withdrawn_routes: Vec<NLRIEncoding> = Vec::with_capacity(0);
65 let mut cursor = Cursor::new(buffer);
66
67 if capabilities.EXTENDED_PATH_NLRI_SUPPORT {
68 while cursor.position() < withdraw_len as u64 {
69 let path_id = cursor.read_u32::<BigEndian>()?;
70 let prefix = Prefix::parse(&mut cursor, AFI::IPV4)?;
71 withdrawn_routes.push(NLRIEncoding::IP_WITH_PATH_ID((prefix, path_id)));
72 }
73 } else {
74 while cursor.position() < withdraw_len as u64 {
75 withdrawn_routes.push(NLRIEncoding::IP(Prefix::parse(&mut cursor, AFI::IPV4)?));
76 }
77 }
78
79 let length = stream.read_u16::<BigEndian>()? as usize;
83 if length > nlri_length {
84 return Err(Error::new(
85 ErrorKind::Other,
86 format!(
87 "Got bogus attributes length {} < msg len {} - withdraw len {}",
88 length, nlri_length, withdraw_len
89 ),
90 ));
91 }
92 let mut buffer = vec![0; length];
93 stream.read_exact(&mut buffer)?;
94 nlri_length -= length;
95
96 let mut attributes: Vec<PathAttribute> = Vec::with_capacity(8);
97 let mut cursor = Cursor::new(buffer);
98 while cursor.position() < length as u64 {
99 let attribute = match PathAttribute::parse(&mut cursor, capabilities) {
100 Ok(a) => a,
101 Err(e) => match e.kind() {
102 ErrorKind::UnexpectedEof => return Err(e),
103 _ => continue,
104 },
105 };
106 attributes.push(attribute);
107 }
108
109 let mut buffer = vec![0; nlri_length as usize];
113
114 stream.read_exact(&mut buffer)?;
115 let mut cursor = Cursor::new(buffer);
116 let mut announced_routes: Vec<NLRIEncoding> = Vec::with_capacity(4);
117
118 while cursor.position() < nlri_length as u64 {
119 if util::detect_add_path_prefix(&mut cursor, 32)? {
120 let path_id = cursor.read_u32::<BigEndian>()?;
121 let prefix = Prefix::parse(&mut cursor, AFI::IPV4)?;
122 announced_routes.push(NLRIEncoding::IP_WITH_PATH_ID((prefix, path_id)));
123 } else {
124 announced_routes.push(NLRIEncoding::IP(Prefix::parse(&mut cursor, AFI::IPV4)?));
125 }
126 }
127
128 Ok(Update {
129 withdrawn_routes,
130 attributes,
131 announced_routes,
132 })
133 }
134
135 pub fn encode(&self, buf: &mut impl Write) -> Result<(), Error> {
137 let mut temp_buf: Vec<u8> = Vec::with_capacity(8);
139
140 let mut unreach_nlri: HashMap<(AFI, SAFI), Vec<NLRIEncoding>> = HashMap::new();
141 for withdrawal in &self.withdrawn_routes {
142 if withdrawal.is_ipv4() {
143 withdrawal.encode(&mut temp_buf)?;
144 } else {
145 let nlris = unreach_nlri
147 .entry((withdrawal.afi(), withdrawal.safi()))
148 .or_insert_with(Vec::new);
149 nlris.push(withdrawal.clone());
150 }
151 }
152 buf.write_u16::<BigEndian>(temp_buf.len() as u16)?;
153 buf.write_all(&temp_buf)?;
154 temp_buf.clear();
155
156 for attribute in &self.attributes {
158 attribute.encode(&mut temp_buf)?;
159 }
160 for ((afi, safi), unreach_nlris) in unreach_nlri.into_iter() {
161 let pa = PathAttribute::MP_UNREACH_NLRI(MPUnreachNLRI {
162 afi,
163 safi,
164 withdrawn_routes: unreach_nlris,
165 });
166 pa.encode(&mut temp_buf)?;
167 }
168 buf.write_u16::<BigEndian>(temp_buf.len() as u16)?;
169 buf.write_all(&temp_buf)?;
170 temp_buf.clear();
171
172 for route in &self.announced_routes {
174 route.encode(&mut temp_buf)?;
175 }
176 buf.write_all(&temp_buf)
177 }
178
179 pub fn get(&self, identifier: Identifier) -> Option<&PathAttribute> {
181 for a in &self.attributes {
182 if a.id() == identifier {
183 return Some(a);
184 }
185 }
186 None
187 }
188
189 pub fn is_announcement(&self) -> bool {
191 if !self.announced_routes.is_empty() || self.get(Identifier::MP_REACH_NLRI).is_some() {
192 return true;
193 }
194 false
195 }
196
197 pub fn is_withdrawal(&self) -> bool {
199 if !self.withdrawn_routes.is_empty() || self.get(Identifier::MP_UNREACH_NLRI).is_some() {
200 return true;
201 }
202 false
203 }
204
205 pub fn normalize(&mut self) {
207 let identifier = match self.get(Identifier::MP_REACH_NLRI) {
209 Some(PathAttribute::MP_REACH_NLRI(routes)) => Some(routes.announced_routes.clone()),
210 _ => None,
211 };
212 if let Some(routes) = identifier {
213 self.announced_routes.extend(routes)
214 }
215
216 let identifier = match self.get(Identifier::MP_UNREACH_NLRI) {
218 Some(PathAttribute::MP_UNREACH_NLRI(routes)) => Some(routes.withdrawn_routes.clone()),
219 _ => None,
220 };
221 if let Some(routes) = identifier {
222 self.withdrawn_routes.extend(routes)
223 }
224 }
225}
226
227#[derive(Debug, Clone, Eq, PartialEq)]
229#[allow(non_camel_case_types)]
230pub enum NLRIEncoding {
231 IP(Prefix),
233
234 IP_WITH_PATH_ID((Prefix, u32)),
236
237 IP_MPLS((Prefix, u32)),
239
240 IP_MPLS_WITH_PATH_ID((Prefix, u32, u32)),
242
243 IP_VPN_MPLS((u64, Prefix, u32)),
245
246 L2VPN((u64, u16, u16, u16, u32)),
248
249 #[cfg(feature = "flowspec")]
251 FLOWSPEC(Vec<FlowspecFilter>),
252}
253
254impl NLRIEncoding {
255 pub fn is_ipv4(&self) -> bool {
257 if let NLRIEncoding::IP(prefix) = &self {
258 prefix.protocol == AFI::IPV4
259 } else {
260 false
261 }
262 }
263
264 pub fn afi(&self) -> AFI {
266 use NLRIEncoding::*;
267 match &self {
268 IP(prefix) => prefix.protocol,
269 #[cfg(feature = "flowspec")]
270 FLOWSPEC(_) => AFI::IPV4, _ => unimplemented!(),
272 }
273 }
274
275 pub fn safi(&self) -> SAFI {
277 use NLRIEncoding::*;
278 match &self {
279 IP(_) => SAFI::Unicast,
280 #[cfg(feature = "flowspec")]
281 FLOWSPEC(_) => SAFI::Flowspec,
282 _ => unimplemented!(),
283 }
284 }
285
286 pub fn encode(&self, buf: &mut impl Write) -> Result<(), Error> {
288 match self {
289 NLRIEncoding::IP(prefix) => {
290 buf.write_u8(prefix.length)?;
291 buf.write_all(&prefix.masked_octets())
292 }
293 NLRIEncoding::IP_WITH_PATH_ID((prefix, path_id)) => {
294 buf.write_u32::<BigEndian>(*path_id)?;
295 buf.write_u8(prefix.length)?;
296 buf.write_all(&prefix.masked_octets())
297 }
298 NLRIEncoding::IP_VPN_MPLS((rd, prefix, label)) => {
299 buf.write_u32::<BigEndian>(*label)?;
301 buf.write_u64::<BigEndian>(*rd)?;
302 buf.write_all(&prefix.prefix)
303 }
304 #[cfg(feature = "flowspec")]
305 NLRIEncoding::FLOWSPEC(filters) => {
306 let mut bytes: Vec<u8> = Vec::with_capacity(16);
307 for filter in filters {
308 filter.encode(&mut bytes)?;
309 }
310 buf.write_u8(bytes.len() as u8)?;
311 buf.write_all(&bytes)
312 }
313 _ => unimplemented!("{:?}", self),
314 }
315 }
316}
317
318#[derive(Clone, Eq, PartialEq)]
320pub struct Prefix {
321 pub protocol: AFI,
323 pub length: u8,
325 pub prefix: Vec<u8>,
327}
328
329impl From<&Prefix> for IpAddr {
330 fn from(prefix: &Prefix) -> Self {
331 match prefix.protocol {
332 AFI::IPV4 => {
333 let mut buffer: [u8; 4] = [0; 4];
334 buffer[..prefix.prefix.len()].clone_from_slice(&prefix.prefix[..]);
335 IpAddr::from(buffer)
336 }
337 AFI::IPV6 => {
338 let mut buffer: [u8; 16] = [0; 16];
339 buffer[..prefix.prefix.len()].clone_from_slice(&prefix.prefix[..]);
340 IpAddr::from(buffer)
341 }
342 AFI::L2VPN => unimplemented!(),
343 AFI::BGPLS => unimplemented!(),
344 }
345 }
346}
347
348impl From<&Prefix> for (IpAddr, u8) {
349 fn from(prefix: &Prefix) -> (IpAddr, u8) {
359 (IpAddr::from(prefix), prefix.length)
360 }
361}
362
363impl From<(IpAddr, u8)> for Prefix {
364 fn from(prefix: (IpAddr, u8)) -> Prefix {
372 let (protocol, octets) = match prefix.0 {
373 IpAddr::V4(v4) => (AFI::IPV4, v4.octets().to_vec()),
374 IpAddr::V6(v6) => (AFI::IPV6, v6.octets().to_vec()),
375 };
376 Prefix {
377 protocol,
378 length: prefix.1,
379 prefix: octets,
380 }
381 }
382}
383
384impl Display for Prefix {
385 fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
386 write!(f, "{}/{}", IpAddr::from(self), self.length)
387 }
388}
389
390impl Debug for Prefix {
391 fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
392 write!(f, "{}/{}", IpAddr::from(self), self.length)
393 }
394}
395
396impl Prefix {
397 fn new(protocol: AFI, length: u8, prefix: Vec<u8>) -> Self {
398 Self {
399 protocol,
400 length,
401 prefix,
402 }
403 }
404
405 fn octet_length(&self) -> usize {
406 (self.length as usize + 7) / 8
407 }
408
409 pub fn masked_octets(&self) -> &[u8] {
412 &self.prefix[..self.octet_length()]
413 }
414
415 fn parse(stream: &mut impl Read, protocol: AFI) -> Result<Prefix, Error> {
416 let length = stream.read_u8()?;
417
418 if length
419 > match protocol {
420 AFI::IPV4 => 32,
421 AFI::IPV6 => 128,
422 AFI::L2VPN => unimplemented!(),
423 AFI::BGPLS => unimplemented!(),
424 }
425 {
426 return Err(Error::new(
427 ErrorKind::Other,
428 format!("Bogus prefix length {}", length),
429 ));
430 }
431
432 let mut prefix: Vec<u8> = vec![0; ((length + 7) / 8) as usize];
433 stream.read_exact(&mut prefix)?;
434
435 Ok(Prefix {
436 protocol,
437 length,
438 prefix,
439 })
440 }
441}
442
443#[test]
444fn test_prefix_masked_octets() {
445 let prefix = Prefix::new(AFI::IPV4, 32, vec![1, 1, 1, 1]);
446 assert_eq!(prefix.masked_octets(), &[1, 1, 1, 1]);
447 assert_eq!(&prefix.to_string(), "1.1.1.1/32");
448
449 let prefix = Prefix::new(AFI::IPV4, 16, vec![1, 1, 1, 1]);
450 assert_eq!(prefix.masked_octets(), &[1, 1]);
451 assert_eq!(&prefix.to_string(), "1.1.1.1/16");
452
453 let prefix = Prefix::new(AFI::IPV4, 18, vec![1, 1, 1, 1]);
454 assert_eq!(prefix.masked_octets(), &[1, 1, 1]);
455 assert_eq!(&prefix.to_string(), "1.1.1.1/18");
456}