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