Skip to content

Commit 8228248

Browse files
committed
Begin storing well known mandatory attributes explicitly
1 parent 3227836 commit 8228248

File tree

2 files changed

+116
-28
lines changed

2 files changed

+116
-28
lines changed

src/models/bgp/attributes/mod.rs

Lines changed: 115 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod origin;
55

66
use crate::models::network::*;
77
use bitflags::bitflags;
8+
use itertools::chain;
89
use num_enum::{FromPrimitive, IntoPrimitive};
910
use std::cmp::Ordering;
1011
use std::iter::{FromIterator, Map};
@@ -138,25 +139,83 @@ pub fn get_deprecated_attr_type(attr_type: u8) -> Option<&'static str> {
138139
pub struct Attributes {
139140
// Black box type to allow for later changes/optimizations. The most common attributes could be
140141
// added as fields to allow for easier lookup.
141-
pub(crate) inner: Vec<Attribute>,
142+
inner: Vec<Attribute>,
143+
144+
// Direct fields for well-known mandatory path attributes.
145+
// ORIGIN is a well-known mandatory attribute
146+
origin: Option<Attribute>,
147+
// AS_PATH is a well-known mandatory attribute
148+
as_path: Option<Attribute>,
149+
// NEXT_HOP is a well-known mandatory attribute
150+
next_hop: Option<Attribute>,
151+
142152
/// RFC 7606 validation warnings collected during parsing
143153
pub(crate) validation_warnings: Vec<BgpValidationWarning>,
144154
}
145155

156+
struct WellKnownMandatoryAndOtherAttributes {
157+
origin: Option<Attribute>,
158+
as_path: Option<Attribute>,
159+
next_hop: Option<Attribute>,
160+
inner: Vec<Attribute>,
161+
}
162+
163+
impl FromIterator<Attribute> for WellKnownMandatoryAndOtherAttributes {
164+
fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
165+
let iter = iter.into_iter();
166+
167+
let mut origin = None;
168+
let mut as_path = None;
169+
let mut next_hop = None;
170+
let mut inner = Vec::with_capacity(iter.size_hint().0.max(253));
171+
172+
for attr in iter {
173+
match attr.value.attr_type() {
174+
AttrType::ORIGIN => origin = Some(attr),
175+
AttrType::AS_PATH => as_path = Some(attr),
176+
AttrType::NEXT_HOP => next_hop = Some(attr),
177+
_ => inner.push(attr),
178+
}
179+
}
180+
181+
WellKnownMandatoryAndOtherAttributes {
182+
origin,
183+
as_path,
184+
next_hop,
185+
inner,
186+
}
187+
}
188+
}
189+
146190
impl Attributes {
147191
pub fn has_attr(&self, ty: AttrType) -> bool {
148-
self.inner.iter().any(|x| x.value.attr_type() == ty)
192+
match ty {
193+
AttrType::ORIGIN => self.origin.is_some(),
194+
AttrType::AS_PATH => self.as_path.is_some(),
195+
AttrType::NEXT_HOP => self.next_hop.is_some(),
196+
_ => self.inner.iter().any(|x| x.value.attr_type() == ty)
197+
}
149198
}
150199

151200
pub fn get_attr(&self, ty: AttrType) -> Option<Attribute> {
152-
self.inner
153-
.iter()
154-
.find(|x| x.value.attr_type() == ty)
155-
.cloned()
201+
match ty {
202+
AttrType::ORIGIN => self.origin.clone(),
203+
AttrType::AS_PATH => self.as_path.clone(),
204+
AttrType::NEXT_HOP => self.next_hop.clone(),
205+
_ => self.inner
206+
.iter()
207+
.find(|x| x.value.attr_type() == ty)
208+
.cloned()
209+
}
156210
}
157211

158212
pub fn add_attr(&mut self, attr: Attribute) {
159-
self.inner.push(attr);
213+
match attr.value.attr_type() {
214+
AttrType::ORIGIN => self.origin = Some(attr),
215+
AttrType::AS_PATH => self.as_path = Some(attr),
216+
AttrType::NEXT_HOP => self.next_hop = Some(attr),
217+
_ => self.inner.push(attr),
218+
}
160219
}
161220

162221
/// Add a validation warning to the attributes
@@ -177,9 +236,8 @@ impl Attributes {
177236
/// Get the `ORIGIN` attribute. In the event that this attribute is not present,
178237
/// [Origin::INCOMPLETE] will be returned instead.
179238
pub fn origin(&self) -> Origin {
180-
self.inner
181-
.iter()
182-
.find_map(|x| match &x.value {
239+
self.origin.as_ref()
240+
.and_then(|x| match &x.value {
183241
AttributeValue::Origin(x) => Some(*x),
184242
_ => None,
185243
})
@@ -199,10 +257,12 @@ impl Attributes {
199257
/// **Note**: Even when this attribute is not present, the next hop address may still be
200258
/// attainable from the `MP_REACH_NLRI` attribute.
201259
pub fn next_hop(&self) -> Option<IpAddr> {
202-
self.inner.iter().find_map(|x| match &x.value {
203-
AttributeValue::NextHop(x) => Some(*x),
204-
_ => None,
205-
})
260+
self.next_hop
261+
.as_ref()
262+
.and_then(|x| match &x.value {
263+
AttributeValue::NextHop(x) => Some(*x),
264+
_ => None,
265+
})
206266
}
207267

208268
pub fn multi_exit_discriminator(&self) -> Option<u32> {
@@ -250,12 +310,12 @@ impl Attributes {
250310

251311
// These implementations are horribly inefficient, but they were super easy to write and use
252312
pub fn as_path(&self) -> Option<&AsPath> {
253-
// Begin searching at the end of the attributes to increase the odds of finding an AS4
254-
// attribute first.
255-
self.inner.iter().rev().find_map(|x| match &x.value {
256-
AttributeValue::AsPath { path, .. } => Some(path),
257-
_ => None,
258-
})
313+
self.as_path
314+
.as_ref()
315+
.and_then(|x| match &x.value {
316+
AttributeValue::AsPath { path, .. } => Some(path),
317+
_ => None,
318+
})
259319
}
260320

261321
pub fn get_reachable_nlri(&self) -> Option<&Nlri> {
@@ -288,7 +348,12 @@ impl Attributes {
288348
/// Get an iterator over the held [Attribute]s. If you do no not need attribute flags, consider
289349
/// using [Attributes::iter] instead.
290350
pub fn into_attributes_iter(self) -> impl Iterator<Item = Attribute> {
291-
self.inner.into_iter()
351+
chain!(
352+
self.as_path.into_iter(),
353+
self.origin.into_iter(),
354+
self.next_hop.into_iter(),
355+
self.inner.into_iter(),
356+
)
292357
}
293358
}
294359

@@ -326,17 +391,27 @@ impl Iterator for MetaCommunitiesIter<'_> {
326391

327392
impl FromIterator<Attribute> for Attributes {
328393
fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
394+
let attrs = WellKnownMandatoryAndOtherAttributes::from_iter(iter);
395+
329396
Attributes {
330-
inner: iter.into_iter().collect(),
397+
origin: attrs.origin,
398+
as_path: attrs.as_path,
399+
next_hop: attrs.next_hop,
400+
inner: attrs.inner,
331401
validation_warnings: Vec::new(),
332402
}
333403
}
334404
}
335405

336406
impl From<Vec<Attribute>> for Attributes {
337407
fn from(value: Vec<Attribute>) -> Self {
408+
let attrs = WellKnownMandatoryAndOtherAttributes::from_iter(value.into_iter());
409+
338410
Attributes {
339-
inner: value,
411+
origin: attrs.origin,
412+
as_path: attrs.as_path,
413+
next_hop: attrs.next_hop,
414+
inner: attrs.inner,
340415
validation_warnings: Vec::new(),
341416
}
342417
}
@@ -356,14 +431,20 @@ impl Extend<AttributeValue> for Attributes {
356431

357432
impl FromIterator<AttributeValue> for Attributes {
358433
fn from_iter<T: IntoIterator<Item = AttributeValue>>(iter: T) -> Self {
359-
Attributes {
360-
inner: iter
434+
let attrs = WellKnownMandatoryAndOtherAttributes::from_iter(
435+
iter
361436
.into_iter()
362437
.map(|value| Attribute {
363438
value,
364439
flag: AttrFlags::empty(),
365440
})
366-
.collect(),
441+
);
442+
443+
Attributes {
444+
origin: attrs.origin,
445+
as_path: attrs.as_path,
446+
next_hop: attrs.next_hop,
447+
inner: attrs.inner,
367448
validation_warnings: Vec::new(),
368449
}
369450
}
@@ -383,7 +464,14 @@ impl<'a> IntoIterator for &'a Attributes {
383464
type IntoIter = Map<Iter<'a, Attribute>, fn(&Attribute) -> &AttributeValue>;
384465

385466
fn into_iter(self) -> Self::IntoIter {
386-
self.inner.iter().map(|x| &x.value)
467+
let tmp: Vec<_> = chain!(
468+
self.as_path.iter(),
469+
self.origin.iter(),
470+
self.next_hop.iter(),
471+
self.inner.iter()
472+
).collect();
473+
474+
tmp.as_slice().into_iter().map(|x| &x.value)
387475
}
388476
}
389477

src/parser/bgp/attributes/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ impl Attribute {
459459
impl Attributes {
460460
pub fn encode(&self, asn_len: AsnLength) -> Bytes {
461461
let mut bytes = BytesMut::new();
462-
for attr in &self.inner {
462+
for attr in &self.iter() {
463463
bytes.extend(attr.encode(asn_len));
464464
}
465465
bytes.freeze()

0 commit comments

Comments
 (0)