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