1#![allow(dead_code)]
7
8use alloc::{collections::BTreeMap, vec::Vec};
9
10#[derive(Debug, Clone)]
16pub struct VxlanTunnel {
17 pub vni: u32,
19 pub local_ip: u32,
21 pub remote_ip: u32,
23 pub port: u16,
25 pub mtu: u16,
27}
28
29impl VxlanTunnel {
30 pub const DEFAULT_PORT: u16 = 4789;
32 pub const DEFAULT_MTU: u16 = 1450;
34
35 pub fn new(vni: u32, local_ip: u32, remote_ip: u32) -> Self {
37 VxlanTunnel {
38 vni: vni & 0x00FF_FFFF, local_ip,
40 remote_ip,
41 port: Self::DEFAULT_PORT,
42 mtu: Self::DEFAULT_MTU,
43 }
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct VxlanHeader {
53 pub flags: u8,
55 pub vni: u32,
57}
58
59impl VxlanHeader {
60 pub const SIZE: usize = 8;
62 pub const FLAG_VNI_VALID: u8 = 0x08;
64
65 pub fn new(vni: u32) -> Self {
67 VxlanHeader {
68 flags: Self::FLAG_VNI_VALID,
69 vni: vni & 0x00FF_FFFF,
70 }
71 }
72
73 pub fn to_bytes(&self) -> [u8; 8] {
75 let mut buf = [0u8; 8];
76 buf[0] = self.flags;
77 buf[4] = ((self.vni >> 16) & 0xFF) as u8;
79 buf[5] = ((self.vni >> 8) & 0xFF) as u8;
80 buf[6] = (self.vni & 0xFF) as u8;
81 buf
83 }
84
85 pub fn from_bytes(data: &[u8]) -> Option<Self> {
87 if data.len() < Self::SIZE {
88 return None;
89 }
90 let flags = data[0];
91 let vni = ((data[4] as u32) << 16) | ((data[5] as u32) << 8) | (data[6] as u32);
92 Some(VxlanHeader { flags, vni })
93 }
94}
95
96pub type MacAddress = [u8; 6];
102
103#[derive(Debug, Clone)]
105pub struct FdbEntry {
106 pub mac: MacAddress,
108 pub vtep_ip: u32,
110 pub last_seen: u64,
112 pub is_static: bool,
114}
115
116#[derive(Debug, Clone)]
122pub struct ArpProxyEntry {
123 pub ip: u32,
125 pub mac: MacAddress,
127 pub vtep_ip: u32,
129}
130
131#[derive(Debug, Clone, PartialEq, Eq)]
137pub enum VxlanError {
138 TunnelNotFound(u32),
140 TunnelExists(u32),
142 PacketTooSmall,
144 InvalidHeader,
146 VniMismatch { expected: u32, got: u32 },
148 FdbMiss,
150}
151
152#[derive(Debug)]
154pub struct VxlanOverlay {
155 tunnels: BTreeMap<u32, VxlanTunnel>,
157 fdb: BTreeMap<MacAddress, FdbEntry>,
159 arp_proxy_table: BTreeMap<u32, ArpProxyEntry>,
161 fdb_age_timeout: u64,
163}
164
165impl Default for VxlanOverlay {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171impl VxlanOverlay {
172 pub const DEFAULT_FDB_AGE: u64 = 300;
174
175 pub fn new() -> Self {
177 VxlanOverlay {
178 tunnels: BTreeMap::new(),
179 fdb: BTreeMap::new(),
180 arp_proxy_table: BTreeMap::new(),
181 fdb_age_timeout: Self::DEFAULT_FDB_AGE,
182 }
183 }
184
185 pub fn add_tunnel(&mut self, tunnel: VxlanTunnel) -> Result<(), VxlanError> {
187 if self.tunnels.contains_key(&tunnel.vni) {
188 return Err(VxlanError::TunnelExists(tunnel.vni));
189 }
190 self.tunnels.insert(tunnel.vni, tunnel);
191 Ok(())
192 }
193
194 pub fn remove_tunnel(&mut self, vni: u32) -> Result<(), VxlanError> {
196 self.tunnels
197 .remove(&vni)
198 .map(|_| ())
199 .ok_or(VxlanError::TunnelNotFound(vni))
200 }
201
202 pub fn get_tunnel(&self, vni: u32) -> Option<&VxlanTunnel> {
204 self.tunnels.get(&vni)
205 }
206
207 pub fn encapsulate(&self, vni: u32, inner_frame: &[u8]) -> Result<Vec<u8>, VxlanError> {
211 if !self.tunnels.contains_key(&vni) {
212 return Err(VxlanError::TunnelNotFound(vni));
213 }
214
215 let header = VxlanHeader::new(vni);
216 let header_bytes = header.to_bytes();
217
218 let mut packet = Vec::with_capacity(VxlanHeader::SIZE + inner_frame.len());
219 packet.extend_from_slice(&header_bytes);
220 packet.extend_from_slice(inner_frame);
221 Ok(packet)
222 }
223
224 pub fn decapsulate(&self, packet: &[u8]) -> Result<(u32, Vec<u8>), VxlanError> {
228 let header = VxlanHeader::from_bytes(packet).ok_or(VxlanError::PacketTooSmall)?;
229
230 if header.flags & VxlanHeader::FLAG_VNI_VALID == 0 {
231 return Err(VxlanError::InvalidHeader);
232 }
233
234 if !self.tunnels.contains_key(&header.vni) {
235 return Err(VxlanError::TunnelNotFound(header.vni));
236 }
237
238 let inner = packet[VxlanHeader::SIZE..].to_vec();
239 Ok((header.vni, inner))
240 }
241
242 pub fn fdb_learn(&mut self, mac: MacAddress, vtep_ip: u32, current_tick: u64) {
244 let entry = self.fdb.entry(mac).or_insert(FdbEntry {
245 mac,
246 vtep_ip,
247 last_seen: current_tick,
248 is_static: false,
249 });
250 entry.vtep_ip = vtep_ip;
251 entry.last_seen = current_tick;
252 }
253
254 pub fn fdb_add_static(&mut self, mac: MacAddress, vtep_ip: u32) {
256 self.fdb.insert(
257 mac,
258 FdbEntry {
259 mac,
260 vtep_ip,
261 last_seen: 0,
262 is_static: true,
263 },
264 );
265 }
266
267 pub fn fdb_lookup(&self, mac: &MacAddress) -> Option<&FdbEntry> {
269 self.fdb.get(mac)
270 }
271
272 pub fn fdb_age(&mut self, current_tick: u64) -> usize {
274 let timeout = self.fdb_age_timeout;
275 let before = self.fdb.len();
276 self.fdb.retain(|_, entry| {
277 entry.is_static || current_tick.saturating_sub(entry.last_seen) < timeout
278 });
279 before - self.fdb.len()
280 }
281
282 pub fn arp_proxy_add(&mut self, ip: u32, mac: MacAddress, vtep_ip: u32) {
284 self.arp_proxy_table
285 .insert(ip, ArpProxyEntry { ip, mac, vtep_ip });
286 }
287
288 pub fn arp_proxy(&self, target_ip: u32) -> Option<MacAddress> {
292 self.arp_proxy_table.get(&target_ip).map(|e| e.mac)
293 }
294
295 pub fn tunnel_count(&self) -> usize {
297 self.tunnels.len()
298 }
299
300 pub fn fdb_count(&self) -> usize {
302 self.fdb.len()
303 }
304}
305
306#[cfg(test)]
311mod tests {
312 use super::*;
313
314 fn ip(a: u8, b: u8, c: u8, d: u8) -> u32 {
315 ((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32)
316 }
317
318 #[test]
319 fn test_vxlan_header_roundtrip() {
320 let header = VxlanHeader::new(12345);
321 let bytes = header.to_bytes();
322 let parsed = VxlanHeader::from_bytes(&bytes).unwrap();
323 assert_eq!(parsed.vni, 12345);
324 assert_eq!(parsed.flags, VxlanHeader::FLAG_VNI_VALID);
325 }
326
327 #[test]
328 fn test_vxlan_header_too_small() {
329 assert!(VxlanHeader::from_bytes(&[0u8; 4]).is_none());
330 }
331
332 #[test]
333 fn test_add_remove_tunnel() {
334 let mut overlay = VxlanOverlay::new();
335 let tunnel = VxlanTunnel::new(100, ip(10, 0, 0, 1), ip(10, 0, 0, 2));
336 overlay.add_tunnel(tunnel).unwrap();
337 assert_eq!(overlay.tunnel_count(), 1);
338 overlay.remove_tunnel(100).unwrap();
339 assert_eq!(overlay.tunnel_count(), 0);
340 }
341
342 #[test]
343 fn test_duplicate_tunnel() {
344 let mut overlay = VxlanOverlay::new();
345 let t1 = VxlanTunnel::new(100, ip(10, 0, 0, 1), ip(10, 0, 0, 2));
346 let t2 = VxlanTunnel::new(100, ip(10, 0, 0, 1), ip(10, 0, 0, 3));
347 overlay.add_tunnel(t1).unwrap();
348 assert_eq!(overlay.add_tunnel(t2), Err(VxlanError::TunnelExists(100)));
349 }
350
351 #[test]
352 fn test_encap_decap() {
353 let mut overlay = VxlanOverlay::new();
354 overlay
355 .add_tunnel(VxlanTunnel::new(42, ip(10, 0, 0, 1), ip(10, 0, 0, 2)))
356 .unwrap();
357
358 let frame = [0xDE, 0xAD, 0xBE, 0xEF];
359 let packet = overlay.encapsulate(42, &frame).unwrap();
360 assert_eq!(packet.len(), 8 + 4);
361
362 let (vni, inner) = overlay.decapsulate(&packet).unwrap();
363 assert_eq!(vni, 42);
364 assert_eq!(inner, frame);
365 }
366
367 #[test]
368 fn test_encap_unknown_vni() {
369 let overlay = VxlanOverlay::new();
370 assert_eq!(
371 overlay.encapsulate(999, &[1, 2, 3]),
372 Err(VxlanError::TunnelNotFound(999))
373 );
374 }
375
376 #[test]
377 fn test_fdb_learn_and_lookup() {
378 let mut overlay = VxlanOverlay::new();
379 let mac = [0x02, 0x42, 0xAC, 0x11, 0x00, 0x02];
380 overlay.fdb_learn(mac, ip(10, 0, 0, 5), 100);
381 let entry = overlay.fdb_lookup(&mac).unwrap();
382 assert_eq!(entry.vtep_ip, ip(10, 0, 0, 5));
383 assert!(!entry.is_static);
384 }
385
386 #[test]
387 fn test_fdb_aging() {
388 let mut overlay = VxlanOverlay::new();
389 let mac = [0x02, 0x42, 0xAC, 0x11, 0x00, 0x03];
390 overlay.fdb_learn(mac, ip(10, 0, 0, 5), 100);
391 assert_eq!(overlay.fdb_age(200), 0);
393 assert_eq!(overlay.fdb_age(500), 1);
395 assert_eq!(overlay.fdb_count(), 0);
396 }
397
398 #[test]
399 fn test_arp_proxy() {
400 let mut overlay = VxlanOverlay::new();
401 let mac = [0x02, 0x42, 0xAC, 0x11, 0x00, 0x04];
402 let container_ip = ip(10, 244, 1, 5);
403 overlay.arp_proxy_add(container_ip, mac, ip(10, 0, 0, 2));
404 let resolved = overlay.arp_proxy(container_ip).unwrap();
405 assert_eq!(resolved, mac);
406 assert!(overlay.arp_proxy(ip(10, 244, 1, 99)).is_none());
407 }
408}