//! VXLAN (Virtual eXtensible LAN) — mirrors Linux 7.1's `drivers/net/vxlan/`. //! //! Reference files: //! - `drivers/net/vxlan/vxlan_core.c:1855` — `vxlan_xmit()` — encapsulation //! - `drivers/net/vxlan/vxlan_core.c:1366` — `vxlan_rcv()` — decapsulation //! - `include/net/vxlan.h` — VXLAN header format, VNI, default port 4789 //! //! VXLAN encapsulates L2 Ethernet frames in UDP for overlay networking. //! Each overlay network is identified by a 24-bit VNI (Virtual Network //! Identifier). The default UDP destination port is 4789 (IANA-assigned). //! //! Packet structure: //! [Outer Eth][Outer IP][UDP:4789][VXLAN 8B][Inner Eth][Inner IP][Payload] use std::collections::VecDeque; use std::rc::Rc; use smoltcp::time::Instant; use smoltcp::wire::{ EthernetAddress, EthernetFrame, EthernetProtocol, EthernetRepr, IpAddress, IpCidr, Ipv4Address, Ipv4Packet, Ipv4Repr, IpProtocol, UdpPacket, UdpRepr, }; use super::LinkDevice; const VXLAN_PORT: u16 = 4789; const VXLAN_FLAGS: u8 = 0x08; pub struct VxlanDevice { name: Rc, parent_name: Rc, local_ip: Ipv4Address, remote_ip: Ipv4Address, vni: u32, vxlan_header: [u8; 8], send_buffer: Vec, recv_buffer: Vec, recv_queue: VecDeque>, virtual_mac: EthernetAddress, ip_address: Option, } impl VxlanDevice { pub fn new( name: &str, parent_name: &str, local_ip: Ipv4Address, remote_ip: Ipv4Address, vni: u32, ) -> Self { let vni_bytes = vni.to_be_bytes(); Self { name: name.into(), parent_name: parent_name.into(), local_ip, remote_ip, vni: vni & 0x00ffffff, vxlan_header: [ VXLAN_FLAGS, 0, 0, 0, vni_bytes[1], vni_bytes[2], vni_bytes[3], 0, ], send_buffer: Vec::with_capacity(1550), recv_buffer: Vec::with_capacity(1550), recv_queue: VecDeque::new(), virtual_mac: EthernetAddress([0x00, 0x00, 0x5e, 0x00, 0x01, 0x01]), ip_address: None, } } pub fn push_received(&mut self, packet: Vec) { self.recv_queue.push_back(packet); } fn build_encapsulated(&mut self, inner_packet: &[u8]) -> &[u8] { self.send_buffer.clear(); let inner_eth = { let mut buf = [0u8; 14]; buf[..6].copy_from_slice(&EthernetAddress::BROADCAST.0); buf[6..12].copy_from_slice(&self.virtual_mac.0); let ethtype = if !inner_packet.is_empty() && inner_packet[0] >> 4 == 6 { EthernetProtocol::Ipv6 } else { EthernetProtocol::Ipv4 }; let proto_bytes: [u8; 2] = match ethtype { EthernetProtocol::Ipv4 => [0x08, 0x00], EthernetProtocol::Ipv6 => [0x86, 0xDD], _ => [0x08, 0x00], }; buf[12..14].copy_from_slice(&proto_bytes); buf }; let inner_frame_len = inner_eth.len() + inner_packet.len(); let udp_repr = UdpRepr { src_port: VXLAN_PORT, dst_port: VXLAN_PORT, }; let udp_payload_len = 8 + inner_frame_len; let outer_ip_repr = Ipv4Repr { src_addr: self.local_ip, dst_addr: self.remote_ip, next_header: IpProtocol::Udp, payload_len: 8 + udp_payload_len, hop_limit: 64, }; let total_len = outer_ip_repr.buffer_len() + 8 + 8 + inner_frame_len; self.send_buffer.resize(total_len, 0); let mut ip = Ipv4Packet::new_unchecked(&mut self.send_buffer); outer_ip_repr.emit(&mut ip, &smoltcp::phy::ChecksumCapabilities::ignored()); let ip_hdr_len = outer_ip_repr.buffer_len(); let mut udp = UdpPacket::new_unchecked(&mut self.send_buffer[ip_hdr_len..]); udp_repr.emit( &mut udp, &IpAddress::Ipv4(self.local_ip), &IpAddress::Ipv4(self.remote_ip), udp_payload_len, |buf| { buf[..8].copy_from_slice(&self.vxlan_header); buf[8..8 + inner_eth.len()].copy_from_slice(&inner_eth); buf[8 + inner_eth.len()..].copy_from_slice(inner_packet); }, &smoltcp::phy::ChecksumCapabilities::ignored(), ); &self.send_buffer } fn matches_endpoint(&self, outer_packet: &[u8]) -> bool { if outer_packet.len() < 50 { return false; } let Ok(ipv4) = Ipv4Packet::new_checked(outer_packet) else { return false; }; if u8::from(ipv4.next_header()) != 17 || ipv4.dst_addr() != self.local_ip { return false; } let ip_hdr_len = 20; let udp = &outer_packet[ip_hdr_len..]; if udp.len() < 18 { return false; } let dst_port = u16::from_be_bytes([udp[2], udp[3]]); if dst_port != VXLAN_PORT { return false; } if udp[8] != VXLAN_FLAGS { return false; } let pkt_vni = u32::from_be_bytes([0, udp[12], udp[13], udp[14]]) & 0x00ffffff; pkt_vni == self.vni } fn decapsulate(&mut self, outer_packet: &[u8]) -> Option<&[u8]> { if !self.matches_endpoint(outer_packet) { return None; } let inner_frame_start = 20 + 8 + 8; let inner_frame = &outer_packet[inner_frame_start..]; if inner_frame.len() < 14 { return None; } let eth = EthernetFrame::new_unchecked(inner_frame); let Ok(repr) = EthernetRepr::parse(ð) else { return None; }; if repr.ethertype != EthernetProtocol::Ipv4 && repr.ethertype != EthernetProtocol::Ipv6 { return None; } self.recv_buffer.clear(); self.recv_buffer.extend_from_slice(eth.payload()); Some(&self.recv_buffer) } } impl LinkDevice for VxlanDevice { fn send(&mut self, _next_hop: IpAddress, packet: &[u8], _now: Instant) { // No parent device reference — drop rather than self-loop. log::debug!("vxlan: dropping {} byte frame (no parent device)", packet.len()); } fn recv(&mut self, _now: Instant) -> Option<&[u8]> { let packet = self.recv_queue.pop_front()?; self.decapsulate(&packet) } fn name(&self) -> &Rc { &self.name } fn can_recv(&self) -> bool { !self.recv_queue.is_empty() } fn mac_address(&self) -> Option { Some(self.virtual_mac) } fn set_mac_address(&mut self, addr: EthernetAddress) { self.virtual_mac = addr; } fn ip_address(&self) -> Option { self.ip_address } fn set_ip_address(&mut self, addr: IpCidr) { self.ip_address = Some(addr); } }