1use crate::models::*;
2use ipnet::IpNet;
3use std::fmt::Debug;
4use std::net::IpAddr;
5
6#[derive(Debug, PartialEq, Clone, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS), ts(export))]
10pub struct Nlri {
11 pub afi: Afi,
12 pub safi: Safi,
13 pub next_hop: Option<NextHopAddress>,
14 #[cfg_attr(
18 feature = "ts-rs",
19 ts(type = "(string | { prefix: string; path_id: number })[]")
20 )]
21 pub prefixes: Vec<NetworkPrefix>,
22 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>[] | null"))]
25 pub labeled_prefixes: Option<Vec<LabeledNetworkPrefix>>,
26 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>[] | null"))]
28 pub link_state_nlris: Option<Vec<crate::models::bgp::linkstate::LinkStateNlri>>,
29 #[cfg_attr(feature = "ts-rs", ts(type = "Record<string, unknown>[] | null"))]
31 pub flowspec_nlris: Option<Vec<crate::models::bgp::flowspec::FlowSpecNlri>>,
32}
33
34impl Nlri {
35 pub const fn is_ipv4(&self) -> bool {
37 matches!(self.afi, Afi::Ipv4)
38 }
39
40 pub const fn is_ipv6(&self) -> bool {
42 matches!(self.afi, Afi::Ipv6)
43 }
44
45 pub const fn is_link_state(&self) -> bool {
47 matches!(self.afi, Afi::LinkState)
48 }
49
50 pub const fn is_flowspec(&self) -> bool {
52 matches!(self.safi, Safi::FlowSpec | Safi::FlowSpecL3Vpn)
53 }
54
55 pub const fn is_mpls_labeled(&self) -> bool {
57 matches!(self.safi, Safi::MplsLabel)
58 }
59
60 pub const fn is_reachable(&self) -> bool {
62 self.next_hop.is_some()
63 }
64
65 pub const fn next_hop_addr(&self) -> IpAddr {
70 match self.next_hop {
71 Some(next_hop) => next_hop.global_addr(),
72 None => panic!("unreachable NLRI"),
73 }
74 }
75
76 pub fn new_reachable(prefix: NetworkPrefix, next_hop: Option<IpAddr>) -> Nlri {
77 let next_hop = next_hop.map(NextHopAddress::from);
78 let afi = match prefix.prefix {
79 IpNet::V4(_) => Afi::Ipv4,
80 IpNet::V6(_) => Afi::Ipv6,
81 };
82 let safi = Safi::Unicast;
83 Nlri {
84 afi,
85 safi,
86 next_hop,
87 prefixes: vec![prefix],
88 labeled_prefixes: None,
89 link_state_nlris: None,
90 flowspec_nlris: None,
91 }
92 }
93
94 pub fn new_unreachable(prefix: NetworkPrefix) -> Nlri {
95 let afi = match prefix.prefix {
96 IpNet::V4(_) => Afi::Ipv4,
97 IpNet::V6(_) => Afi::Ipv6,
98 };
99 let safi = Safi::Unicast;
100 Nlri {
101 afi,
102 safi,
103 next_hop: None,
104 prefixes: vec![prefix],
105 labeled_prefixes: None,
106 link_state_nlris: None,
107 flowspec_nlris: None,
108 }
109 }
110
111 pub fn new_link_state_reachable(
112 next_hop: Option<IpAddr>,
113 safi: Safi,
114 nlri_list: Vec<crate::models::bgp::linkstate::LinkStateNlri>,
115 ) -> Nlri {
116 let next_hop = next_hop.map(NextHopAddress::from);
117 Nlri {
118 afi: Afi::LinkState,
119 safi,
120 next_hop,
121 prefixes: Vec::new(),
122 labeled_prefixes: None,
123 link_state_nlris: Some(nlri_list),
124 flowspec_nlris: None,
125 }
126 }
127
128 pub fn new_link_state_unreachable(
129 safi: Safi,
130 nlri_list: Vec<crate::models::bgp::linkstate::LinkStateNlri>,
131 ) -> Nlri {
132 Nlri {
133 afi: Afi::LinkState,
134 safi,
135 next_hop: None,
136 prefixes: Vec::new(),
137 labeled_prefixes: None,
138 link_state_nlris: Some(nlri_list),
139 flowspec_nlris: None,
140 }
141 }
142
143 pub fn new_flowspec_reachable(
145 afi: Afi,
146 safi: Safi,
147 next_hop: Option<IpAddr>,
148 flowspec_nlris: Vec<crate::models::bgp::flowspec::FlowSpecNlri>,
149 ) -> Nlri {
150 let next_hop = next_hop.map(NextHopAddress::from);
151 Nlri {
152 afi,
153 safi,
154 next_hop,
155 prefixes: Vec::new(),
156 labeled_prefixes: None,
157 link_state_nlris: None,
158 flowspec_nlris: Some(flowspec_nlris),
159 }
160 }
161
162 pub fn new_flowspec_unreachable(
164 afi: Afi,
165 safi: Safi,
166 flowspec_nlris: Vec<crate::models::bgp::flowspec::FlowSpecNlri>,
167 ) -> Nlri {
168 Nlri {
169 afi,
170 safi,
171 next_hop: None,
172 prefixes: Vec::new(),
173 labeled_prefixes: None,
174 link_state_nlris: None,
175 flowspec_nlris: Some(flowspec_nlris),
176 }
177 }
178
179 pub fn new_labeled_reachable(
181 afi: Afi,
182 next_hop: Option<IpAddr>,
183 labeled_prefixes: Vec<LabeledNetworkPrefix>,
184 ) -> Nlri {
185 let next_hop = next_hop.map(NextHopAddress::from);
186 Nlri {
187 afi,
188 safi: Safi::MplsLabel,
189 next_hop,
190 prefixes: Vec::new(),
191 labeled_prefixes: Some(labeled_prefixes),
192 link_state_nlris: None,
193 flowspec_nlris: None,
194 }
195 }
196
197 pub fn new_labeled_unreachable(afi: Afi, prefixes: Vec<NetworkPrefix>) -> Nlri {
200 Nlri {
201 afi,
202 safi: Safi::MplsLabel,
203 next_hop: None,
204 prefixes,
205 labeled_prefixes: None,
206 link_state_nlris: None,
207 flowspec_nlris: None,
208 }
209 }
210}
211
212impl IntoIterator for Nlri {
219 type Item = IpNet;
220 type IntoIter = std::vec::IntoIter<IpNet>;
221
222 fn into_iter(self) -> Self::IntoIter {
223 let mut result: Vec<IpNet> = self.prefixes.into_iter().map(|x| x.prefix).collect();
224
225 if let Some(labeled) = self.labeled_prefixes {
227 for labeled_prefix in labeled {
228 result.push(labeled_prefix.prefix);
229 }
230 }
231
232 result.into_iter()
233 }
234}
235
236impl<'a> IntoIterator for &'a Nlri {
237 type Item = &'a IpNet;
238 type IntoIter = std::vec::IntoIter<&'a IpNet>;
239
240 fn into_iter(self) -> Self::IntoIter {
241 let mut result: Vec<&'a IpNet> = self.prefixes.iter().map(|x| &x.prefix).collect();
242
243 if let Some(ref labeled) = self.labeled_prefixes {
245 for labeled_prefix in labeled {
246 result.push(&labeled_prefix.prefix);
247 }
248 }
249
250 result.into_iter()
251 }
252}
253
254#[derive(Debug, PartialEq, Clone, Eq)]
255#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
256pub struct MpReachableNlri {
257 afi: Afi,
258 safi: Safi,
259 next_hop: NextHopAddress,
260 prefixes: Vec<NetworkPrefix>,
261}
262
263impl MpReachableNlri {
264 pub fn new(
265 afi: Afi,
266 safi: Safi,
267 next_hop: NextHopAddress,
268 prefixes: Vec<NetworkPrefix>,
269 ) -> MpReachableNlri {
270 MpReachableNlri {
271 afi,
272 safi,
273 next_hop,
274 prefixes,
275 }
276 }
277}
278
279#[derive(Debug, PartialEq, Clone, Eq)]
280#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
281pub struct MpUnreachableNlri {
282 afi: Afi,
283 safi: Safi,
284 prefixes: Vec<NetworkPrefix>,
285}
286
287impl MpUnreachableNlri {
288 pub fn new(afi: Afi, safi: Safi, prefixes: Vec<NetworkPrefix>) -> MpUnreachableNlri {
289 MpUnreachableNlri {
290 afi,
291 safi,
292 prefixes,
293 }
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300 use std::str::FromStr;
301
302 #[test]
303 fn nlri_is_ipv4() {
304 let nlri = Nlri::new_reachable(
305 NetworkPrefix::from_str("10.0.2.0/24").unwrap(),
306 Some("10.0.2.1".parse().unwrap()),
307 );
308
309 assert!(nlri.is_ipv4());
310 }
311
312 #[test]
313 fn nlri_is_ipv6() {
314 let nlri = Nlri::new_unreachable(NetworkPrefix::from_str("2001:db8::/32").unwrap());
315
316 assert!(nlri.is_ipv6());
317 }
318
319 #[test]
320 fn nlri_is_reachable() {
321 let nlri = Nlri::new_reachable(
322 NetworkPrefix::from_str("10.0.2.0/24").unwrap(),
323 Some("10.0.2.1".parse().unwrap()),
324 );
325
326 assert!(nlri.is_reachable());
327 }
328
329 #[test]
330 #[should_panic]
331 fn nlri_next_hop_addr_unreachable() {
332 let nlri = Nlri::new_unreachable(NetworkPrefix::from_str("10.0.2.0/24").unwrap());
333
334 let _ = nlri.next_hop_addr();
335 }
336
337 #[test]
338 fn mp_reachable_nlri_new() {
339 let next_hop_addr = IpAddr::from_str("10.0.2.1").unwrap();
340 let nlri = MpReachableNlri::new(
341 Afi::Ipv4,
342 Safi::Unicast,
343 NextHopAddress::from(next_hop_addr),
344 vec![NetworkPrefix::from_str("10.0.2.0/24").unwrap()],
345 );
346
347 assert_eq!(nlri.afi, Afi::Ipv4);
348 assert_eq!(nlri.safi, Safi::Unicast);
349 assert_eq!(nlri.next_hop.addr(), next_hop_addr);
350 assert_eq!(nlri.prefixes.len(), 1);
351 }
352
353 #[test]
354 fn mp_unreachable_nlri_new() {
355 let nlri = MpUnreachableNlri::new(
356 Afi::Ipv4,
357 Safi::Unicast,
358 vec![NetworkPrefix::from_str("10.0.2.0/24").unwrap()],
359 );
360
361 assert_eq!(nlri.afi, Afi::Ipv4);
362 assert_eq!(nlri.safi, Safi::Unicast);
363 assert_eq!(nlri.prefixes.len(), 1);
364 }
365
366 #[test]
367 fn nlri_link_state_creation() {
368 use crate::models::bgp::linkstate::{LinkStateNlri, NodeDescriptor, ProtocolId};
369
370 let node_desc = NodeDescriptor {
371 autonomous_system: Some(65001),
372 ..Default::default()
373 };
374
375 let ls_nlri = LinkStateNlri::new_node_nlri(ProtocolId::Ospfv2, 123456, node_desc);
376 let nlri = Nlri::new_link_state_reachable(
377 Some("192.168.1.1".parse().unwrap()),
378 Safi::LinkState,
379 vec![ls_nlri],
380 );
381
382 assert!(nlri.is_link_state());
383 assert!(nlri.is_reachable());
384 assert_eq!(nlri.afi, Afi::LinkState);
385 assert_eq!(nlri.safi, Safi::LinkState);
386 }
387
388 #[test]
389 fn nlri_link_state_unreachable() {
390 use crate::models::bgp::linkstate::{LinkStateNlri, NodeDescriptor, ProtocolId};
391
392 let node_desc = NodeDescriptor::default();
393 let ls_nlri = LinkStateNlri::new_node_nlri(ProtocolId::Ospfv2, 123456, node_desc);
394 let nlri = Nlri::new_link_state_unreachable(Safi::LinkState, vec![ls_nlri]);
395
396 assert!(nlri.is_link_state());
397 assert!(!nlri.is_reachable());
398 assert_eq!(nlri.afi, Afi::LinkState);
399 assert_eq!(nlri.safi, Safi::LinkState);
400 }
401
402 #[test]
403 #[should_panic]
404 fn nlri_link_state_next_hop_addr_unreachable() {
405 use crate::models::bgp::linkstate::{LinkStateNlri, NodeDescriptor, ProtocolId};
406
407 let node_desc = NodeDescriptor::default();
408 let ls_nlri = LinkStateNlri::new_node_nlri(ProtocolId::Ospfv2, 123456, node_desc);
409 let nlri = Nlri::new_link_state_unreachable(Safi::LinkState, vec![ls_nlri]);
410
411 let _ = nlri.next_hop_addr();
412 }
413
414 #[test]
415 fn nlri_flowspec_creation() {
416 use crate::models::bgp::flowspec::{FlowSpecComponent, FlowSpecNlri};
417 use std::str::FromStr;
418
419 let component =
420 FlowSpecComponent::DestinationPrefix(NetworkPrefix::from_str("192.0.2.0/24").unwrap());
421 let flowspec_nlri = FlowSpecNlri::new(vec![component]);
422
423 let nlri = Nlri::new_flowspec_reachable(
424 Afi::Ipv4,
425 Safi::FlowSpec,
426 Some("192.0.2.1".parse().unwrap()),
427 vec![flowspec_nlri],
428 );
429
430 assert!(nlri.is_flowspec());
431 assert!(nlri.is_reachable());
432 assert!(nlri.is_ipv4());
433 assert_eq!(nlri.afi, Afi::Ipv4);
434 assert_eq!(nlri.safi, Safi::FlowSpec);
435 assert_eq!(nlri.prefixes.len(), 0); assert!(nlri.flowspec_nlris.is_some());
437 assert_eq!(nlri.flowspec_nlris.as_ref().unwrap().len(), 1);
438 }
439
440 #[test]
441 fn nlri_flowspec_unreachable() {
442 use crate::models::bgp::flowspec::{FlowSpecComponent, FlowSpecNlri};
443 use std::str::FromStr;
444
445 let component =
446 FlowSpecComponent::DestinationPrefix(NetworkPrefix::from_str("2001:db8::/32").unwrap());
447 let flowspec_nlri = FlowSpecNlri::new(vec![component]);
448
449 let nlri = Nlri::new_flowspec_unreachable(Afi::Ipv6, Safi::FlowSpec, vec![flowspec_nlri]);
450
451 assert!(nlri.is_flowspec());
452 assert!(!nlri.is_reachable());
453 assert!(nlri.is_ipv6());
454 assert_eq!(nlri.afi, Afi::Ipv6);
455 assert_eq!(nlri.safi, Safi::FlowSpec);
456 assert!(nlri.flowspec_nlris.is_some());
457 assert_eq!(nlri.flowspec_nlris.as_ref().unwrap().len(), 1);
458 }
459
460 #[test]
461 fn nlri_flowspec_l3vpn() {
462 use crate::models::bgp::flowspec::{FlowSpecComponent, FlowSpecNlri, NumericOperator};
463 use std::str::FromStr;
464
465 let components = vec![
466 FlowSpecComponent::DestinationPrefix(NetworkPrefix::from_str("10.0.0.0/8").unwrap()),
467 FlowSpecComponent::IpProtocol(vec![NumericOperator::equal_to(6)]), ];
469 let flowspec_nlri = FlowSpecNlri::new(components);
470
471 let nlri = Nlri::new_flowspec_reachable(
472 Afi::Ipv4,
473 Safi::FlowSpecL3Vpn,
474 None, vec![flowspec_nlri],
476 );
477
478 assert!(nlri.is_flowspec());
479 assert!(!nlri.is_reachable());
480 assert!(nlri.is_ipv4());
481 assert_eq!(nlri.safi, Safi::FlowSpecL3Vpn);
482 }
483
484 #[test]
485 #[should_panic]
486 fn nlri_flowspec_next_hop_addr_unreachable() {
487 use crate::models::bgp::flowspec::{FlowSpecComponent, FlowSpecNlri};
488 use std::str::FromStr;
489
490 let component =
491 FlowSpecComponent::DestinationPrefix(NetworkPrefix::from_str("192.0.2.0/24").unwrap());
492 let flowspec_nlri = FlowSpecNlri::new(vec![component]);
493
494 let nlri = Nlri::new_flowspec_unreachable(Afi::Ipv4, Safi::FlowSpec, vec![flowspec_nlri]);
495
496 let _ = nlri.next_hop_addr();
497 }
498}