bgpkit_parser/models/network/
nexthop.rs

1use std::fmt::{Debug, Display, Formatter};
2use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3
4/// enum that represents the type of the next hop address.
5///
6/// [NextHopAddress] is used when parsing for next hops in [Nlri](crate::models::Nlri).
7#[derive(PartialEq, Copy, Clone, Eq, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum NextHopAddress {
10    Ipv4(Ipv4Addr),
11    Ipv6(Ipv6Addr),
12    Ipv6LinkLocal(Ipv6Addr, Ipv6Addr),
13}
14
15impl NextHopAddress {
16    /// Returns true if the next hop is a link local address
17    pub const fn is_link_local(&self) -> bool {
18        match self {
19            NextHopAddress::Ipv4(x) => x.is_link_local(),
20            NextHopAddress::Ipv6(x) => (x.segments()[0] & 0xffc0) == 0xfe80,
21            NextHopAddress::Ipv6LinkLocal(_, _) => true,
22        }
23    }
24
25    /// Returns the address that this next hop points to
26    pub const fn addr(&self) -> IpAddr {
27        match self {
28            NextHopAddress::Ipv4(x) => IpAddr::V4(*x),
29            NextHopAddress::Ipv6(x) => IpAddr::V6(*x),
30            NextHopAddress::Ipv6LinkLocal(x, _) => IpAddr::V6(*x),
31        }
32    }
33}
34
35// Attempt to reduce the size of the debug output
36impl Debug for NextHopAddress {
37    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
38        match self {
39            NextHopAddress::Ipv4(x) => write!(f, "{}", x),
40            NextHopAddress::Ipv6(x) => write!(f, "{}", x),
41            NextHopAddress::Ipv6LinkLocal(x, y) => write!(f, "Ipv6LinkLocal({}, {})", x, y),
42        }
43    }
44}
45
46impl Display for NextHopAddress {
47    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48        match self {
49            NextHopAddress::Ipv4(v) => write!(f, "{}", v),
50            NextHopAddress::Ipv6(v) => write!(f, "{}", v),
51            NextHopAddress::Ipv6LinkLocal(v, _) => write!(f, "{}", v),
52        }
53    }
54}
55
56impl From<IpAddr> for NextHopAddress {
57    fn from(value: IpAddr) -> Self {
58        match value {
59            IpAddr::V4(x) => NextHopAddress::Ipv4(x),
60            IpAddr::V6(x) => NextHopAddress::Ipv6(x),
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
69
70    #[test]
71    fn test_next_hop_address_is_link_local() {
72        let ipv4_addr = Ipv4Addr::new(169, 254, 0, 1);
73        let ipv6_addr = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0);
74        let ipv6_link_local_addrs = (
75            Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 1),
76            Ipv6Addr::new(0xfe80, 0, 0, 2, 0, 0, 0, 1),
77        );
78
79        let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
80        let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
81        let next_hop_ipv6_link_local =
82            NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
83
84        assert!(next_hop_ipv4.is_link_local());
85        assert!(next_hop_ipv6.is_link_local());
86        assert!(next_hop_ipv6_link_local.is_link_local());
87    }
88
89    #[test]
90    fn test_next_hop_address_addr() {
91        let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
92        let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
93        let ipv6_link_local_addrs = (
94            Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
95            Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
96        );
97
98        let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
99        let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
100        let next_hop_ipv6_link_local =
101            NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
102
103        assert_eq!(next_hop_ipv4.addr(), IpAddr::V4(ipv4_addr));
104        assert_eq!(next_hop_ipv6.addr(), IpAddr::V6(ipv6_addr));
105        assert_eq!(
106            next_hop_ipv6_link_local.addr(),
107            IpAddr::V6(ipv6_link_local_addrs.0)
108        );
109    }
110
111    #[test]
112    fn test_next_hop_address_from() {
113        let ipv4_addr = IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1));
114        let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
115
116        let next_hop_ipv4 = NextHopAddress::from(ipv4_addr);
117        let next_hop_ipv6 = NextHopAddress::from(ipv6_addr);
118
119        assert_eq!(next_hop_ipv4.addr(), ipv4_addr);
120        assert_eq!(next_hop_ipv6.addr(), ipv6_addr);
121    }
122
123    #[test]
124    fn test_debug_for_next_hop_address() {
125        let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
126        let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
127        let ipv6_link_local_addrs = (
128            Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
129            Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
130        );
131
132        let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
133        let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
134        let next_hop_ipv6_link_local =
135            NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
136
137        assert_eq!(format!("{:?}", next_hop_ipv4), "192.0.2.1");
138        assert_eq!(format!("{:?}", next_hop_ipv6), "2001:db8::1");
139        assert_eq!(
140            format!("{:?}", next_hop_ipv6_link_local),
141            "Ipv6LinkLocal(fe80::, fe80::)"
142        );
143    }
144
145    #[test]
146    fn test_display_for_next_hop_address() {
147        let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
148        let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
149        let ipv6_link_local_addrs = (
150            Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
151            Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
152        );
153
154        let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
155        let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
156        let next_hop_ipv6_link_local =
157            NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
158
159        assert_eq!(format!("{}", next_hop_ipv4), "192.0.2.1");
160        assert_eq!(format!("{}", next_hop_ipv6), "2001:db8::1");
161        assert_eq!(format!("{}", next_hop_ipv6_link_local), "fe80::");
162    }
163}