Skip to content

Commit 498b61d

Browse files
authored
Merge pull request #48 from nrybowski/isis_bier
isis: Add support for RFC8401
2 parents e4a5187 + 718281e commit 498b61d

File tree

13 files changed

+388
-27
lines changed

13 files changed

+388
-27
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,9 @@ Holo supports the following Internet Standards:
174174
* RFC 5304 - IS-IS Cryptographic Authentication
175175
* RFC 5305 - IS-IS Extensions for Traffic Engineering
176176
* RFC 5308 - Routing IPv6 with IS-IS
177+
* RFC 8401 - Bit Index Explicit Replication (BIER) Support via IS-IS
177178
* RFC 8405 - Shortest Path First (SPF) Back-Off Delay Algorithm for Link-State IGPs
179+
* draft-ietf-bier-lsr-non-mpls-extensions-03 - LSR Extensions for BIER non-MPLS Encapsulation
178180

179181
##### MPLS LDP
180182

holo-isis/src/instance.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,10 @@ async fn process_ibus_msg(
632632
IbusMsg::HostnameUpdate(hostname) => {
633633
events::process_hostname_update(instance, hostname);
634634
}
635+
// BIER configuration update.
636+
IbusMsg::BierCfgUpd(bier_config) => {
637+
instance.shared.bier_config = bier_config.clone();
638+
}
635639
// Ignore other events.
636640
_ => {}
637641
}

holo-isis/src/lsdb.rs

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@ use std::time::Instant;
1414
use bitflags::bitflags;
1515
use derive_new::new;
1616
use holo_utils::UnboundedSender;
17+
use holo_utils::bier::{
18+
BierEncapId, BierEncapsulationType, BierInBiftId, BiftId,
19+
UnderlayProtocolType,
20+
};
1721
use holo_utils::ip::{AddressFamily, Ipv4NetworkExt, Ipv6NetworkExt};
22+
use holo_utils::mpls::Label;
1823
use holo_utils::task::TimeoutTask;
19-
use ipnetwork::{Ipv4Network, Ipv6Network};
24+
use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
2025

2126
use crate::adjacency::AdjacencyState;
2227
use crate::collections::{Arena, LspEntryId};
@@ -26,9 +31,12 @@ use crate::interface::{Interface, InterfaceType};
2631
use crate::northbound::notification;
2732
use crate::packet::consts::LspFlags;
2833
use crate::packet::pdu::{Lsp, LspTlvs, Pdu};
34+
use crate::packet::subtlvs::prefix::{
35+
BierEncapSubSubTlv, BierInfoSubTlv, BierSubSubTlv,
36+
};
2937
use crate::packet::tlv::{
30-
ExtIpv4Reach, ExtIsReach, IpReachTlvEntry, Ipv4Reach, Ipv6Reach, IsReach,
31-
MAX_NARROW_METRIC, Nlpid,
38+
ExtIpv4Reach, ExtIsReach, IpReachTlvEntry, Ipv4Reach, Ipv6Reach,
39+
Ipv6ReachSubTlvs, IsReach, MAX_NARROW_METRIC, Nlpid,
3240
};
3341
use crate::packet::{LanId, LevelNumber, LevelType, LspId};
3442
use crate::spf::{SpfType, VertexId};
@@ -211,6 +219,7 @@ fn lsp_build_tlvs(
211219
let mut ext_ipv4_reach = BTreeMap::new();
212220
let mut ipv6_addrs = BTreeSet::new();
213221
let mut ipv6_reach = BTreeMap::new();
222+
let bier_config = &instance.shared.bier_config;
214223

215224
// Add supported protocols.
216225
if instance.config.is_af_enabled(AddressFamily::Ipv4) {
@@ -329,14 +338,83 @@ fn lsp_build_tlvs(
329338
ipv6_addrs.insert(addr.ip());
330339

331340
let prefix = addr.apply_mask();
341+
342+
let mut sub_tlvs: Ipv6ReachSubTlvs = Default::default();
343+
344+
// Add BIER Sub-TLV(s) if BIER is enabled and allowed to
345+
// advertise
346+
if instance.config.bier.enabled
347+
&& instance.config.bier.advertise
348+
{
349+
bier_config
350+
.sd_cfg
351+
.iter()
352+
.filter(|((_, af), sd_cfg)| {
353+
af == &AddressFamily::Ipv6
354+
&& sd_cfg.bfr_prefix == IpNetwork::V6(prefix)
355+
// Enforce RFC8401 Section 4.2
356+
&& sd_cfg.bfr_prefix.prefix() == 128
357+
&& sd_cfg.underlay_protocol
358+
== UnderlayProtocolType::IsIs
359+
})
360+
.for_each(|((sd_id, _), sd_cfg)| {
361+
let bier_encaps = sd_cfg
362+
.encap
363+
.iter()
364+
.filter_map(|((bsl, encap_type), encap)| {
365+
match encap_type {
366+
BierEncapsulationType::Mpls => {
367+
// TODO: where is the label defined?
368+
Some(BierEncapId::Mpls(Label::new(
369+
0,
370+
)))
371+
}
372+
_ => match encap.in_bift_id {
373+
BierInBiftId::Base(id) => Some(id),
374+
BierInBiftId::Encoding(true) => {
375+
Some(0)
376+
}
377+
_ => None,
378+
}
379+
.map(|id| {
380+
BierEncapId::NonMpls(BiftId::new(
381+
id,
382+
))
383+
}),
384+
}
385+
.map(
386+
|id| {
387+
BierSubSubTlv::BierEncapSubSubTlv(
388+
BierEncapSubSubTlv::new(
389+
encap.max_si,
390+
(*bsl).into(),
391+
id,
392+
),
393+
)
394+
},
395+
)
396+
})
397+
.collect::<Vec<BierSubSubTlv>>();
398+
399+
let bier = BierInfoSubTlv::new(
400+
sd_cfg.bar,
401+
sd_cfg.ipa,
402+
*sd_id,
403+
sd_cfg.bfr_id,
404+
bier_encaps,
405+
);
406+
sub_tlvs.bier.push(bier);
407+
})
408+
}
409+
332410
ipv6_reach.insert(
333411
prefix,
334412
Ipv6Reach {
335413
metric,
336414
up_down: false,
337415
external: false,
338416
prefix,
339-
sub_tlvs: Default::default(),
417+
sub_tlvs,
340418
},
341419
);
342420
}

holo-isis/src/northbound/configuration.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ pub struct InstanceCfg {
100100
pub overload_status: bool,
101101
pub att_suppress: bool,
102102
pub att_ignore: bool,
103+
pub bier: BierIsisCfg,
104+
}
105+
106+
#[derive(Debug)]
107+
pub struct BierIsisCfg {
108+
pub mt_id: u8,
109+
pub enabled: bool,
110+
pub advertise: bool,
111+
pub receive: bool,
103112
}
104113

105114
#[derive(Debug)]
@@ -1132,6 +1141,36 @@ fn load_callbacks() -> Callbacks<Instance> {
11321141
let af = AddressFamily::try_from_yang(&af).unwrap();
11331142
ListEntry::InterfaceAddressFamily(iface_idx, af)
11341143
})
1144+
.path(isis::bier::mt_id::PATH)
1145+
.modify_apply(|instance, args| {
1146+
let mt_id = args.dnode.get_u8();
1147+
instance.config.bier.mt_id = mt_id;
1148+
1149+
// TODO: should reoriginate LSP
1150+
})
1151+
.delete_apply(|instance, _args| {
1152+
let mt_id = 0;
1153+
instance.config.bier.mt_id = mt_id;
1154+
})
1155+
.path(isis::bier::bier::enable::PATH)
1156+
.modify_apply(|instance, args| {
1157+
let enable = args.dnode.get_bool();
1158+
instance.config.bier.enabled = enable;
1159+
1160+
let event_queue = args.event_queue;
1161+
event_queue.insert(Event::ReoriginateLsps(LevelNumber::L1));
1162+
event_queue.insert(Event::ReoriginateLsps(LevelNumber::L2));
1163+
})
1164+
.path(isis::bier::bier::advertise::PATH)
1165+
.modify_apply(|instance, args| {
1166+
let advertise = args.dnode.get_bool();
1167+
instance.config.bier.advertise = advertise;
1168+
})
1169+
.path(isis::bier::bier::receive::PATH)
1170+
.modify_apply(|instance, args| {
1171+
let receive = args.dnode.get_bool();
1172+
instance.config.bier.receive = receive;
1173+
})
11351174
.build()
11361175
}
11371176

@@ -1484,6 +1523,21 @@ impl Default for InstanceCfg {
14841523
overload_status,
14851524
att_suppress,
14861525
att_ignore,
1526+
bier: Default::default(),
1527+
}
1528+
}
1529+
}
1530+
1531+
impl Default for BierIsisCfg {
1532+
fn default() -> Self {
1533+
let enabled = isis::bier::bier::enable::DFLT;
1534+
let advertise = isis::bier::bier::advertise::DFLT;
1535+
let receive = isis::bier::bier::receive::DFLT;
1536+
Self {
1537+
mt_id: 0,
1538+
enabled,
1539+
advertise,
1540+
receive,
14871541
}
14881542
}
14891543
}

holo-isis/src/packet/consts.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,22 @@ pub enum NeighborSubTlvType {
9999
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
100100
#[derive(FromPrimitive, ToPrimitive)]
101101
#[derive(Deserialize, Serialize)]
102-
pub enum PrefixSubTlvType {}
102+
pub enum PrefixSubTlvType {
103+
BierInfo = 32,
104+
}
105+
106+
// IS-IS Sub-Sub-TLVs for BIER Info Sub-TLV.
107+
//
108+
// IANA Registry:
109+
// https://www.iana.org/assignments/isis-tlv-codepoints/isis-tlv-codepoints.xhtml#bier-info-sub-tlv
110+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
111+
#[derive(FromPrimitive, ToPrimitive)]
112+
#[derive(Deserialize, Serialize)]
113+
pub enum BierSubSubTlvType {
114+
MplsEncap = 1,
115+
// FIXME: TBD1 in https://datatracker.ietf.org/doc/html/draft-ietf-bier-lsr-non-mpls-extensions-03#name-is-is-bier-non-mpls-encapsu
116+
NonMplsEncap = 42,
117+
}
103118

104119
// IS-IS LSP flags field.
105120
bitflags! {

holo-isis/src/packet/subtlvs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
//
99

1010
pub mod neighbor;
11+
pub mod prefix;
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use bytes::{Buf, BufMut, Bytes, BytesMut};
2+
use derive_new::new;
3+
use holo_utils::bier::{BierEncapId, BiftId};
4+
use holo_utils::bytes::{BytesExt, BytesMutExt};
5+
use holo_utils::mpls::Label;
6+
use num_traits::FromPrimitive;
7+
use serde::{Deserialize, Serialize};
8+
9+
use crate::packet::consts::{BierSubSubTlvType, PrefixSubTlvType};
10+
use crate::packet::error::{DecodeError, DecodeResult};
11+
use crate::packet::tlv::{TLV_HDR_SIZE, tlv_encode_end, tlv_encode_start};
12+
13+
#[derive(Clone, Debug, PartialEq)]
14+
#[derive(new)]
15+
#[derive(Deserialize, Serialize)]
16+
pub struct BierInfoSubTlv {
17+
pub bar: u8,
18+
pub ipa: u8,
19+
pub sub_domain_id: u8,
20+
pub bfr_id: u16,
21+
pub subtlvs: Vec<BierSubSubTlv>,
22+
}
23+
24+
#[derive(Clone, Debug, PartialEq)]
25+
#[derive(new)]
26+
#[derive(Deserialize, Serialize)]
27+
pub enum BierSubSubTlv {
28+
BierEncapSubSubTlv(BierEncapSubSubTlv),
29+
}
30+
31+
#[derive(Clone, Debug, PartialEq)]
32+
#[derive(new)]
33+
#[derive(Deserialize, Serialize)]
34+
pub struct BierEncapSubSubTlv {
35+
pub max_si: u8,
36+
pub bs_len: u8,
37+
pub id: BierEncapId,
38+
}
39+
40+
// ===== impl BierInfoSubTlv =====
41+
42+
impl BierInfoSubTlv {
43+
const ENTRY_MIN_SIZE: usize = 5;
44+
45+
pub(crate) fn decode(tlv_len: u8, buf: &mut Bytes) -> DecodeResult<Self> {
46+
if tlv_len < Self::ENTRY_MIN_SIZE as u8 {
47+
return Err(DecodeError::InvalidTlvLength(tlv_len));
48+
}
49+
let bar = buf.get_u8();
50+
let ipa = buf.get_u8();
51+
let sub_domain_id = buf.get_u8();
52+
let bfr_id = buf.get_u16();
53+
54+
let mut subtlvs: Vec<BierSubSubTlv> = Vec::new();
55+
56+
while buf.remaining() >= TLV_HDR_SIZE {
57+
// Parse SubTlv type.
58+
let stlv_type = buf.get_u8();
59+
let stlv_etype = BierSubSubTlvType::from_u8(stlv_type);
60+
61+
// Parse and validate SubTlv length.
62+
let stlv_len = buf.get_u8();
63+
if stlv_len as usize > buf.remaining() {
64+
return Err(DecodeError::InvalidTlvLength(stlv_len));
65+
}
66+
67+
// Parse SubTlv value.
68+
let mut buf_stlv = buf.copy_to_bytes(stlv_len as usize);
69+
match stlv_etype {
70+
Some(
71+
BierSubSubTlvType::MplsEncap
72+
| BierSubSubTlvType::NonMplsEncap,
73+
) => {
74+
let max_si = buf_stlv.get_u8();
75+
let id = buf_stlv.get_u24();
76+
let bs_len = ((id >> 20) & 0xf) as u8;
77+
let id = match stlv_etype.unwrap() {
78+
BierSubSubTlvType::MplsEncap => {
79+
BierEncapId::Mpls(Label::new(id))
80+
}
81+
BierSubSubTlvType::NonMplsEncap => {
82+
BierEncapId::NonMpls(BiftId::new(id))
83+
}
84+
};
85+
subtlvs.push(BierSubSubTlv::BierEncapSubSubTlv(
86+
BierEncapSubSubTlv { max_si, bs_len, id },
87+
));
88+
}
89+
_ => {
90+
// Igore unknown Sub-TLV
91+
continue;
92+
}
93+
}
94+
}
95+
96+
Ok(BierInfoSubTlv {
97+
bar,
98+
ipa,
99+
sub_domain_id,
100+
bfr_id,
101+
subtlvs,
102+
})
103+
}
104+
105+
pub(crate) fn encode(&self, buf: &mut BytesMut) {
106+
let start_pos = tlv_encode_start(buf, PrefixSubTlvType::BierInfo);
107+
buf.put_u8(self.bar);
108+
buf.put_u8(self.ipa);
109+
buf.put_u8(self.sub_domain_id);
110+
buf.put_u16(self.bfr_id);
111+
for subtlv in &self.subtlvs {
112+
match subtlv {
113+
BierSubSubTlv::BierEncapSubSubTlv(encap) => {
114+
let tlv_type = match encap.id {
115+
BierEncapId::NonMpls(_) => {
116+
BierSubSubTlvType::NonMplsEncap
117+
}
118+
BierEncapId::Mpls(_) => BierSubSubTlvType::MplsEncap,
119+
};
120+
let start_pos = tlv_encode_start(buf, tlv_type);
121+
buf.put_u8(encap.max_si);
122+
buf.put_u24(
123+
(encap.id.clone().get() & 0x0fffff)
124+
| ((encap.bs_len as u32 | 0xf) << 20),
125+
);
126+
tlv_encode_end(buf, start_pos);
127+
}
128+
}
129+
}
130+
tlv_encode_end(buf, start_pos);
131+
}
132+
}

0 commit comments

Comments
 (0)