restore: networking stack files from reflog (Phases 1-6)
Recovered from reflog commits 1c80937e and d0ecc067 after force-push data loss.
Includes: filter/, icmp_error.rs, slaac.rs, bond.rs, bridge.rs, gre.rs, ipip.rs,
qdisc.rs, tun.rs, vlan.rs, vxlan.rs, netfilter.rs, tun.rs, conntrack.rs, nat.rs,
rule.rs, table.rs, redbear-ufw/, dhcpv6d/, netdiag/ — 39 files total.
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
//! 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(ð) 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) {
|
||||
let enc = self.build_encapsulated(packet).to_vec();
|
||||
self.push_received(enc);
|
||||
let _ = now;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user