1use crate::models::Asn;
2use num_enum::{FromPrimitive, IntoPrimitive};
3use std::fmt::{Display, Formatter};
4use std::net::{Ipv4Addr, Ipv6Addr};
5
6#[derive(Debug, PartialEq, Copy, Clone, Eq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[cfg_attr(feature = "serde", serde(untagged))]
9pub enum MetaCommunity {
10 Plain(Community),
11 Extended(ExtendedCommunity),
12 Ipv6Extended(Ipv6AddrExtCommunity),
13 Large(LargeCommunity),
14}
15
16#[derive(Debug, PartialEq, Copy, Clone, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18pub enum Community {
19 NoExport,
20 NoAdvertise,
21 NoExportSubConfed,
22 Custom(Asn, u16),
23}
24
25#[derive(Debug, PartialEq, Clone, Copy, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct LargeCommunity {
33 pub global_admin: u32,
34 pub local_data: [u32; 2],
35}
36
37impl LargeCommunity {
38 pub fn new(global_admin: u32, local_data: [u32; 2]) -> LargeCommunity {
39 LargeCommunity {
40 global_admin,
41 local_data,
42 }
43 }
44}
45
46#[derive(Debug, FromPrimitive, IntoPrimitive, PartialEq, Eq, Hash, Copy, Clone)]
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49#[repr(u8)]
50pub enum ExtendedCommunityType {
51 TransitiveTwoOctetAs = 0x00,
53 TransitiveIpv4Addr = 0x01,
54 TransitiveFourOctetAs = 0x02,
55 TransitiveOpaque = 0x03,
56
57 NonTransitiveTwoOctetAs = 0x40,
59 NonTransitiveIpv4Addr = 0x41,
60 NonTransitiveFourOctetAs = 0x42,
61 NonTransitiveOpaque = 0x43,
62 #[num_enum(catch_all)]
64 Unknown(u8),
65}
66
67#[derive(Debug, PartialEq, Clone, Copy, Eq)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96pub enum ExtendedCommunity {
97 TransitiveTwoOctetAs(TwoOctetAsExtCommunity),
98 TransitiveIpv4Addr(Ipv4AddrExtCommunity),
99 TransitiveFourOctetAs(FourOctetAsExtCommunity),
100 TransitiveOpaque(OpaqueExtCommunity),
101 NonTransitiveTwoOctetAs(TwoOctetAsExtCommunity),
102 NonTransitiveIpv4Addr(Ipv4AddrExtCommunity),
103 NonTransitiveFourOctetAs(FourOctetAsExtCommunity),
104 NonTransitiveOpaque(OpaqueExtCommunity),
105 Raw([u8; 8]),
106}
107
108impl ExtendedCommunity {
109 pub const fn community_type(&self) -> ExtendedCommunityType {
110 use ExtendedCommunityType::*;
111 match self {
112 ExtendedCommunity::TransitiveTwoOctetAs(_) => TransitiveTwoOctetAs,
113 ExtendedCommunity::TransitiveIpv4Addr(_) => TransitiveIpv4Addr,
114 ExtendedCommunity::TransitiveFourOctetAs(_) => TransitiveFourOctetAs,
115 ExtendedCommunity::TransitiveOpaque(_) => TransitiveOpaque,
116 ExtendedCommunity::NonTransitiveTwoOctetAs(_) => NonTransitiveTwoOctetAs,
117 ExtendedCommunity::NonTransitiveIpv4Addr(_) => NonTransitiveIpv4Addr,
118 ExtendedCommunity::NonTransitiveFourOctetAs(_) => NonTransitiveFourOctetAs,
119 ExtendedCommunity::NonTransitiveOpaque(_) => NonTransitiveOpaque,
120 ExtendedCommunity::Raw(buffer) => Unknown(buffer[0]),
121 }
122 }
123}
124
125#[derive(Debug, PartialEq, Clone, Copy, Eq)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127pub struct Ipv6AddrExtCommunity {
128 pub community_type: ExtendedCommunityType,
129 pub subtype: u8,
130 pub global_admin: Ipv6Addr,
132 pub local_admin: [u8; 2],
134}
135
136#[derive(Debug, PartialEq, Clone, Copy, Eq)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141pub struct TwoOctetAsExtCommunity {
142 pub subtype: u8,
143 pub global_admin: Asn,
145 pub local_admin: [u8; 4],
147}
148
149#[derive(Debug, PartialEq, Clone, Copy, Eq)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154pub struct FourOctetAsExtCommunity {
155 pub subtype: u8,
156 pub global_admin: Asn,
158 pub local_admin: [u8; 2],
160}
161
162#[derive(Debug, PartialEq, Clone, Copy, Eq)]
166#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
167pub struct Ipv4AddrExtCommunity {
168 pub subtype: u8,
169 pub global_admin: Ipv4Addr,
171 pub local_admin: [u8; 2],
173}
174
175#[derive(Debug, PartialEq, Clone, Copy, Eq)]
179#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
180pub struct OpaqueExtCommunity {
181 pub subtype: u8,
182 pub value: [u8; 6],
184}
185
186struct ToHexString<'a>(&'a [u8]);
191
192impl Display for ToHexString<'_> {
193 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
194 for byte in self.0 {
195 write!(f, "{:02X}", byte)?;
196 }
197 Ok(())
198 }
199}
200
201impl Display for Community {
202 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
203 match self {
204 Community::NoExport => write!(f, "no-export"),
205 Community::NoAdvertise => write!(f, "no-advertise"),
206 Community::NoExportSubConfed => write!(f, "no-export-sub-confed"),
207 Community::Custom(asn, value) => write!(f, "{}:{}", asn, value),
208 }
209 }
210}
211
212impl Display for LargeCommunity {
213 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
214 write!(
215 f,
216 "{}:{}:{}",
217 self.global_admin, self.local_data[0], self.local_data[1]
218 )
219 }
220}
221
222impl Display for ExtendedCommunity {
223 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
224 let ec_type = u8::from(self.community_type());
225 match self {
226 ExtendedCommunity::TransitiveTwoOctetAs(ec)
227 | ExtendedCommunity::NonTransitiveTwoOctetAs(ec) => {
228 write!(
229 f,
230 "{}:{}:{}:{}",
231 ec_type,
232 ec.subtype,
233 ec.global_admin,
234 ToHexString(&ec.local_admin)
235 )
236 }
237 ExtendedCommunity::TransitiveIpv4Addr(ec)
238 | ExtendedCommunity::NonTransitiveIpv4Addr(ec) => {
239 write!(
240 f,
241 "{}:{}:{}:{}",
242 ec_type,
243 ec.subtype,
244 ec.global_admin,
245 ToHexString(&ec.local_admin)
246 )
247 }
248 ExtendedCommunity::TransitiveFourOctetAs(ec)
249 | ExtendedCommunity::NonTransitiveFourOctetAs(ec) => {
250 write!(
251 f,
252 "{}:{}:{}:{}",
253 ec_type,
254 ec.subtype,
255 ec.global_admin,
256 ToHexString(&ec.local_admin)
257 )
258 }
259 ExtendedCommunity::TransitiveOpaque(ec)
260 | ExtendedCommunity::NonTransitiveOpaque(ec) => {
261 write!(f, "{}:{}:{}", ec_type, ec.subtype, ToHexString(&ec.value))
262 }
263 ExtendedCommunity::Raw(ec) => {
264 write!(f, "{}", ToHexString(ec))
265 }
266 }
267 }
268}
269
270impl Display for Ipv6AddrExtCommunity {
271 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
272 write!(
273 f,
274 "{}:{}:{}:{}",
275 u8::from(self.community_type),
276 self.subtype,
277 self.global_admin,
278 ToHexString(&self.local_admin)
279 )
280 }
281}
282
283impl Display for MetaCommunity {
284 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
285 match self {
286 MetaCommunity::Plain(c) => write!(f, "{}", c),
287 MetaCommunity::Extended(c) => write!(f, "{}", c),
288 MetaCommunity::Large(c) => write!(f, "{}", c),
289 MetaCommunity::Ipv6Extended(c) => write!(f, "{}", c),
290 }
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn test_large_community_new() {
300 let global_admin = 56;
301 let local_data = [3, 4];
302 let large_comm = LargeCommunity::new(global_admin, local_data);
303 assert_eq!(large_comm.global_admin, global_admin);
304 assert_eq!(large_comm.local_data, local_data);
305 }
306
307 #[test]
308 fn test_extended_community_community_type() {
309 let two_octet_as_ext_comm = TwoOctetAsExtCommunity {
310 subtype: 0,
311 global_admin: Asn::new_32bit(0),
312 local_admin: [0; 4],
313 };
314 let extended_community = ExtendedCommunity::TransitiveTwoOctetAs(two_octet_as_ext_comm);
315 assert_eq!(
316 extended_community.community_type(),
317 ExtendedCommunityType::TransitiveTwoOctetAs
318 );
319 }
320
321 #[test]
322 fn test_display_community() {
323 assert_eq!(format!("{}", Community::NoExport), "no-export");
324 assert_eq!(format!("{}", Community::NoAdvertise), "no-advertise");
325 assert_eq!(
326 format!("{}", Community::NoExportSubConfed),
327 "no-export-sub-confed"
328 );
329 assert_eq!(
330 format!("{}", Community::Custom(Asn::new_32bit(64512), 100)),
331 "64512:100"
332 );
333 }
334
335 #[test]
336 fn test_display_large_community() {
337 let large_community = LargeCommunity::new(1, [2, 3]);
338 assert_eq!(format!("{}", large_community), "1:2:3");
339 }
340
341 #[test]
342 fn test_display_extended_community() {
343 let two_octet_as_ext_comm = TwoOctetAsExtCommunity {
344 subtype: 0,
345 global_admin: Asn::new_32bit(0),
346 local_admin: [0; 4],
347 };
348 let extended_community = ExtendedCommunity::TransitiveTwoOctetAs(two_octet_as_ext_comm);
349 assert_eq!(format!("{}", extended_community), "0:0:0:00000000");
350
351 let two_octet_as_ext_comm = TwoOctetAsExtCommunity {
352 subtype: 0,
353 global_admin: Asn::new_32bit(0),
354 local_admin: [0; 4],
355 };
356 let extended_community = ExtendedCommunity::NonTransitiveTwoOctetAs(two_octet_as_ext_comm);
357 assert_eq!(format!("{}", extended_community), "64:0:0:00000000");
358
359 let ipv4_ext_comm = Ipv4AddrExtCommunity {
360 subtype: 1,
361 global_admin: "192.168.1.1".parse().unwrap(),
362 local_admin: [5, 6],
363 };
364 let extended_community = ExtendedCommunity::TransitiveIpv4Addr(ipv4_ext_comm);
365 assert_eq!(format!("{}", extended_community), "1:1:192.168.1.1:0506");
366
367 let ipv4_ext_comm = Ipv4AddrExtCommunity {
368 subtype: 1,
369 global_admin: "192.168.1.1".parse().unwrap(),
370 local_admin: [5, 6],
371 };
372 let extended_community = ExtendedCommunity::NonTransitiveIpv4Addr(ipv4_ext_comm);
373 assert_eq!(format!("{}", extended_community), "65:1:192.168.1.1:0506");
374
375 let four_octet_as_ext_comm = FourOctetAsExtCommunity {
376 subtype: 2,
377 global_admin: Asn::new_32bit(64512),
378 local_admin: [7, 8],
379 };
380 let extended_community = ExtendedCommunity::TransitiveFourOctetAs(four_octet_as_ext_comm);
381 assert_eq!(format!("{}", extended_community), "2:2:64512:0708");
382
383 let four_octet_as_ext_comm = FourOctetAsExtCommunity {
384 subtype: 2,
385 global_admin: Asn::new_32bit(64512),
386 local_admin: [7, 8],
387 };
388 let extended_community =
389 ExtendedCommunity::NonTransitiveFourOctetAs(four_octet_as_ext_comm);
390 assert_eq!(format!("{}", extended_community), "66:2:64512:0708");
391
392 let opaque_ext_comm = OpaqueExtCommunity {
393 subtype: 3,
394 value: [9, 10, 11, 12, 13, 14],
395 };
396 let extended_community = ExtendedCommunity::TransitiveOpaque(opaque_ext_comm);
397 assert_eq!(format!("{}", extended_community), "3:3:090A0B0C0D0E");
398
399 let opaque_ext_comm = OpaqueExtCommunity {
400 subtype: 3,
401 value: [9, 10, 11, 12, 13, 14],
402 };
403 let extended_community = ExtendedCommunity::NonTransitiveOpaque(opaque_ext_comm);
404 assert_eq!(format!("{}", extended_community), "67:3:090A0B0C0D0E");
405
406 let raw_ext_comm = [0, 1, 2, 3, 4, 5, 6, 7];
407 let extended_community = ExtendedCommunity::Raw(raw_ext_comm);
408 assert_eq!(format!("{}", extended_community), "0001020304050607");
409 }
410
411 #[test]
412 fn test_display_ipv6_addr_ext_community() {
413 let ipv6_addr_ext_comm = Ipv6AddrExtCommunity {
414 community_type: ExtendedCommunityType::TransitiveTwoOctetAs,
415 subtype: 0,
416 global_admin: "2001:db8::8a2e:370:7334".parse().unwrap(),
417 local_admin: [0, 1],
418 };
419 assert_eq!(
420 format!("{}", ipv6_addr_ext_comm),
421 "0:0:2001:db8::8a2e:370:7334:0001"
422 );
423 }
424
425 #[test]
426 fn test_display_meta_community() {
427 let large_community = LargeCommunity::new(1, [2, 3]);
428 let meta_community = MetaCommunity::Large(large_community);
429 assert_eq!(format!("{}", meta_community), "1:2:3");
430 }
431
432 #[test]
433 #[cfg(feature = "serde")]
434 fn test_serde() {
435 let meta_community = MetaCommunity::Large(LargeCommunity::new(1, [2, 3]));
436 let serialized = serde_json::to_string(&meta_community).unwrap();
437 let deserialized: MetaCommunity = serde_json::from_str(&serialized).unwrap();
438 assert_eq!(meta_community, deserialized);
439
440 let meta_community = MetaCommunity::Extended(ExtendedCommunity::TransitiveTwoOctetAs(
441 TwoOctetAsExtCommunity {
442 subtype: 0,
443 global_admin: Asn::new_32bit(0),
444 local_admin: [0; 4],
445 },
446 ));
447 let serialized = serde_json::to_string(&meta_community).unwrap();
448 let deserialized: MetaCommunity = serde_json::from_str(&serialized).unwrap();
449 assert_eq!(meta_community, deserialized);
450
451 let meta_community = MetaCommunity::Plain(Community::NoExport);
452 let serialized = serde_json::to_string(&meta_community).unwrap();
453 let deserialized: MetaCommunity = serde_json::from_str(&serialized).unwrap();
454 assert_eq!(meta_community, deserialized);
455
456 let meta_community = MetaCommunity::Ipv6Extended(Ipv6AddrExtCommunity {
457 community_type: ExtendedCommunityType::TransitiveTwoOctetAs,
458 subtype: 0,
459 global_admin: "2001:db8::8a2e:370:7334".parse().unwrap(),
460 local_admin: [0, 1],
461 });
462 let serialized = serde_json::to_string(&meta_community).unwrap();
463 let deserialized: MetaCommunity = serde_json::from_str(&serialized).unwrap();
464 assert_eq!(meta_community, deserialized);
465 }
466}