//! The one "device" smoltcp talks to: the virtio-net card (if there is //! one) *plus* a loopback queue. //! //! **Loopback.** Programs talk to themselves over the network all the //! time: a database on 127.0.0.1, a test client and server, a compositor //! and its clients. Linux has a separate `lo` interface for that. We keep //! smoltcp's single Ethernet interface and give it a second address, //! 127.0.0.1/8, next to DHCP's. Then this device does the trick: a frame //! meant for *ourselves* never goes to the card, it's put on the `lo` //! queue, and `receive` hands queued frames back to smoltcp first. //! "Meant for ourselves" is: //! * an ARP request asking who has 127.x.x.x or our own DHCP address //! (smoltcp then answers itself: "that's me", with its own MAC), and //! * any frame sent to our own MAC address. //! So 127.0.0.1 works with no network card at all (MAC 02:00:00:00:00:01, //! a "locally administered" one), and connecting to our own 10.0.2.15 //! doesn't leave the machine either, just like Linux. use super::virtio_net::{MTU, VirtioNet}; use alloc::collections::VecDeque; use alloc::vec; use alloc::vec::Vec; use smoltcp::phy::{self, DeviceCapabilities, Medium}; use smoltcp::time::Instant; /// Our MAC when there's no card. pub const LOOPBACK_MAC: [u8; 6] = [0x02, 0, 0, 0, 0, 1]; /// Frames waiting on lo at most (more are dropped, like a full queue). const LO_QUEUE: usize = 256; pub struct NetDevice { pub nic: Option, pub mac: [u8; 6], /// Our DHCP address (frames ARPing for it are ours). pub own_ip: Option<[u8; 4]>, lo: VecDeque>, pub lo_packets: u64, } impl NetDevice { pub fn new(nic: Option) -> NetDevice { let mac = nic.as_ref().map_or(LOOPBACK_MAC, |n| n.mac); NetDevice { nic, mac, own_ip: None, lo: VecDeque::new(), lo_packets: 0 } } /// Frames are waiting on lo (net::poll then polls smoltcp again). pub fn lo_pending(&self) -> bool { !self.lo.is_empty() } /// Is this outgoing Ethernet frame addressed to us? (See the top.) fn is_for_us(&self, frame: &[u8]) -> bool { if frame.len() < 14 { return false; } if frame[0..6] == self.mac { return true; } // ARP (ethertype 0x0806): the asked-for IP is at bytes 38..42. if frame[12..14] == [0x08, 0x06] && frame.len() >= 42 { let target = [frame[38], frame[39], frame[40], frame[41]]; return target[0] == 127 || Some(target) == self.own_ip; } false } } // smoltcp asks a `Device` for *tokens*: an RxToken is "a received frame you // may look at", a TxToken is "permission to build one frame to send". pub struct RxToken(Vec); pub struct TxToken<'a>(&'a mut NetDevice); impl phy::Device for NetDevice { type RxToken<'a> = RxToken; type TxToken<'a> = TxToken<'a>; fn receive(&mut self, _now: Instant) -> Option<(RxToken, TxToken<'_>)> { // Loopback first, then the card. let frame = match self.lo.pop_front() { Some(f) => f, None => self.nic.as_mut()?.receive_frame()?, }; Some((RxToken(frame), TxToken(self))) } fn transmit(&mut self, _now: Instant) -> Option> { Some(TxToken(self)) } fn capabilities(&self) -> DeviceCapabilities { let mut caps = DeviceCapabilities::default(); caps.medium = Medium::Ethernet; caps.max_transmission_unit = MTU; caps } } impl phy::RxToken for RxToken { fn consume R>(self, f: F) -> R { f(&self.0) } } impl phy::TxToken for TxToken<'_> { /// smoltcp builds the frame in `f`; we then decide where it goes. fn consume R>(self, len: usize, f: F) -> R { let dev = self.0; let mut frame = vec![0; len.min(MTU)]; let result = f(&mut frame); if dev.is_for_us(&frame) { if dev.lo.len() < LO_QUEUE { dev.lo_packets += 1; dev.lo.push_back(frame); } } else if let Some(nic) = dev.nic.as_mut() { nic.send_frame(frame.len(), |buf| buf.copy_from_slice(&frame[..buf.len()])); } // no card: nowhere to send it result } }