1mod attr_01_origin;
2mod attr_02_17_as_path;
3mod attr_03_next_hop;
4mod attr_04_med;
5mod attr_05_local_pref;
6mod attr_07_18_aggregator;
7mod attr_08_communities;
8mod attr_09_originator;
9mod attr_10_13_cluster;
10mod attr_14_15_nlri;
11mod attr_16_25_extended_communities;
12mod attr_23_tunnel_encap;
13mod attr_29_linkstate;
14mod attr_32_large_communities;
15mod attr_35_otc;
16
17use bytes::{Buf, BufMut, Bytes, BytesMut};
18use log::{debug, warn};
19use std::net::IpAddr;
20
21use crate::models::*;
22
23use crate::error::{BgpValidationWarning, ParserError};
24use crate::parser::bgp::attributes::attr_01_origin::{encode_origin, parse_origin};
25use crate::parser::bgp::attributes::attr_02_17_as_path::{encode_as_path, parse_as_path};
26use crate::parser::bgp::attributes::attr_03_next_hop::{encode_next_hop, parse_next_hop};
27use crate::parser::bgp::attributes::attr_04_med::{encode_med, parse_med};
28use crate::parser::bgp::attributes::attr_05_local_pref::{encode_local_pref, parse_local_pref};
29use crate::parser::bgp::attributes::attr_07_18_aggregator::{encode_aggregator, parse_aggregator};
30use crate::parser::bgp::attributes::attr_08_communities::{
31 encode_regular_communities, parse_regular_communities,
32};
33use crate::parser::bgp::attributes::attr_09_originator::{
34 encode_originator_id, parse_originator_id,
35};
36use crate::parser::bgp::attributes::attr_10_13_cluster::{encode_clusters, parse_clusters};
37use crate::parser::bgp::attributes::attr_14_15_nlri::{encode_nlri, parse_nlri};
38use crate::parser::bgp::attributes::attr_16_25_extended_communities::{
39 encode_extended_communities, encode_ipv6_extended_communities, parse_extended_community,
40 parse_ipv6_extended_community,
41};
42use crate::parser::bgp::attributes::attr_23_tunnel_encap::{
43 encode_tunnel_encapsulation_attribute, parse_tunnel_encapsulation_attribute,
44};
45use crate::parser::bgp::attributes::attr_29_linkstate::{
46 encode_link_state_attribute, parse_link_state_attribute,
47};
48use crate::parser::bgp::attributes::attr_32_large_communities::{
49 encode_large_communities, parse_large_communities,
50};
51use crate::parser::bgp::attributes::attr_35_otc::{
52 encode_only_to_customer, parse_only_to_customer,
53};
54use crate::parser::ReadUtils;
55
56fn validate_attribute_flags(
58 attr_type: AttrType,
59 flags: AttrFlags,
60 warnings: &mut Vec<BgpValidationWarning>,
61) {
62 let expected_flags = match attr_type {
63 AttrType::ORIGIN | AttrType::AS_PATH | AttrType::NEXT_HOP => AttrFlags::TRANSITIVE,
65 AttrType::ATOMIC_AGGREGATE => AttrFlags::TRANSITIVE,
67 AttrType::MULTI_EXIT_DISCRIMINATOR
69 | AttrType::ORIGINATOR_ID
70 | AttrType::CLUSTER_LIST
71 | AttrType::MP_REACHABLE_NLRI
72 | AttrType::MP_UNREACHABLE_NLRI => AttrFlags::OPTIONAL,
73 AttrType::AGGREGATOR
75 | AttrType::AS4_AGGREGATOR
76 | AttrType::AS4_PATH
77 | AttrType::COMMUNITIES
78 | AttrType::EXTENDED_COMMUNITIES
79 | AttrType::IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES
80 | AttrType::LARGE_COMMUNITIES
81 | AttrType::ONLY_TO_CUSTOMER => AttrFlags::OPTIONAL | AttrFlags::TRANSITIVE,
82 AttrType::LOCAL_PREFERENCE => AttrFlags::TRANSITIVE,
84 _ => return, };
87
88 let relevant_flags = flags & (AttrFlags::OPTIONAL | AttrFlags::TRANSITIVE);
90 if relevant_flags != expected_flags {
91 warnings.push(BgpValidationWarning::AttributeFlagsError {
92 attr_type,
93 expected_flags: expected_flags.bits(),
94 actual_flags: relevant_flags.bits(),
95 });
96 }
97
98 if flags.contains(AttrFlags::PARTIAL) {
100 match attr_type {
101 AttrType::ORIGIN
103 | AttrType::AS_PATH
104 | AttrType::NEXT_HOP
105 | AttrType::LOCAL_PREFERENCE
106 | AttrType::ATOMIC_AGGREGATE
107 | AttrType::MULTI_EXIT_DISCRIMINATOR
108 | AttrType::ORIGINATOR_ID
109 | AttrType::CLUSTER_LIST
110 | AttrType::MP_REACHABLE_NLRI
111 | AttrType::MP_UNREACHABLE_NLRI => {
112 warnings.push(BgpValidationWarning::AttributeFlagsError {
113 attr_type,
114 expected_flags: expected_flags.bits(),
115 actual_flags: flags.bits(),
116 });
117 }
118 _ => {} }
120 }
121}
122
123fn is_well_known_mandatory(attr_type: AttrType) -> bool {
125 matches!(
126 attr_type,
127 AttrType::ORIGIN | AttrType::AS_PATH | AttrType::NEXT_HOP | AttrType::LOCAL_PREFERENCE
128 )
129}
130
131fn validate_attribute_length(
133 attr_type: AttrType,
134 length: usize,
135 warnings: &mut Vec<BgpValidationWarning>,
136) {
137 let expected_length = match attr_type {
138 AttrType::ORIGIN => Some(1),
139 AttrType::NEXT_HOP => Some(4), AttrType::MULTI_EXIT_DISCRIMINATOR => Some(4),
141 AttrType::LOCAL_PREFERENCE => Some(4),
142 AttrType::ATOMIC_AGGREGATE => Some(0),
143 AttrType::ORIGINATOR_ID => Some(4),
144 AttrType::ONLY_TO_CUSTOMER => Some(4),
145 AttrType::AS_PATH
147 | AttrType::AS4_PATH
148 | AttrType::AGGREGATOR
149 | AttrType::AS4_AGGREGATOR
150 | AttrType::COMMUNITIES
151 | AttrType::EXTENDED_COMMUNITIES
152 | AttrType::IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES
153 | AttrType::LARGE_COMMUNITIES
154 | AttrType::CLUSTER_LIST
155 | AttrType::MP_REACHABLE_NLRI
156 | AttrType::MP_UNREACHABLE_NLRI => None,
157 _ => None, };
159
160 if let Some(expected) = expected_length {
161 if length != expected {
162 warnings.push(BgpValidationWarning::AttributeLengthError {
163 attr_type,
164 expected_length: Some(expected),
165 actual_length: length,
166 });
167 }
168 }
169}
170
171pub fn parse_attributes(
176 mut data: Bytes,
177 asn_len: &AsnLength,
178 add_path: bool,
179 afi: Option<Afi>,
180 safi: Option<Safi>,
181 prefixes: Option<&[NetworkPrefix]>,
182) -> Result<Attributes, ParserError> {
183 let mut attributes: Vec<Attribute> = Vec::with_capacity(20);
184 let mut validation_warnings: Vec<BgpValidationWarning> = Vec::new();
185 let mut seen_attributes: [bool; 256] = [false; 256];
187
188 while data.remaining() >= 3 {
189 let flag = AttrFlags::from_bits_retain(data.get_u8());
194 let attr_type = data.get_u8();
195 let attr_length = match flag.contains(AttrFlags::EXTENDED) {
196 false => data.get_u8() as usize,
197 true => data.get_u16() as usize,
198 };
199
200 let mut partial = false;
201 if flag.contains(AttrFlags::PARTIAL) {
202 partial = true;
213 }
214
215 debug!(
216 "reading attribute: type -- {:?}, length -- {}",
217 &attr_type, attr_length
218 );
219
220 let parsed_attr_type = AttrType::from(attr_type);
221
222 if seen_attributes[attr_type as usize] {
224 validation_warnings.push(BgpValidationWarning::DuplicateAttribute {
225 attr_type: parsed_attr_type,
226 });
227 }
229 seen_attributes[attr_type as usize] = true;
230
231 validate_attribute_flags(parsed_attr_type, flag, &mut validation_warnings);
233 validate_attribute_length(parsed_attr_type, attr_length, &mut validation_warnings);
234
235 let attr_type = match parsed_attr_type {
236 attr_type @ AttrType::Unknown(unknown_type) => {
237 let bytes = data.read_n_bytes(attr_length)?;
239 let attr_value = match get_deprecated_attr_type(unknown_type) {
240 Some(t) => {
241 debug!("deprecated attribute type: {} - {}", unknown_type, t);
242 AttributeValue::Deprecated(AttrRaw { attr_type, bytes })
243 }
244 None => {
245 debug!("unknown attribute type: {}", unknown_type);
246 AttributeValue::Unknown(AttrRaw { attr_type, bytes })
247 }
248 };
249
250 assert_eq!(attr_type, attr_value.attr_type());
251 attributes.push(Attribute {
252 value: attr_value,
253 flag,
254 });
255 continue;
256 }
257 t => t,
258 };
259
260 let bytes_left = data.remaining();
261
262 if data.remaining() < attr_length {
263 warn!(
264 "not enough bytes: input bytes left - {}, want to read - {}; skipping",
265 bytes_left, attr_length
266 );
267 break;
269 }
270
271 data.has_n_remaining(attr_length)?;
273 let mut attr_data = data.split_to(attr_length);
274
275 let attr = match attr_type {
276 AttrType::ORIGIN => parse_origin(attr_data),
277 AttrType::AS_PATH => {
278 parse_as_path(attr_data, asn_len).map(|path| AttributeValue::AsPath {
279 path,
280 is_as4: false,
281 })
282 }
283 AttrType::NEXT_HOP => parse_next_hop(attr_data, &afi),
284 AttrType::MULTI_EXIT_DISCRIMINATOR => parse_med(attr_data),
285 AttrType::LOCAL_PREFERENCE => parse_local_pref(attr_data),
286 AttrType::ATOMIC_AGGREGATE => Ok(AttributeValue::AtomicAggregate),
287 AttrType::AGGREGATOR => {
288 parse_aggregator(attr_data, asn_len).map(|(asn, id)| AttributeValue::Aggregator {
289 asn,
290 id,
291 is_as4: false,
292 })
293 }
294 AttrType::ORIGINATOR_ID => parse_originator_id(attr_data),
295 AttrType::CLUSTER_LIST => parse_clusters(attr_data),
296 AttrType::MP_REACHABLE_NLRI => {
297 parse_nlri(attr_data, &afi, &safi, &prefixes, true, add_path)
298 }
299 AttrType::MP_UNREACHABLE_NLRI => {
300 parse_nlri(attr_data, &afi, &safi, &prefixes, false, add_path)
301 }
302 AttrType::AS4_PATH => parse_as_path(attr_data, &AsnLength::Bits32)
303 .map(|path| AttributeValue::AsPath { path, is_as4: true }),
304 AttrType::AS4_AGGREGATOR => {
305 parse_aggregator(attr_data, &AsnLength::Bits32).map(|(asn, id)| {
306 AttributeValue::Aggregator {
307 asn,
308 id,
309 is_as4: true,
310 }
311 })
312 }
313
314 AttrType::COMMUNITIES => parse_regular_communities(attr_data),
316 AttrType::LARGE_COMMUNITIES => parse_large_communities(attr_data),
317 AttrType::EXTENDED_COMMUNITIES => parse_extended_community(attr_data),
318 AttrType::IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITIES => {
319 parse_ipv6_extended_community(attr_data)
320 }
321 AttrType::DEVELOPMENT => {
322 let mut value = vec![];
323 for _i in 0..attr_length {
324 value.push(attr_data.get_u8());
325 }
326 Ok(AttributeValue::Development(value))
327 }
328 AttrType::ONLY_TO_CUSTOMER => parse_only_to_customer(attr_data),
329 AttrType::TUNNEL_ENCAPSULATION => parse_tunnel_encapsulation_attribute(attr_data),
330 AttrType::BGP_LS_ATTRIBUTE => parse_link_state_attribute(attr_data),
331 _ => Err(ParserError::Unsupported(format!(
332 "unsupported attribute type: {attr_type:?}"
333 ))),
334 };
335
336 match attr {
337 Ok(value) => {
338 assert_eq!(attr_type, value.attr_type());
339 attributes.push(Attribute { value, flag });
340 }
341 Err(e) => {
342 if partial {
344 validation_warnings.push(BgpValidationWarning::PartialAttributeError {
346 attr_type,
347 reason: e.to_string(),
348 });
349 debug!("PARTIAL attribute error: {}", e);
350 } else if is_well_known_mandatory(attr_type) {
351 validation_warnings.push(BgpValidationWarning::MalformedAttributeList {
354 reason: format!(
355 "Well-known mandatory attribute {} parsing failed: {}",
356 u8::from(attr_type),
357 e
358 ),
359 });
360 debug!(
361 "Well-known mandatory attribute parsing failed, treating as withdraw: {}",
362 e
363 );
364 } else {
365 validation_warnings.push(BgpValidationWarning::OptionalAttributeError {
367 attr_type,
368 reason: e.to_string(),
369 });
370 debug!("Optional attribute error, discarding: {}", e);
371 }
372 continue;
374 }
375 };
376 }
377
378 let mandatory_attributes = [
380 AttrType::ORIGIN,
381 AttrType::AS_PATH,
382 AttrType::NEXT_HOP,
383 ];
385
386 for &mandatory_attr in &mandatory_attributes {
387 if !seen_attributes[u8::from(mandatory_attr) as usize] {
388 validation_warnings.push(BgpValidationWarning::MissingWellKnownAttribute {
389 attr_type: mandatory_attr,
390 });
391 }
392 }
393
394 let mut result = Attributes::from(attributes);
395 result.validation_warnings = validation_warnings;
396 Ok(result)
397}
398
399impl Attribute {
400 pub fn encode(&self, asn_len: AsnLength) -> Bytes {
401 let mut bytes = BytesMut::new();
402
403 let flag = self.flag.bits();
404 let type_code = self.value.attr_type().into();
405
406 bytes.put_u8(flag);
407 bytes.put_u8(type_code);
408
409 let value_bytes = match &self.value {
410 AttributeValue::Origin(v) => encode_origin(v),
411 AttributeValue::AsPath { path, is_as4 } => {
412 let four_byte = match is_as4 {
413 true => AsnLength::Bits32,
414 false => match asn_len.is_four_byte() {
415 true => AsnLength::Bits32,
416 false => AsnLength::Bits16,
417 },
418 };
419 encode_as_path(path, four_byte)
420 }
421 AttributeValue::NextHop(v) => encode_next_hop(v),
422 AttributeValue::MultiExitDiscriminator(v) => encode_med(*v),
423 AttributeValue::LocalPreference(v) => encode_local_pref(*v),
424 AttributeValue::OnlyToCustomer(v) => encode_only_to_customer(v.into()),
425 AttributeValue::AtomicAggregate => Bytes::default(),
426 AttributeValue::Aggregator { asn, id, is_as4: _ } => {
427 encode_aggregator(asn, &IpAddr::from(*id))
428 }
429 AttributeValue::Communities(v) => encode_regular_communities(v),
430 AttributeValue::ExtendedCommunities(v) => encode_extended_communities(v),
431 AttributeValue::LargeCommunities(v) => encode_large_communities(v),
432 AttributeValue::Ipv6AddressSpecificExtendedCommunities(v) => {
433 encode_ipv6_extended_communities(v)
434 }
435 AttributeValue::OriginatorId(v) => encode_originator_id(&IpAddr::from(*v)),
436 AttributeValue::Clusters(v) => encode_clusters(v),
437 AttributeValue::MpReachNlri(v) => encode_nlri(v, true),
438 AttributeValue::MpUnreachNlri(v) => encode_nlri(v, false),
439 AttributeValue::LinkState(v) => encode_link_state_attribute(v),
440 AttributeValue::TunnelEncapsulation(v) => encode_tunnel_encapsulation_attribute(v),
441 AttributeValue::Development(v) => Bytes::from(v.to_owned()),
442 AttributeValue::Deprecated(v) => Bytes::from(v.bytes.to_owned()),
443 AttributeValue::Unknown(v) => Bytes::from(v.bytes.to_owned()),
444 };
445
446 match self.is_extended() {
447 false => {
448 bytes.put_u8(value_bytes.len() as u8);
449 }
450 true => {
451 bytes.put_u16(value_bytes.len() as u16);
452 }
453 }
454 bytes.extend(value_bytes);
455 bytes.freeze()
456 }
457}
458
459impl Attributes {
460 pub fn encode(&self, asn_len: AsnLength) -> Bytes {
461 let mut bytes = BytesMut::new();
462 for attr in &self.inner {
463 bytes.extend(attr.encode(asn_len));
464 }
465 bytes.freeze()
466 }
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472
473 #[test]
474 fn test_unknwon_attribute_type() {
475 let data = Bytes::from(vec![0x40, 0xFE, 0x00]);
476 let asn_len = AsnLength::Bits16;
477 let add_path = false;
478 let afi = None;
479 let safi = None;
480 let prefixes = None;
481 let attributes = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes);
482 assert!(attributes.is_ok());
483 let attributes = attributes.unwrap();
484 assert_eq!(attributes.inner.len(), 1);
485 assert_eq!(
486 attributes.inner[0].value.attr_type(),
487 AttrType::Unknown(254)
488 );
489 }
490
491 #[test]
492 fn test_rfc7606_attribute_flags_error() {
493 let data = Bytes::from(vec![0x80, 0x01, 0x01, 0x00]); let asn_len = AsnLength::Bits16;
496 let add_path = false;
497 let afi = None;
498 let safi = None;
499 let prefixes = None;
500
501 let attributes = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes).unwrap();
502
503 assert!(attributes.has_validation_warnings());
505 let warnings = attributes.validation_warnings();
506 assert!(!warnings.is_empty());
508
509 match &warnings[0] {
510 BgpValidationWarning::AttributeFlagsError { attr_type, .. } => {
511 assert_eq!(*attr_type, AttrType::ORIGIN);
512 }
513 _ => panic!("Expected AttributeFlagsError warning"),
514 }
515 }
516
517 #[test]
518 fn test_rfc7606_missing_mandatory_attribute() {
519 let data = Bytes::from(vec![]);
521 let asn_len = AsnLength::Bits16;
522 let add_path = false;
523 let afi = None;
524 let safi = None;
525 let prefixes = None;
526
527 let attributes = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes).unwrap();
528
529 assert!(attributes.has_validation_warnings());
531 let warnings = attributes.validation_warnings();
532 assert_eq!(warnings.len(), 3); for warning in warnings {
535 match warning {
536 BgpValidationWarning::MissingWellKnownAttribute { attr_type } => {
537 assert!(matches!(
538 attr_type,
539 AttrType::ORIGIN | AttrType::AS_PATH | AttrType::NEXT_HOP
540 ));
541 }
542 _ => panic!("Expected MissingWellKnownAttribute warning"),
543 }
544 }
545 }
546
547 #[test]
548 fn test_rfc7606_duplicate_attribute() {
549 let data = Bytes::from(vec![
551 0x40, 0x01, 0x01, 0x00, 0x40, 0x01, 0x01, 0x01, ]);
554 let asn_len = AsnLength::Bits16;
555 let add_path = false;
556 let afi = None;
557 let safi = None;
558 let prefixes = None;
559
560 let attributes = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes).unwrap();
561
562 assert!(attributes.has_validation_warnings());
564 let warnings = attributes.validation_warnings();
565
566 let has_duplicate_warning = warnings
568 .iter()
569 .any(|w| matches!(w, BgpValidationWarning::DuplicateAttribute { .. }));
570 assert!(has_duplicate_warning);
571 }
572
573 #[test]
574 fn test_attribute_type_boundaries() {
575 let asn_len = AsnLength::Bits16;
576 let add_path = false;
577 let afi = None;
578 let safi = None;
579 let prefixes = None;
580
581 const REQUIRED_ATTRS: &[u8] = &[
583 0x40, 0x01, 0x01, 0x00, 0x40, 0x02, 0x00, 0x40, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, ];
587
588 let mut data = REQUIRED_ATTRS.to_vec();
590 data.extend_from_slice(&[0x40, 0xFF, 0x01, 0x00]); let data = Bytes::from(data);
592
593 let attributes = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes).unwrap();
594
595 assert!(attributes.has_attr(AttrType::DEVELOPMENT));
596 assert!(!attributes.has_validation_warnings());
597
598 let mut data = REQUIRED_ATTRS.to_vec();
600 data.extend_from_slice(&[0x40, 0x00, 0x01, 0x01]); let data = Bytes::from(data);
602
603 let attributes = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes).unwrap();
604
605 assert!(attributes.validation_warnings.iter().any(|vw| {
607 matches!(vw, BgpValidationWarning::OptionalAttributeError { attr_type, reason:_ } if *attr_type == AttrType::RESERVED)
608 }));
609 }
610
611 #[test]
612 fn test_rfc7606_attribute_length_error() {
613 let data = Bytes::from(vec![0x40, 0x01, 0x02, 0x00, 0x01]);
615 let asn_len = AsnLength::Bits16;
616 let add_path = false;
617 let afi = None;
618 let safi = None;
619 let prefixes = None;
620
621 let attributes = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes).unwrap();
622
623 assert!(attributes.has_validation_warnings());
625 let warnings = attributes.validation_warnings();
626
627 let has_length_warning = warnings
628 .iter()
629 .any(|w| matches!(w, BgpValidationWarning::AttributeLengthError { .. }));
630 assert!(has_length_warning);
631 }
632
633 #[test]
634 fn test_rfc7606_no_session_reset() {
635 let data = Bytes::from(vec![
637 0x80, 0x01, 0x02, 0x00, 0x01, 0x40, 0x01, 0x01, 0x00, 0x40, 0xFF, 0x01, 0x00, ]);
641 let asn_len = AsnLength::Bits16;
642 let add_path = false;
643 let afi = None;
644 let safi = None;
645 let prefixes = None;
646
647 let result = parse_attributes(data, &asn_len, add_path, afi, safi, prefixes);
649 assert!(result.is_ok());
650
651 let attributes = result.unwrap();
652 assert!(attributes.has_validation_warnings());
653
654 let warnings = attributes.validation_warnings();
656 assert!(!warnings.is_empty());
657 }
658}