1use std::fmt::{Debug, Display, Formatter};
2use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3
4#[derive(PartialEq, Copy, Clone, Eq, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct RouteDistinguisher(pub [u8; 8]);
9
10impl Debug for RouteDistinguisher {
11 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
12 write!(
13 f,
14 "RD({:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x})",
15 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7]
16 )
17 }
18}
19
20impl Display for RouteDistinguisher {
21 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
22 write!(
23 f,
24 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
25 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7]
26 )
27 }
28}
29
30#[derive(PartialEq, Copy, Clone, Eq, Hash)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36pub enum NextHopAddress {
37 Ipv4(Ipv4Addr),
38 Ipv6(Ipv6Addr),
39 Ipv6LinkLocal(Ipv6Addr, Ipv6Addr),
40 VpnIpv6(RouteDistinguisher, Ipv6Addr),
43 VpnIpv6LinkLocal(RouteDistinguisher, Ipv6Addr, RouteDistinguisher, Ipv6Addr),
46}
47
48impl NextHopAddress {
49 pub const fn is_link_local(&self) -> bool {
51 match self {
52 NextHopAddress::Ipv4(x) => x.is_link_local(),
53 NextHopAddress::Ipv6(x) => (x.segments()[0] & 0xffc0) == 0xfe80,
54 NextHopAddress::Ipv6LinkLocal(_, _) => true,
55 NextHopAddress::VpnIpv6(_, x) => (x.segments()[0] & 0xffc0) == 0xfe80,
56 NextHopAddress::VpnIpv6LinkLocal(_, _, _, _) => true,
57 }
58 }
59
60 pub const fn addr(&self) -> IpAddr {
62 match self {
63 NextHopAddress::Ipv4(x) => IpAddr::V4(*x),
64 NextHopAddress::Ipv6(x) => IpAddr::V6(*x),
65 NextHopAddress::Ipv6LinkLocal(x, _) => IpAddr::V6(*x),
66 NextHopAddress::VpnIpv6(_, x) => IpAddr::V6(*x),
67 NextHopAddress::VpnIpv6LinkLocal(_, x, _, _) => IpAddr::V6(*x),
68 }
69 }
70}
71
72impl Debug for NextHopAddress {
74 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75 match self {
76 NextHopAddress::Ipv4(x) => write!(f, "{x}"),
77 NextHopAddress::Ipv6(x) => write!(f, "{x}"),
78 NextHopAddress::Ipv6LinkLocal(x, y) => write!(f, "Ipv6LinkLocal({x}, {y})"),
79 NextHopAddress::VpnIpv6(rd, x) => write!(f, "VpnIpv6({rd}, {x})"),
80 NextHopAddress::VpnIpv6LinkLocal(rd1, x, rd2, y) => {
81 write!(f, "VpnIpv6LinkLocal({rd1}, {x}, {rd2}, {y})")
82 }
83 }
84 }
85}
86
87impl Display for NextHopAddress {
88 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
89 match self {
90 NextHopAddress::Ipv4(v) => write!(f, "{v}"),
91 NextHopAddress::Ipv6(v) => write!(f, "{v}"),
92 NextHopAddress::Ipv6LinkLocal(v, _) => write!(f, "{v}"),
93 NextHopAddress::VpnIpv6(_, v) => write!(f, "{v}"),
94 NextHopAddress::VpnIpv6LinkLocal(_, v, _, _) => write!(f, "{v}"),
95 }
96 }
97}
98
99impl From<IpAddr> for NextHopAddress {
100 fn from(value: IpAddr) -> Self {
101 match value {
102 IpAddr::V4(x) => NextHopAddress::Ipv4(x),
103 IpAddr::V6(x) => NextHopAddress::Ipv6(x),
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
112
113 #[test]
114 fn test_next_hop_address_is_link_local() {
115 let ipv4_addr = Ipv4Addr::new(169, 254, 0, 1);
116 let ipv6_addr = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0);
117 let ipv6_link_local_addrs = (
118 Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 1),
119 Ipv6Addr::new(0xfe80, 0, 0, 2, 0, 0, 0, 1),
120 );
121
122 let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
123 let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
124 let next_hop_ipv6_link_local =
125 NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
126
127 assert!(next_hop_ipv4.is_link_local());
128 assert!(next_hop_ipv6.is_link_local());
129 assert!(next_hop_ipv6_link_local.is_link_local());
130 }
131
132 #[test]
133 fn test_next_hop_address_addr() {
134 let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
135 let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
136 let ipv6_link_local_addrs = (
137 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
138 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
139 );
140
141 let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
142 let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
143 let next_hop_ipv6_link_local =
144 NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
145
146 assert_eq!(next_hop_ipv4.addr(), IpAddr::V4(ipv4_addr));
147 assert_eq!(next_hop_ipv6.addr(), IpAddr::V6(ipv6_addr));
148 assert_eq!(
149 next_hop_ipv6_link_local.addr(),
150 IpAddr::V6(ipv6_link_local_addrs.0)
151 );
152 }
153
154 #[test]
155 fn test_next_hop_address_from() {
156 let ipv4_addr = IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1));
157 let ipv6_addr = IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1));
158
159 let next_hop_ipv4 = NextHopAddress::from(ipv4_addr);
160 let next_hop_ipv6 = NextHopAddress::from(ipv6_addr);
161
162 assert_eq!(next_hop_ipv4.addr(), ipv4_addr);
163 assert_eq!(next_hop_ipv6.addr(), ipv6_addr);
164 }
165
166 #[test]
167 fn test_debug_for_next_hop_address() {
168 let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
169 let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
170 let ipv6_link_local_addrs = (
171 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
172 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
173 );
174
175 let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
176 let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
177 let next_hop_ipv6_link_local =
178 NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
179
180 assert_eq!(format!("{next_hop_ipv4:?}"), "192.0.2.1");
181 assert_eq!(format!("{next_hop_ipv6:?}"), "2001:db8::1");
182 assert_eq!(
183 format!("{next_hop_ipv6_link_local:?}"),
184 "Ipv6LinkLocal(fe80::, fe80::)"
185 );
186 }
187
188 #[test]
189 fn test_display_for_next_hop_address() {
190 let ipv4_addr = Ipv4Addr::new(192, 0, 2, 1);
191 let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
192 let ipv6_link_local_addrs = (
193 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
194 Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
195 );
196
197 let next_hop_ipv4 = NextHopAddress::Ipv4(ipv4_addr);
198 let next_hop_ipv6 = NextHopAddress::Ipv6(ipv6_addr);
199 let next_hop_ipv6_link_local =
200 NextHopAddress::Ipv6LinkLocal(ipv6_link_local_addrs.0, ipv6_link_local_addrs.1);
201
202 assert_eq!(format!("{next_hop_ipv4}"), "192.0.2.1");
203 assert_eq!(format!("{next_hop_ipv6}"), "2001:db8::1");
204 assert_eq!(format!("{next_hop_ipv6_link_local}"), "fe80::");
205 }
206
207 #[test]
208 fn test_route_distinguisher() {
209 let rd = RouteDistinguisher([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
210
211 assert_eq!(format!("{rd:?}"), "RD(00:01:02:03:04:05:06:07)");
213
214 assert_eq!(format!("{rd}"), "00:01:02:03:04:05:06:07");
216 }
217
218 #[test]
219 fn test_vpn_next_hop_address() {
220 let rd = RouteDistinguisher([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
221 let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
222 let ipv6_link_local = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
223 let rd2 = RouteDistinguisher([0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17]);
224
225 let vpn_next_hop = NextHopAddress::VpnIpv6(rd, ipv6_addr);
227 assert_eq!(vpn_next_hop.addr(), IpAddr::V6(ipv6_addr));
228 assert!(!vpn_next_hop.is_link_local());
229 assert_eq!(format!("{vpn_next_hop}"), "2001:db8::1");
230 assert_eq!(
231 format!("{vpn_next_hop:?}"),
232 "VpnIpv6(00:01:02:03:04:05:06:07, 2001:db8::1)"
233 );
234
235 let vpn_ll_next_hop = NextHopAddress::VpnIpv6LinkLocal(rd, ipv6_addr, rd2, ipv6_link_local);
237 assert_eq!(vpn_ll_next_hop.addr(), IpAddr::V6(ipv6_addr));
238 assert!(vpn_ll_next_hop.is_link_local()); assert_eq!(format!("{vpn_ll_next_hop}"), "2001:db8::1");
240 assert_eq!(format!("{vpn_ll_next_hop:?}"), "VpnIpv6LinkLocal(00:01:02:03:04:05:06:07, 2001:db8::1, 10:11:12:13:14:15:16:17, fe80::1)");
241
242 let vpn_ll_ip = NextHopAddress::VpnIpv6(rd, ipv6_link_local);
244 assert!(vpn_ll_ip.is_link_local()); }
246}