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