Files
RedBear-OS/netstack/src/link/vxlan.rs
T
Red Bear OS baea0e523b review: 4 critical fixes (ICMP queue, refcount, udp panic, vlan send)
CRITICAL BUGS FIXED:

1. router/mod.rs: ICMP errors for Unreachable/Prohibit went to rx_buffer
   (infinite loop back to input) instead of tx_buffer (sent to source).
   Combined Unreachable/Prohibit arms; both now use tx_buffer.

2. scheme/socket.rs dup(): refcount leak in update_with branch.
   OLD code: new_handle.socket_handle() got +2 when update_with was
   Some, but only decremented once on close. Net: SH1 over-counted (+1),
   SH2 (new listening socket from update_with) never tracked at all.
   FIX: in update_with branch, increment refcount of 'socket_handle'
   (the new SH), not new_handle.socket_handle(). The always-run
   increment at the bottom covers new_handle. Both increments serve
   different purposes and are now distinct.

3. scheme/udp.rs: 4 .expect() panic vectors in bind/send/recv.
   'Can't bind', 'Can't send', 'Can't receive', 'Can't recieve' all
   panicked the daemon. Now return EIO via ? operator.

4. link/vlan.rs (and vxlan/gre/ipip already partially fixed in R42):
   send() pushed tagged packets into self.recv_queue, creating a
   self-loop where packets were never delivered. Now drops packets
   with a debug log since no parent device reference exists.

5. link/qdisc.rs: TokenBucket token_add could overflow u64 on long
   elapsed durations. Changed to saturating_mul.

DOC FIX:
6. filter/table.rs docstring example used --sport 1024:65535 (port
   range) but parse_port only accepts single port. Changed example to
   use single port value. Range support is a future enhancement.

All 29 existing tests still pass.
2026-07-09 00:29:46 +03:00

212 lines
6.8 KiB
Rust

//! 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<str>,
parent_name: Rc<str>,
local_ip: Ipv4Address,
remote_ip: Ipv4Address,
vni: u32,
vxlan_header: [u8; 8],
send_buffer: Vec<u8>,
recv_buffer: Vec<u8>,
recv_queue: VecDeque<Vec<u8>>,
virtual_mac: EthernetAddress,
ip_address: Option<IpCidr>,
}
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<u8>) {
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(&eth) 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<str> {
&self.name
}
fn can_recv(&self) -> bool {
!self.recv_queue.is_empty()
}
fn mac_address(&self) -> Option<EthernetAddress> {
Some(self.virtual_mac)
}
fn set_mac_address(&mut self, addr: EthernetAddress) {
self.virtual_mac = addr;
}
fn ip_address(&self) -> Option<IpCidr> {
self.ip_address
}
fn set_ip_address(&mut self, addr: IpCidr) {
self.ip_address = Some(addr);
}
}