bgpkit_parser/models/bgp/
tunnel_encap.rs

1//! BGP Tunnel Encapsulation data structures based on RFC 9012
2
3use num_enum::{FromPrimitive, IntoPrimitive};
4use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
5
6/// BGP Tunnel Encapsulation Types as defined in RFC 9012 and IANA registry
7#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[repr(u16)]
10pub enum TunnelType {
11    #[num_enum(default)]
12    Reserved = 0,
13    /// L2TPv3 over IP
14    L2tpv3OverIp = 1,
15    /// GRE
16    Gre = 2,
17    /// Transmit tunnel endpoint (DEPRECATED)
18    TransmitTunnelEndpoint = 3,
19    /// IPsec in Tunnel-mode (DEPRECATED)
20    IpsecTunnelMode = 4,
21    /// IP in IP tunnel with IPsec Transport Mode
22    IpInIpWithIpsecTransport = 5,
23    /// MPLS-in-IP tunnel with IPsec Transport Mode
24    MplsInIpWithIpsecTransport = 6,
25    /// IP in IP
26    IpInIp = 7,
27    /// VXLAN Encapsulation
28    Vxlan = 8,
29    /// NVGRE Encapsulation
30    Nvgre = 9,
31    /// MPLS Encapsulation
32    Mpls = 10,
33    /// MPLS in GRE Encapsulation
34    MplsInGre = 11,
35    /// VXLAN GPE Encapsulation
36    VxlanGpe = 12,
37    /// MPLS in UDP Encapsulation
38    MplsInUdp = 13,
39    /// IPv6 Tunnel
40    Ipv6Tunnel = 14,
41    /// SR Policy
42    SrPolicy = 15,
43    /// Bare
44    Bare = 16,
45    /// SR Tunnel (DEPRECATED)
46    SrTunnel = 17,
47    /// Cloud Security
48    CloudSecurity = 18,
49    /// Geneve Encapsulation
50    Geneve = 19,
51    /// Any Encapsulation
52    AnyEncapsulation = 20,
53    /// GTP Tunnel Type
54    GtpTunnel = 21,
55    /// Dynamic Path Selection (DPS) Tunnel Encapsulation
56    DpsTunnel = 22,
57}
58
59/// BGP Tunnel Encapsulation Sub-TLV Types
60#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, FromPrimitive, IntoPrimitive)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62#[repr(u16)]
63pub enum SubTlvType {
64    #[num_enum(default)]
65    Reserved = 0,
66    /// Encapsulation Sub-TLV
67    Encapsulation = 1,
68    /// Protocol Type Sub-TLV
69    ProtocolType = 2,
70    /// IPsec Tunnel Authenticator Sub-TLV (DEPRECATED)
71    IpsecTunnelAuthenticator = 3,
72    /// Color Sub-TLV
73    Color = 4,
74    /// Load-Balancing Block Sub-TLV
75    LoadBalancingBlock = 5,
76    /// Tunnel Egress Endpoint Sub-TLV
77    TunnelEgressEndpoint = 6,
78    /// DS Field Sub-TLV
79    DsField = 7,
80    /// UDP Destination Port Sub-TLV
81    UdpDestinationPort = 8,
82    /// Embedded Label Handling Sub-TLV
83    EmbeddedLabelHandling = 9,
84    /// MPLS Label Stack Sub-TLV
85    MplsLabelStack = 10,
86    /// Prefix-SID Sub-TLV
87    PrefixSid = 11,
88    /// Preference Sub-TLV
89    Preference = 12,
90    /// Binding SID Sub-TLV
91    BindingSid = 13,
92    /// ENLP Sub-TLV
93    Enlp = 14,
94    /// Priority Sub-TLV
95    Priority = 15,
96    /// SPI/SI Representation Sub-TLV
97    SpiSiRepresentation = 16,
98    /// IPv6 SID Structure Sub-TLV
99    Ipv6SidStructure = 17,
100    /// IPv4 SID Sub-TLV
101    Ipv4Sid = 18,
102    /// IPv6 SID Sub-TLV
103    Ipv6Sid = 19,
104    /// SRv6 Binding SID Sub-TLV
105    Srv6BindingSid = 20,
106    /// Segment List Sub-TLV
107    SegmentList = 128,
108    /// Policy Candidate Path Name Sub-TLV
109    PolicyCandidatePathName = 129,
110}
111
112/// Sub-TLV structure for Tunnel Encapsulation
113#[derive(Debug, PartialEq, Clone, Eq)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
115pub struct SubTlv {
116    pub sub_tlv_type: SubTlvType,
117    pub value: Vec<u8>,
118}
119
120impl SubTlv {
121    pub fn new(sub_tlv_type: SubTlvType, value: Vec<u8>) -> Self {
122        Self {
123            sub_tlv_type,
124            value,
125        }
126    }
127
128    pub fn length(&self) -> u16 {
129        self.value.len() as u16
130    }
131}
132
133/// Tunnel Encapsulation TLV
134#[derive(Debug, PartialEq, Clone, Eq)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub struct TunnelEncapTlv {
137    pub tunnel_type: TunnelType,
138    pub sub_tlvs: Vec<SubTlv>,
139}
140
141impl TunnelEncapTlv {
142    pub fn new(tunnel_type: TunnelType) -> Self {
143        Self {
144            tunnel_type,
145            sub_tlvs: Vec::new(),
146        }
147    }
148
149    pub fn add_sub_tlv(&mut self, sub_tlv: SubTlv) {
150        self.sub_tlvs.push(sub_tlv);
151    }
152
153    /// Get the tunnel egress endpoint if present
154    pub fn get_tunnel_egress_endpoint(&self) -> Option<IpAddr> {
155        self.sub_tlvs
156            .iter()
157            .find(|tlv| tlv.sub_tlv_type == SubTlvType::TunnelEgressEndpoint)
158            .and_then(|tlv| match tlv.value.len() {
159                4 => {
160                    let bytes = &tlv.value[0..4];
161                    Some(IpAddr::V4(Ipv4Addr::new(
162                        bytes[0], bytes[1], bytes[2], bytes[3],
163                    )))
164                }
165                16 => {
166                    let mut bytes = [0u8; 16];
167                    bytes.copy_from_slice(&tlv.value[0..16]);
168                    Some(IpAddr::V6(Ipv6Addr::from(bytes)))
169                }
170                _ => None,
171            })
172    }
173
174    /// Get the color value if present
175    pub fn get_color(&self) -> Option<u32> {
176        self.sub_tlvs
177            .iter()
178            .find(|tlv| tlv.sub_tlv_type == SubTlvType::Color)
179            .and_then(|tlv| {
180                if tlv.value.len() >= 4 {
181                    Some(u32::from_be_bytes([
182                        tlv.value[0],
183                        tlv.value[1],
184                        tlv.value[2],
185                        tlv.value[3],
186                    ]))
187                } else {
188                    None
189                }
190            })
191    }
192
193    /// Get the UDP destination port if present
194    pub fn get_udp_destination_port(&self) -> Option<u16> {
195        self.sub_tlvs
196            .iter()
197            .find(|tlv| tlv.sub_tlv_type == SubTlvType::UdpDestinationPort)
198            .and_then(|tlv| {
199                if tlv.value.len() >= 2 {
200                    Some(u16::from_be_bytes([tlv.value[0], tlv.value[1]]))
201                } else {
202                    None
203                }
204            })
205    }
206
207    /// Get the preference value if present
208    pub fn get_preference(&self) -> Option<u32> {
209        self.sub_tlvs
210            .iter()
211            .find(|tlv| tlv.sub_tlv_type == SubTlvType::Preference)
212            .and_then(|tlv| {
213                if tlv.value.len() >= 4 {
214                    Some(u32::from_be_bytes([
215                        tlv.value[0],
216                        tlv.value[1],
217                        tlv.value[2],
218                        tlv.value[3],
219                    ]))
220                } else {
221                    None
222                }
223            })
224    }
225}
226
227/// BGP Tunnel Encapsulation Attribute
228#[derive(Debug, PartialEq, Clone, Eq, Default)]
229#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
230pub struct TunnelEncapAttribute {
231    pub tunnel_tlvs: Vec<TunnelEncapTlv>,
232}
233
234impl TunnelEncapAttribute {
235    pub fn new() -> Self {
236        Self::default()
237    }
238
239    pub fn add_tunnel_tlv(&mut self, tlv: TunnelEncapTlv) {
240        self.tunnel_tlvs.push(tlv);
241    }
242
243    /// Get all tunnel TLVs of a specific type
244    pub fn get_tunnels_by_type(&self, tunnel_type: TunnelType) -> Vec<&TunnelEncapTlv> {
245        self.tunnel_tlvs
246            .iter()
247            .filter(|tlv| tlv.tunnel_type == tunnel_type)
248            .collect()
249    }
250
251    /// Check if the attribute contains any tunnel of the specified type
252    pub fn has_tunnel_type(&self, tunnel_type: TunnelType) -> bool {
253        self.tunnel_tlvs
254            .iter()
255            .any(|tlv| tlv.tunnel_type == tunnel_type)
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn test_tunnel_type_conversion() {
265        assert_eq!(TunnelType::Vxlan as u16, 8);
266        assert_eq!(TunnelType::Nvgre as u16, 9);
267        assert_eq!(TunnelType::SrPolicy as u16, 15);
268        assert_eq!(TunnelType::Geneve as u16, 19);
269    }
270
271    #[test]
272    fn test_sub_tlv_type_conversion() {
273        assert_eq!(SubTlvType::Color as u16, 4);
274        assert_eq!(SubTlvType::TunnelEgressEndpoint as u16, 6);
275        assert_eq!(SubTlvType::UdpDestinationPort as u16, 8);
276        assert_eq!(SubTlvType::SegmentList as u16, 128);
277    }
278
279    #[test]
280    fn test_tunnel_encap_tlv_creation() {
281        let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
282
283        // Add a color sub-TLV
284        let color_sub_tlv = SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]); // color 100
285        tlv.add_sub_tlv(color_sub_tlv);
286
287        // Add a UDP port sub-TLV
288        let udp_port_sub_tlv = SubTlv::new(SubTlvType::UdpDestinationPort, vec![0x12, 0xB5]); // port 4789
289        tlv.add_sub_tlv(udp_port_sub_tlv);
290
291        assert_eq!(tlv.tunnel_type, TunnelType::Vxlan);
292        assert_eq!(tlv.sub_tlvs.len(), 2);
293        assert_eq!(tlv.get_color(), Some(100));
294        assert_eq!(tlv.get_udp_destination_port(), Some(4789));
295    }
296
297    #[test]
298    fn test_tunnel_encap_attribute() {
299        let mut attr = TunnelEncapAttribute::new();
300
301        let mut vxlan_tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
302        vxlan_tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]));
303
304        let mut gre_tlv = TunnelEncapTlv::new(TunnelType::Gre);
305        gre_tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0xC8]));
306
307        attr.add_tunnel_tlv(vxlan_tlv);
308        attr.add_tunnel_tlv(gre_tlv);
309
310        assert_eq!(attr.tunnel_tlvs.len(), 2);
311        assert!(attr.has_tunnel_type(TunnelType::Vxlan));
312        assert!(attr.has_tunnel_type(TunnelType::Gre));
313        assert!(!attr.has_tunnel_type(TunnelType::Nvgre));
314
315        let vxlan_tunnels = attr.get_tunnels_by_type(TunnelType::Vxlan);
316        assert_eq!(vxlan_tunnels.len(), 1);
317        assert_eq!(vxlan_tunnels[0].get_color(), Some(100));
318    }
319
320    #[test]
321    fn test_tunnel_egress_endpoint_parsing() {
322        let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
323
324        // Test IPv4 egress endpoint
325        let ipv4_endpoint = SubTlv::new(
326            SubTlvType::TunnelEgressEndpoint,
327            vec![192, 168, 1, 1], // 192.168.1.1
328        );
329        tlv.add_sub_tlv(ipv4_endpoint);
330
331        if let Some(IpAddr::V4(addr)) = tlv.get_tunnel_egress_endpoint() {
332            assert_eq!(addr, Ipv4Addr::new(192, 168, 1, 1));
333        } else {
334            panic!("Expected IPv4 address");
335        }
336    }
337
338    #[test]
339    fn test_sub_tlv_length() {
340        let sub_tlv = SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]);
341        assert_eq!(sub_tlv.length(), 4);
342    }
343
344    #[test]
345    #[cfg(feature = "serde")]
346    fn test_serde_serialization() {
347        let mut attr = TunnelEncapAttribute::new();
348        let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
349        tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]));
350        attr.add_tunnel_tlv(tlv);
351
352        let serialized = serde_json::to_string(&attr).unwrap();
353        let deserialized: TunnelEncapAttribute = serde_json::from_str(&serialized).unwrap();
354
355        assert_eq!(attr, deserialized);
356    }
357}