bd595851e2
251 files: init, acpid, ipcd, netcfg, ihdgd, virtio-gpud, scheme-utils, inputd, block driver, ptyd, ramfs, randd, initfs bootstrap, path deps, version +rb0.3.1, author attribution
132 lines
4.1 KiB
Rust
132 lines
4.1 KiB
Rust
//! IPIP Tunnel (IP-in-IP) — mirrors Linux 7.1's `net/ipv4/ipip.c`.
|
|
//!
|
|
//! Reference files:
|
|
//! - `net/ipv4/ipip.c:184` — `ipip_tunnel_xmit()` — build outer header, transmit
|
|
//! - `net/ipv4/ip_tunnel.c:326` — `ip_tunnel_rcv()` — receive and decapsulate
|
|
//! - `include/uapi/linux/in.h:30` — `IPPROTO_IPIP = 4`
|
|
//!
|
|
//! The IPIP tunnel wraps an inner IP packet in an outer IP header, routing
|
|
//! it through a parent link-layer device to a remote endpoint. On reception,
|
|
//! the outer header is stripped and the inner packet is delivered to the
|
|
//! network stack.
|
|
|
|
use std::cell::RefCell;
|
|
use std::collections::VecDeque;
|
|
use std::rc::Rc;
|
|
|
|
use smoltcp::time::Instant;
|
|
use smoltcp::wire::{
|
|
EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Packet, Ipv4Repr, IpProtocol,
|
|
};
|
|
|
|
use super::{DeviceList, LinkDevice};
|
|
|
|
const IPIP_PROTO: u8 = 4;
|
|
|
|
pub struct IpipDevice {
|
|
name: Rc<str>,
|
|
parent_name: Rc<str>,
|
|
devices: Rc<RefCell<DeviceList>>,
|
|
local_ip: Ipv4Address,
|
|
remote_ip: Ipv4Address,
|
|
send_buffer: Vec<u8>,
|
|
recv_buffer: Vec<u8>,
|
|
recv_queue: VecDeque<Vec<u8>>,
|
|
ip_address: Option<IpCidr>,
|
|
}
|
|
|
|
impl IpipDevice {
|
|
pub fn new(name: &str, parent_name: &str, local_ip: Ipv4Address, remote_ip: Ipv4Address, devices: Rc<RefCell<DeviceList>>) -> Self {
|
|
Self {
|
|
name: name.into(),
|
|
parent_name: parent_name.into(),
|
|
devices,
|
|
local_ip,
|
|
remote_ip,
|
|
send_buffer: Vec::with_capacity(1500),
|
|
recv_buffer: Vec::with_capacity(1500),
|
|
recv_queue: VecDeque::new(),
|
|
ip_address: None,
|
|
}
|
|
}
|
|
|
|
pub fn push_received(&mut self, packet: Vec<u8>) {
|
|
self.recv_queue.push_back(packet);
|
|
}
|
|
|
|
fn build_outer_header(&mut self, inner_packet: &[u8]) -> &[u8] {
|
|
self.send_buffer.clear();
|
|
let outer_header = Ipv4Repr {
|
|
src_addr: self.local_ip,
|
|
dst_addr: self.remote_ip,
|
|
next_header: IpProtocol::from(IPIP_PROTO),
|
|
payload_len: inner_packet.len(),
|
|
hop_limit: 64,
|
|
};
|
|
let header_len = outer_header.buffer_len();
|
|
self.send_buffer.resize(header_len + inner_packet.len(), 0);
|
|
let mut ip_pkt = Ipv4Packet::new_unchecked(&mut self.send_buffer);
|
|
outer_header.emit(&mut ip_pkt, &smoltcp::phy::ChecksumCapabilities::ignored());
|
|
self.send_buffer[header_len..].copy_from_slice(inner_packet);
|
|
&self.send_buffer
|
|
}
|
|
|
|
fn matches_endpoint(&self, outer_packet: &[u8]) -> bool {
|
|
if outer_packet.len() < 20 {
|
|
return false;
|
|
}
|
|
let Ok(ipv4) = Ipv4Packet::new_checked(outer_packet) else {
|
|
return false;
|
|
};
|
|
u8::from(ipv4.next_header()) == IPIP_PROTO && ipv4.dst_addr() == self.local_ip
|
|
}
|
|
}
|
|
|
|
impl LinkDevice for IpipDevice {
|
|
fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) {
|
|
let encapsulated = self.build_outer_header(packet).to_vec();
|
|
if let Some(parent) = self.devices.borrow_mut().get_mut(&self.parent_name) {
|
|
parent.send(next_hop, &encapsulated, now);
|
|
} else {
|
|
log::debug!("ipip: parent {} not found, dropping {} byte frame",
|
|
self.parent_name, packet.len());
|
|
}
|
|
}
|
|
|
|
fn recv(&mut self, _now: Instant) -> Option<&[u8]> {
|
|
let packet = self.recv_queue.pop_front()?;
|
|
if !self.matches_endpoint(&packet) {
|
|
return None;
|
|
}
|
|
let Ok(ipv4) = Ipv4Packet::new_checked(&packet) else {
|
|
return None;
|
|
};
|
|
let header_len = 20;
|
|
let payload = &packet[header_len..];
|
|
self.recv_buffer.clear();
|
|
self.recv_buffer.extend_from_slice(payload);
|
|
Some(&self.recv_buffer)
|
|
}
|
|
|
|
fn name(&self) -> &Rc<str> {
|
|
&self.name
|
|
}
|
|
|
|
fn can_recv(&self) -> bool {
|
|
!self.recv_queue.is_empty()
|
|
}
|
|
|
|
fn mac_address(&self) -> Option<EthernetAddress> {
|
|
None
|
|
}
|
|
|
|
fn set_mac_address(&mut self, _addr: EthernetAddress) {}
|
|
|
|
fn ip_address(&self) -> Option<IpCidr> {
|
|
self.ip_address
|
|
}
|
|
|
|
fn set_ip_address(&mut self, addr: IpCidr) {
|
|
self.ip_address = Some(addr);
|
|
}
|
|
} |