bgp_models/bgp/
community.rs

1use crate::network::Asn;
2use enum_primitive_derive::Primitive;
3use serde::Serialize;
4use std::fmt::Formatter;
5use std::net::{Ipv4Addr, Ipv6Addr};
6
7#[derive(Debug, PartialEq, Copy, Clone, Eq)]
8pub enum MetaCommunity {
9    Community(Community),
10    ExtendedCommunity(ExtendedCommunity),
11    LargeCommunity(LargeCommunity),
12}
13
14#[derive(Debug, PartialEq, Copy, Clone, Eq)]
15pub enum Community {
16    NoExport,
17    NoAdvertise,
18    NoExportSubConfed,
19    Custom(Asn, u16),
20}
21
22/// Large community structure as defined in [RFC8092](https://datatracker.ietf.org/doc/html/rfc8092)
23///
24/// ## Display
25///
26/// Large community is displayed as `lg:GLOBAL_ADMINISTRATOR:LOCAL_DATA_1:LOCAL_DATA_2`, where `lg`
27/// is a prefix for large community.
28#[derive(Debug, PartialEq, Clone, Copy, Eq)]
29pub struct LargeCommunity {
30    pub global_administrator: u32,
31    pub local_data: [u32; 2],
32}
33
34impl LargeCommunity {
35    pub fn new(global_administrator: u32, local_data: [u32; 2]) -> LargeCommunity {
36        LargeCommunity {
37            global_administrator,
38            local_data,
39        }
40    }
41}
42
43/// Type definitions of extended communities
44#[derive(Debug, Primitive, PartialEq, Eq, Hash, Copy, Clone)]
45pub enum ExtendedCommunityType {
46    // transitive types
47    TransitiveTwoOctetAsSpecific = 0x00,
48    TransitiveIpv4AddressSpecific = 0x01,
49    TransitiveFourOctetAsSpecific = 0x02,
50    TransitiveOpaque = 0x03,
51
52    // non-transitive types
53    NonTransitiveTwoOctetAsSpecific = 0x40,
54    NonTransitiveIpv4AddressSpecific = 0x41,
55    NonTransitiveFourOctetAsSpecific = 0x42,
56    NonTransitiveOpaque = 0x43,
57    // the rest are either draft or experimental
58}
59
60/// Extended Communities.
61///
62/// ## Overview  
63///
64/// It is a 8-octet data that has flexible definition based on the types:
65/// <https://datatracker.ietf.org/doc/html/rfc4360>
66///
67/// For more up-to-date definitions, see [IANA' website](https://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml).
68///
69/// ```text
70///    Each Extended Community is encoded as an 8-octet quantity, as
71///    follows:
72///
73///       - Type Field  : 1 or 2 octets
74///       - Value Field : Remaining octets
75///
76///        0                   1                   2                   3
77///        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
78///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79///       |  Type high    |  Type low(*)  |                               |
80///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+          Value                |
81///       |                                                               |
82///       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
83///
84///       (*) Present for Extended types only, used for the Value field
85///           otherwise.
86/// ```
87///
88/// ## Display
89///
90/// When output, the extended communities has the following string prefixes to indicate the sub type:
91/// - `ecas2:` stands for `Extended Community AS Specific 2-octet`
92/// - `ecas4:` stands for `Extended Community AS Specific 4-octet`
93/// - `ecv4:` stands for `Extended Community IPv4 Specific`
94/// - `ecv6:` stands for `Extended Community IPv6 Specific`
95/// - `ecop:` stands for `Extended Community Opaque`
96/// - `ecraw:` stands for `Extended Community Raw`
97#[derive(Debug, PartialEq, Clone, Copy, Eq)]
98pub enum ExtendedCommunity {
99    TransitiveTwoOctetAsSpecific(TwoOctetAsSpecific),
100    TransitiveIpv4AddressSpecific(Ipv4AddressSpecific),
101    TransitiveFourOctetAsSpecific(FourOctetAsSpecific),
102    TransitiveOpaque(Opaque),
103    NonTransitiveTwoOctetAsSpecific(TwoOctetAsSpecific),
104    NonTransitiveIpv4AddressSpecific(Ipv4AddressSpecific),
105    NonTransitiveFourOctetAsSpecific(FourOctetAsSpecific),
106    NonTransitiveOpaque(Opaque),
107    Ipv6AddressSpecific(Ipv6AddressSpecific),
108    Raw([u8; 8]),
109}
110
111#[derive(Debug, PartialEq, Clone, Copy, Eq)]
112pub struct Ipv6AddressSpecific {
113    pub ec_type: u8,
114    pub ec_subtype: u8,
115    // 16 octets
116    pub global_administrator: Ipv6Addr,
117    // 2 octets
118    pub local_administrator: [u8; 2],
119}
120
121/// Two-Octet AS Specific Extended Community
122///
123/// <https://datatracker.ietf.org/doc/html/rfc4360#section-3.1>
124#[derive(Debug, PartialEq, Clone, Copy, Eq)]
125pub struct TwoOctetAsSpecific {
126    pub ec_type: u8,
127    pub ec_subtype: u8,
128    // 2 octet
129    pub global_administrator: Asn,
130    // 4 octet
131    pub local_administrator: [u8; 4],
132}
133
134/// Four-Octet AS Specific Extended Community
135///
136/// <https://datatracker.ietf.org/doc/html/rfc5668#section-2>
137#[derive(Debug, PartialEq, Clone, Copy, Eq)]
138pub struct FourOctetAsSpecific {
139    pub ec_type: u8,
140    pub ec_subtype: u8,
141    // 4 octet
142    pub global_administrator: Asn,
143    // 2 octet
144    pub local_administrator: [u8; 2],
145}
146
147/// IPv4 Address Specific Extended Community
148///
149/// <https://datatracker.ietf.org/doc/html/rfc4360#section-3.2>
150#[derive(Debug, PartialEq, Clone, Copy, Eq)]
151pub struct Ipv4AddressSpecific {
152    pub ec_type: u8,
153    pub ec_subtype: u8,
154    // 4 octet
155    pub global_administrator: Ipv4Addr,
156    // 2 octet
157    pub local_administrator: [u8; 2],
158}
159
160/// Opaque Extended Community
161///
162/// <https://datatracker.ietf.org/doc/html/rfc4360#section-3.3>
163#[derive(Debug, PartialEq, Clone, Copy, Eq)]
164pub struct Opaque {
165    pub ec_type: u8,
166    pub ec_subtype: u8,
167    // 6 octet
168    pub value: [u8; 6],
169}
170
171/////////////
172// DISPLAY //
173/////////////
174
175fn bytes_to_string(bytes: &[u8]) -> String {
176    bytes
177        .iter()
178        .map(|x| format!("{:02X}", x))
179        .collect::<Vec<String>>()
180        .join("")
181}
182
183impl std::fmt::Display for Community {
184    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185        write!(
186            f,
187            "{}",
188            match self {
189                Community::NoExport => {
190                    "no-export".to_string()
191                }
192                Community::NoAdvertise => {
193                    "no-advertise".to_string()
194                }
195                Community::NoExportSubConfed => {
196                    "no-export-sub-confed".to_string()
197                }
198                Community::Custom(asn, value) => {
199                    format!("{}:{}", asn, value)
200                }
201            }
202        )
203    }
204}
205
206impl std::fmt::Display for LargeCommunity {
207    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208        write!(
209            f,
210            "lg:{}:{}:{}",
211            self.global_administrator, self.local_data[0], self.local_data[1]
212        )
213    }
214}
215
216impl std::fmt::Display for ExtendedCommunity {
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        write!(
219            f,
220            "{}",
221            match self {
222                ExtendedCommunity::TransitiveTwoOctetAsSpecific(ec)
223                | ExtendedCommunity::NonTransitiveTwoOctetAsSpecific(ec) => {
224                    format!(
225                        "ecas2:{}:{}:{}:{}",
226                        ec.ec_type,
227                        ec.ec_subtype,
228                        ec.global_administrator,
229                        bytes_to_string(&ec.local_administrator)
230                    )
231                }
232                ExtendedCommunity::TransitiveIpv4AddressSpecific(ec)
233                | ExtendedCommunity::NonTransitiveIpv4AddressSpecific(ec) => {
234                    format!(
235                        "ecv4:{}:{}:{}:{}",
236                        ec.ec_type,
237                        ec.ec_subtype,
238                        ec.global_administrator,
239                        bytes_to_string(&ec.local_administrator)
240                    )
241                }
242                ExtendedCommunity::TransitiveFourOctetAsSpecific(ec)
243                | ExtendedCommunity::NonTransitiveFourOctetAsSpecific(ec) => {
244                    format!(
245                        "ecas4:{}:{}:{}:{}",
246                        ec.ec_type,
247                        ec.ec_subtype,
248                        ec.global_administrator,
249                        bytes_to_string(&ec.local_administrator)
250                    )
251                }
252                ExtendedCommunity::TransitiveOpaque(ec)
253                | ExtendedCommunity::NonTransitiveOpaque(ec) => {
254                    format!(
255                        "ecop:{}:{}:{}",
256                        ec.ec_type,
257                        ec.ec_subtype,
258                        bytes_to_string(&ec.value)
259                    )
260                }
261                ExtendedCommunity::Ipv6AddressSpecific(ec) => {
262                    format!(
263                        "ecv6:{}:{}:{}:{}",
264                        ec.ec_type,
265                        ec.ec_subtype,
266                        ec.global_administrator,
267                        bytes_to_string(&ec.local_administrator)
268                    )
269                }
270                ExtendedCommunity::Raw(ec) => {
271                    format!("ecraw:{}", bytes_to_string(ec))
272                }
273            }
274        )
275    }
276}
277
278impl std::fmt::Display for MetaCommunity {
279    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
280        write!(
281            f,
282            "{}",
283            match self {
284                MetaCommunity::Community(c) => {
285                    c.to_string()
286                }
287                MetaCommunity::ExtendedCommunity(c) => {
288                    c.to_string()
289                }
290                MetaCommunity::LargeCommunity(c) => {
291                    c.to_string()
292                }
293            }
294        )
295    }
296}
297
298///////////////
299// SERIALIZE //
300///////////////
301
302macro_rules! impl_serialize {
303    ($a:ident) => {
304        impl Serialize for $a {
305            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
306            where
307                S: serde::Serializer,
308            {
309                serializer.serialize_str(self.to_string().as_str())
310            }
311        }
312    };
313}
314
315impl_serialize!(Community);
316impl_serialize!(ExtendedCommunity);
317impl_serialize!(LargeCommunity);
318impl_serialize!(MetaCommunity);