Skip to main content

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
129/// Tunnel Encapsulation TLV
130#[derive(Debug, PartialEq, Clone, Eq)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
132pub struct TunnelEncapTlv {
133    pub tunnel_type: TunnelType,
134    pub sub_tlvs: Vec<SubTlv>,
135}
136
137impl TunnelEncapTlv {
138    pub fn new(tunnel_type: TunnelType) -> Self {
139        Self {
140            tunnel_type,
141            sub_tlvs: Vec::new(),
142        }
143    }
144
145    pub fn add_sub_tlv(&mut self, sub_tlv: SubTlv) {
146        self.sub_tlvs.push(sub_tlv);
147    }
148
149    /// Get the tunnel egress endpoint if present
150    pub fn get_tunnel_egress_endpoint(&self) -> Option<IpAddr> {
151        self.sub_tlvs
152            .iter()
153            .find(|tlv| tlv.sub_tlv_type == SubTlvType::TunnelEgressEndpoint)
154            .and_then(|tlv| match tlv.value.len() {
155                4 => {
156                    let bytes = &tlv.value[0..4];
157                    Some(IpAddr::V4(Ipv4Addr::new(
158                        bytes[0], bytes[1], bytes[2], bytes[3],
159                    )))
160                }
161                16 => {
162                    let mut bytes = [0u8; 16];
163                    bytes.copy_from_slice(&tlv.value[0..16]);
164                    Some(IpAddr::V6(Ipv6Addr::from(bytes)))
165                }
166                _ => None,
167            })
168    }
169
170    /// Get the color value if present
171    pub fn get_color(&self) -> Option<u32> {
172        self.sub_tlvs
173            .iter()
174            .find(|tlv| tlv.sub_tlv_type == SubTlvType::Color)
175            .and_then(|tlv| {
176                if tlv.value.len() >= 4 {
177                    Some(u32::from_be_bytes([
178                        tlv.value[0],
179                        tlv.value[1],
180                        tlv.value[2],
181                        tlv.value[3],
182                    ]))
183                } else {
184                    None
185                }
186            })
187    }
188
189    /// Get the UDP destination port if present
190    pub fn get_udp_destination_port(&self) -> Option<u16> {
191        self.sub_tlvs
192            .iter()
193            .find(|tlv| tlv.sub_tlv_type == SubTlvType::UdpDestinationPort)
194            .and_then(|tlv| {
195                if tlv.value.len() >= 2 {
196                    Some(u16::from_be_bytes([tlv.value[0], tlv.value[1]]))
197                } else {
198                    None
199                }
200            })
201    }
202
203    /// Get the preference value if present
204    pub fn get_preference(&self) -> Option<u32> {
205        self.sub_tlvs
206            .iter()
207            .find(|tlv| tlv.sub_tlv_type == SubTlvType::Preference)
208            .and_then(|tlv| {
209                if tlv.value.len() >= 4 {
210                    Some(u32::from_be_bytes([
211                        tlv.value[0],
212                        tlv.value[1],
213                        tlv.value[2],
214                        tlv.value[3],
215                    ]))
216                } else {
217                    None
218                }
219            })
220    }
221}
222
223/// BGP Tunnel Encapsulation Attribute
224#[derive(Debug, PartialEq, Clone, Eq, Default)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226pub struct TunnelEncapAttribute {
227    pub tunnel_tlvs: Vec<TunnelEncapTlv>,
228}
229
230impl TunnelEncapAttribute {
231    pub fn new() -> Self {
232        Self::default()
233    }
234
235    pub fn add_tunnel_tlv(&mut self, tlv: TunnelEncapTlv) {
236        self.tunnel_tlvs.push(tlv);
237    }
238
239    /// Get all tunnel TLVs of a specific type
240    pub fn get_tunnels_by_type(&self, tunnel_type: TunnelType) -> Vec<&TunnelEncapTlv> {
241        self.tunnel_tlvs
242            .iter()
243            .filter(|tlv| tlv.tunnel_type == tunnel_type)
244            .collect()
245    }
246
247    /// Check if the attribute contains any tunnel of the specified type
248    pub fn has_tunnel_type(&self, tunnel_type: TunnelType) -> bool {
249        self.tunnel_tlvs
250            .iter()
251            .any(|tlv| tlv.tunnel_type == tunnel_type)
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258
259    #[test]
260    fn test_tunnel_type_conversion() {
261        assert_eq!(TunnelType::Vxlan as u16, 8);
262        assert_eq!(TunnelType::Nvgre as u16, 9);
263        assert_eq!(TunnelType::SrPolicy as u16, 15);
264        assert_eq!(TunnelType::Geneve as u16, 19);
265    }
266
267    #[test]
268    fn test_sub_tlv_type_conversion() {
269        assert_eq!(SubTlvType::Color as u16, 4);
270        assert_eq!(SubTlvType::TunnelEgressEndpoint as u16, 6);
271        assert_eq!(SubTlvType::UdpDestinationPort as u16, 8);
272        assert_eq!(SubTlvType::SegmentList as u16, 128);
273    }
274
275    #[test]
276    fn test_tunnel_encap_tlv_creation() {
277        let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
278
279        // Add a color sub-TLV
280        let color_sub_tlv = SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]); // color 100
281        tlv.add_sub_tlv(color_sub_tlv);
282
283        // Add a UDP port sub-TLV
284        let udp_port_sub_tlv = SubTlv::new(SubTlvType::UdpDestinationPort, vec![0x12, 0xB5]); // port 4789
285        tlv.add_sub_tlv(udp_port_sub_tlv);
286
287        assert_eq!(tlv.tunnel_type, TunnelType::Vxlan);
288        assert_eq!(tlv.sub_tlvs.len(), 2);
289        assert_eq!(tlv.get_color(), Some(100));
290        assert_eq!(tlv.get_udp_destination_port(), Some(4789));
291    }
292
293    #[test]
294    fn test_tunnel_encap_attribute() {
295        let mut attr = TunnelEncapAttribute::new();
296
297        let mut vxlan_tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
298        vxlan_tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]));
299
300        let mut gre_tlv = TunnelEncapTlv::new(TunnelType::Gre);
301        gre_tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0xC8]));
302
303        attr.add_tunnel_tlv(vxlan_tlv);
304        attr.add_tunnel_tlv(gre_tlv);
305
306        assert_eq!(attr.tunnel_tlvs.len(), 2);
307        assert!(attr.has_tunnel_type(TunnelType::Vxlan));
308        assert!(attr.has_tunnel_type(TunnelType::Gre));
309        assert!(!attr.has_tunnel_type(TunnelType::Nvgre));
310
311        let vxlan_tunnels = attr.get_tunnels_by_type(TunnelType::Vxlan);
312        assert_eq!(vxlan_tunnels.len(), 1);
313        assert_eq!(vxlan_tunnels[0].get_color(), Some(100));
314    }
315
316    #[test]
317    fn test_tunnel_egress_endpoint_parsing() {
318        let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
319
320        // Test IPv4 egress endpoint
321        let ipv4_endpoint = SubTlv::new(
322            SubTlvType::TunnelEgressEndpoint,
323            vec![192, 168, 1, 1], // 192.168.1.1
324        );
325        tlv.add_sub_tlv(ipv4_endpoint);
326
327        if let Some(IpAddr::V4(addr)) = tlv.get_tunnel_egress_endpoint() {
328            assert_eq!(addr, Ipv4Addr::new(192, 168, 1, 1));
329        } else {
330            panic!("Expected IPv4 address");
331        }
332    }
333
334    #[test]
335    fn test_sub_tlv_creation() {
336        let sub_tlv = SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]);
337        assert_eq!(sub_tlv.sub_tlv_type, SubTlvType::Color);
338        assert_eq!(sub_tlv.value.len(), 4);
339    }
340
341    #[test]
342    #[cfg(feature = "serde")]
343    fn test_serde_serialization() {
344        let mut attr = TunnelEncapAttribute::new();
345        let mut tlv = TunnelEncapTlv::new(TunnelType::Vxlan);
346        tlv.add_sub_tlv(SubTlv::new(SubTlvType::Color, vec![0x00, 0x00, 0x00, 0x64]));
347        attr.add_tunnel_tlv(tlv);
348
349        let serialized = serde_json::to_string(&attr).unwrap();
350        let deserialized: TunnelEncapAttribute = serde_json::from_str(&serialized).unwrap();
351
352        assert_eq!(attr, deserialized);
353    }
354}