Skip to content

Commit 58da276

Browse files
committed
bgp: add otc attribute
- Introduce the Only To Customer (OTC) optional transitive attribute. - Move RoleName to holo-utils/bgp.rs Signed-off-by: Paul Wekesa <paul1tw1@gmail.com>
1 parent bb20cac commit 58da276

7 files changed

Lines changed: 120 additions & 65 deletions

File tree

holo-bgp/src/neighbor.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::time::Duration;
1313
use arbitrary::Arbitrary;
1414
use chrono::{DateTime, Utc};
1515
use holo_protocol::InstanceChannelsTx;
16-
use holo_utils::bgp::{AfiSafi, RouteType, WellKnownCommunities};
16+
use holo_utils::bgp::{AfiSafi, RoleName, RouteType, WellKnownCommunities};
1717
use holo_utils::ibus::IbusChannelsTx;
1818
use holo_utils::socket::{TTL_MAX, TcpConnInfo, TcpStream};
1919
use holo_utils::task::{IntervalTask, Task, TimeoutTask};
@@ -30,8 +30,7 @@ use crate::northbound::notification;
3030
use crate::northbound::rpc::ClearType;
3131
use crate::packet::attribute::Attrs;
3232
use crate::packet::consts::{
33-
AS_TRANS, Afi, BGP_VERSION, CeaseSubcode, ErrorCode, FsmErrorSubcode,
34-
RoleName, Safi,
33+
AS_TRANS, Afi, BGP_VERSION, CeaseSubcode, ErrorCode, FsmErrorSubcode, Safi,
3534
};
3635
use crate::packet::message::{
3736
Capability, DecodeCxt, EncodeCxt, KeepaliveMsg, Message,

holo-bgp/src/northbound/configuration.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use holo_northbound::configuration::{
1616
Callbacks, CallbacksBuilder, Provider, ValidationCallbacks,
1717
ValidationCallbacksBuilder,
1818
};
19-
use holo_utils::bgp::AfiSafi;
19+
use holo_utils::bgp::{AfiSafi, RoleName};
2020
use holo_utils::ip::{AddressFamily, IpAddrKind};
2121
use holo_utils::policy::{ApplyPolicyCfg, DefaultPolicyType};
2222
use holo_utils::protocol::Protocol;
@@ -28,7 +28,7 @@ use crate::instance::{Instance, InstanceUpView};
2828
use crate::neighbor::{Neighbor, PeerType, fsm};
2929
use crate::network;
3030
use crate::northbound::yang_gen::bgp;
31-
use crate::packet::consts::{CeaseSubcode, ErrorCode, RoleName};
31+
use crate::packet::consts::{CeaseSubcode, ErrorCode};
3232
use crate::packet::message::{Message, NotificationMsg};
3333
use crate::rib::RouteOrigin;
3434

holo-bgp/src/northbound/yang.rs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::northbound::configuration::{
1616
use crate::packet::consts::{
1717
AddPathMode, AsPathSegmentType, CapabilityCode, CeaseSubcode, ErrorCode,
1818
FsmErrorSubcode, MessageHeaderErrorSubcode, OpenMessageErrorSubcode,
19-
RoleName, RouteRefreshErrorSubcode, Safi, UpdateMessageErrorSubcode,
19+
RouteRefreshErrorSubcode, Safi, UpdateMessageErrorSubcode,
2020
};
2121
use crate::packet::message::NotificationMsg;
2222
use crate::rib::{RouteIneligibleReason, RouteOrigin, RouteRejectReason};
@@ -335,19 +335,6 @@ impl ToYang for RouteRejectReason {
335335

336336
// ===== TryFromYang implementations =====
337337

338-
impl TryFromYang for RoleName {
339-
fn try_from_yang(value: &str) -> Option<RoleName> {
340-
match value {
341-
"provider" => Some(RoleName::Provider),
342-
"customer" => Some(RoleName::Customer),
343-
"peer" => Some(RoleName::Peer),
344-
"rs-client" => Some(RoleName::RsClient),
345-
"rs" => Some(RoleName::Rs),
346-
_ => None,
347-
}
348-
}
349-
}
350-
351338
impl TryFromYang for PrivateAsRemove {
352339
fn try_from_yang(value: &str) -> Option<PrivateAsRemove> {
353340
match value {

holo-bgp/src/packet/attribute.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub struct BaseAttrs {
5858
pub atomic_aggregate: Option<()>,
5959
pub originator_id: Option<Ipv4Addr>,
6060
pub cluster_list: Option<ClusterList>,
61+
pub otc: Option<u32>,
6162
}
6263

6364
#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
@@ -181,6 +182,11 @@ impl Attrs {
181182
atomic_aggregate::encode(buf);
182183
}
183184

185+
// Only To Customer (OTC) attribute.
186+
if let Some(otc) = self.base.otc {
187+
otc::encode(otc, buf);
188+
}
189+
184190
// AGGREGATOR attribute.
185191
if let Some(aggregator) = &self.base.aggregator {
186192
aggregator.encode(
@@ -278,6 +284,7 @@ impl Attrs {
278284
let mut ext_comm = None;
279285
let mut extv6_comm = None;
280286
let mut large_comm = None;
287+
let mut otc = None;
281288
let mut unknown = vec![];
282289
let mut withdraw = false;
283290

@@ -452,6 +459,7 @@ impl Attrs {
452459
AttrType::LargeCommunity => {
453460
LargeComms::decode(&mut buf, &mut large_comm)
454461
}
462+
AttrType::Otc => otc::decode(&mut buf, &mut otc),
455463
} {
456464
// Log malformed attribute.
457465
Debug::NbrAttrError(attr_type, error).log();
@@ -493,6 +501,7 @@ impl Attrs {
493501
atomic_aggregate,
494502
originator_id,
495503
cluster_list,
504+
otc,
496505
},
497506
comm,
498507
ext_comm,
@@ -549,6 +558,9 @@ impl Attrs {
549558
if let Some(large_comm) = &self.large_comm {
550559
length += large_comm.length();
551560
}
561+
if self.base.otc.is_some() {
562+
length += otc::length();
563+
}
552564

553565
length
554566
}
@@ -865,6 +877,37 @@ mod med {
865877
}
866878
}
867879

880+
// ==== Only To Customer(OTC) attribute ====
881+
882+
mod otc {
883+
use super::*;
884+
const LEN: u8 = 4;
885+
886+
pub(super) fn encode(otc: u32, buf: &mut BytesMut) {
887+
buf.put_u8(AttrFlags::OPTIONAL.bits());
888+
buf.put_u8(AttrType::Otc as u8);
889+
buf.put_u8(LEN);
890+
buf.put_u32(otc);
891+
}
892+
893+
pub(super) fn decode(
894+
buf: &mut Bytes,
895+
otc: &mut Option<u32>,
896+
) -> Result<(), AttrError> {
897+
if buf.remaining() != LEN as usize {
898+
return Err(AttrError::Withdraw);
899+
}
900+
901+
let value = buf.try_get_u32()?;
902+
*otc = Some(value);
903+
Ok(())
904+
}
905+
906+
pub(super) fn length() -> u16 {
907+
ATTR_MIN_LEN + LEN as u16
908+
}
909+
}
910+
868911
// ===== LOCAL_PREF attribute =====
869912

870913
mod local_pref {
@@ -1450,9 +1493,8 @@ fn attribute_flags(attr_type: AttrType) -> AttrFlags {
14501493
| AttrType::As4Path
14511494
| AttrType::As4Aggregator
14521495
| AttrType::Extv6Community
1453-
| AttrType::LargeCommunity => {
1454-
AttrFlags::TRANSITIVE | AttrFlags::OPTIONAL
1455-
}
1496+
| AttrType::LargeCommunity
1497+
| AttrType::Otc => AttrFlags::TRANSITIVE | AttrFlags::OPTIONAL,
14561498
}
14571499
}
14581500

holo-bgp/src/packet/consts.rs

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
// SPDX-License-Identifier: MIT
55
//
66

7-
use std::collections::HashMap;
8-
97
use arbitrary::Arbitrary;
108
use bitflags::bitflags;
119
use holo_utils::ip::AddressFamily;
@@ -318,46 +316,6 @@ pub enum AttrType {
318316
//AttrSet = 128,
319317
}
320318

321-
// Roles as defined in RFC 9234.
322-
#[derive(Clone, Copy, Ord, Debug, Eq, Hash, PartialEq, PartialOrd)]
323-
#[derive(FromPrimitive, ToPrimitive)]
324-
#[derive(Deserialize, Serialize)]
325-
pub enum RoleName {
326-
Provider = 0,
327-
Rs = 1,
328-
RsClient = 2,
329-
Customer = 3,
330-
Peer = 4, // i.e Lateral Peer.
331-
}
332-
333-
impl RoleName {
334-
fn allowed_role() -> HashMap<Self, Self> {
335-
HashMap::from([
336-
(RoleName::Provider, RoleName::Customer),
337-
(RoleName::Customer, RoleName::Provider),
338-
(RoleName::Rs, RoleName::RsClient),
339-
(RoleName::RsClient, RoleName::Rs),
340-
(RoleName::Peer, RoleName::Peer),
341-
])
342-
}
343-
344-
// Maps the Local AS Role and the Remote AS Role.
345-
// If the Roles do not match, there will be a NotificationMsg that will be
346-
// sent back.
347-
// RFC 9234 section 4.2 Table 2.
348-
pub fn validate_role_correctness(
349-
local_role: &RoleName,
350-
remote_role: &RoleName,
351-
) -> bool {
352-
if let Some(approved_role) = Self::allowed_role().get(local_role)
353-
&& approved_role == remote_role
354-
{
355-
return true;
356-
}
357-
false
358-
}
359-
}
360-
361319
// BGP Origin.
362320
pub type Origin = holo_utils::bgp::Origin;
363321

holo-bgp/src/packet/message.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::net::{Ipv4Addr, Ipv6Addr};
1010
use arbitrary::Arbitrary;
1111
use bytes::{Buf, BufMut, Bytes, BytesMut};
1212
use enum_as_inner::EnumAsInner;
13+
use holo_utils::bgp::RoleName;
1314
use holo_utils::bytes::{BytesExt, BytesMutExt, TLS_BUF};
1415
use holo_utils::ip::{
1516
Ipv4AddrExt, Ipv4NetworkExt, Ipv6AddrExt, Ipv6NetworkExt,
@@ -24,7 +25,7 @@ use crate::packet::attribute::Attrs;
2425
use crate::packet::consts::{
2526
AddPathMode, Afi, BGP_VERSION, CapabilityCode, ErrorCode,
2627
MessageHeaderErrorSubcode, MessageType, OpenMessageErrorSubcode,
27-
OpenParamType, RoleName, Safi, UpdateMessageErrorSubcode,
28+
OpenParamType, Safi, UpdateMessageErrorSubcode,
2829
};
2930
use crate::packet::error::{
3031
DecodeError, MessageHeaderError, OpenMessageError, UpdateMessageError,

holo-utils/src/bgp.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//! eliminating the need for shared definitions.
1313
1414
use std::borrow::Cow;
15+
use std::collections::HashMap;
1516
use std::net::Ipv6Addr;
1617

1718
use holo_yang::{ToYang, TryFromYang};
@@ -77,6 +78,18 @@ pub enum WellKnownCommunities {
7778
NoExportSubconfed = 0xFFFFFF03,
7879
}
7980

81+
// Roles as defined in RFC 9234.
82+
#[derive(Clone, Copy, Ord, Debug, Eq, Hash, PartialEq, PartialOrd)]
83+
#[derive(FromPrimitive, ToPrimitive)]
84+
#[derive(Deserialize, Serialize)]
85+
pub enum RoleName {
86+
Provider = 0,
87+
Rs = 1,
88+
RsClient = 2,
89+
Customer = 3,
90+
Peer = 4, // i.e Lateral Peer.
91+
}
92+
8093
// ===== impl AfiSafi =====
8194

8295
impl ToYang for AfiSafi {
@@ -268,3 +281,58 @@ impl TryFromYang for LargeComm {
268281
None
269282
}
270283
}
284+
285+
// ==== impl RoleName ====
286+
287+
impl RoleName {
288+
fn allowed_role() -> HashMap<Self, Self> {
289+
HashMap::from([
290+
(RoleName::Provider, RoleName::Customer),
291+
(RoleName::Customer, RoleName::Provider),
292+
(RoleName::Rs, RoleName::RsClient),
293+
(RoleName::RsClient, RoleName::Rs),
294+
(RoleName::Peer, RoleName::Peer),
295+
])
296+
}
297+
298+
// Maps the Local AS Role and the Remote AS Role.
299+
// If the Roles do not match, there will be a NotificationMsg that will be
300+
// sent back.
301+
// RFC 9234 section 4.2 Table 2.
302+
pub fn validate_role_correctness(
303+
local_role: &RoleName,
304+
remote_role: &RoleName,
305+
) -> bool {
306+
if let Some(approved_role) = Self::allowed_role().get(local_role)
307+
&& approved_role == remote_role
308+
{
309+
return true;
310+
}
311+
false
312+
}
313+
}
314+
315+
impl TryFromYang for RoleName {
316+
fn try_from_yang(value: &str) -> Option<RoleName> {
317+
match value {
318+
"provider" => Some(RoleName::Provider),
319+
"customer" => Some(RoleName::Customer),
320+
"peer" => Some(RoleName::Peer),
321+
"rs-client" => Some(RoleName::RsClient),
322+
"rs" => Some(RoleName::Rs),
323+
_ => None,
324+
}
325+
}
326+
}
327+
328+
impl ToYang for RoleName {
329+
fn to_yang(&self) -> Cow<'static, str> {
330+
match self {
331+
RoleName::Provider => "provider".into(),
332+
RoleName::Customer => "customer".into(),
333+
RoleName::Peer => "peer".into(),
334+
RoleName::RsClient => "rs-client".into(),
335+
RoleName::Rs => "rs".into(),
336+
}
337+
}
338+
}

0 commit comments

Comments
 (0)