//! 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, parent_name: Rc, devices: Rc>, local_ip: Ipv4Address, remote_ip: Ipv4Address, send_buffer: Vec, recv_buffer: Vec, recv_queue: VecDeque>, ip_address: Option, } impl IpipDevice { pub fn new(name: &str, parent_name: &str, local_ip: Ipv4Address, remote_ip: Ipv4Address, devices: Rc>) -> 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) { 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 { &self.name } fn can_recv(&self) -> bool { !self.recv_queue.is_empty() } fn mac_address(&self) -> Option { None } fn set_mac_address(&mut self, _addr: EthernetAddress) {} fn ip_address(&self) -> Option { self.ip_address } fn set_ip_address(&mut self, addr: IpCidr) { self.ip_address = Some(addr); } }