From b54ead7d773c5737b928ab43db8ff07366f6e81b Mon Sep 17 00:00:00 2001 From: Egor Karavaev Date: Tue, 24 Oct 2017 22:23:03 +0300 Subject: [PATCH] Support for loopback interface. --- src/smolnetd/arp_cache.rs | 45 +++++++++++++++++++++++++++++++++++++ src/smolnetd/buffer_pool.rs | 21 ++++++++++++----- src/smolnetd/device.rs | 36 +++++++++++++++++++++++++---- src/smolnetd/main.rs | 1 + src/smolnetd/scheme/mod.rs | 28 ++++++++++++++++------- 5 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 src/smolnetd/arp_cache.rs diff --git a/src/smolnetd/arp_cache.rs b/src/smolnetd/arp_cache.rs new file mode 100644 index 0000000000..2585c75e41 --- /dev/null +++ b/src/smolnetd/arp_cache.rs @@ -0,0 +1,45 @@ +use smoltcp::iface::{ArpCache, SliceArpCache}; +use smoltcp::wire::{EthernetAddress, IpAddress}; +use std::collections::BTreeSet; +use std::iter::FromIterator; + +//TODO: move to EthernetAddress::LOCAL (?) +pub const LOOPBACK_HWADDR: EthernetAddress = EthernetAddress([0; 6]); + +pub struct LoArpCache { + arp_cache: SliceArpCache<'static>, + local_ips: BTreeSet, +} + +impl LoArpCache { + pub fn new(local_ips: I) -> LoArpCache + where + I: IntoIterator, + { + LoArpCache { + arp_cache: SliceArpCache::new(vec![Default::default(); 16]), + local_ips: BTreeSet::from_iter(local_ips), + } + } +} + +impl ArpCache for LoArpCache { + fn fill(&mut self, protocol_addr: &IpAddress, hardware_addr: &EthernetAddress) { + self.arp_cache.fill(protocol_addr, hardware_addr) + } + + fn lookup(&mut self, protocol_addr: &IpAddress) -> Option { + //TODO: use IpAddress::is_loopback + if let &IpAddress::Ipv4(ipv4_addr) = protocol_addr { + if ipv4_addr.is_loopback() { + return Some(LOOPBACK_HWADDR); + } + } + + if self.local_ips.contains(protocol_addr) { + return Some(LOOPBACK_HWADDR); + } + + self.arp_cache.lookup(protocol_addr) + } +} diff --git a/src/smolnetd/buffer_pool.rs b/src/smolnetd/buffer_pool.rs index c96baea781..790c3e7995 100644 --- a/src/smolnetd/buffer_pool.rs +++ b/src/smolnetd/buffer_pool.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut, Drop}; use std::rc::Rc; use std::cell::RefCell; -use std::mem::swap; +use std::mem::{replace, swap}; type BufferStack = Rc>>>; @@ -14,6 +14,13 @@ impl Buffer { pub fn resize(&mut self, new_len: usize) { self.buffer.resize(new_len, 0u8); } + + pub fn move_out(&mut self) -> Buffer { + Buffer { + buffer: replace(&mut self.buffer, vec![]), + stack: self.stack.clone(), + } + } } impl AsRef<[u8]> for Buffer { @@ -44,11 +51,13 @@ impl DerefMut for Buffer { impl Drop for Buffer { fn drop(&mut self) { - let mut tmp = vec![]; - swap(&mut tmp, &mut self.buffer); - { - let mut stack = self.stack.borrow_mut(); - stack.push(tmp); + if self.buffer.capacity() > 0 { + let mut tmp = vec![]; + swap(&mut tmp, &mut self.buffer); + { + let mut stack = self.stack.borrow_mut(); + stack.push(tmp); + } } } } diff --git a/src/smolnetd/device.rs b/src/smolnetd/device.rs index a572c56ef6..a62d3741b1 100644 --- a/src/smolnetd/device.rs +++ b/src/smolnetd/device.rs @@ -5,11 +5,14 @@ use std::fs::File; use std::io::Write; use std::rc::Rc; -use buffer_pool::Buffer; +use buffer_pool::{Buffer, BufferPool}; +use arp_cache::LOOPBACK_HWADDR; pub struct NetworkDevice { network_file: Rc>, input_queue: Rc>>, + local_hwaddr: smoltcp::wire::EthernetAddress, + buffer_pool: Rc>, } impl NetworkDevice { @@ -18,17 +21,23 @@ impl NetworkDevice { pub fn new( network_file: Rc>, input_queue: Rc>>, + local_hwaddr: smoltcp::wire::EthernetAddress, + buffer_pool: Rc>, ) -> NetworkDevice { NetworkDevice { network_file, input_queue, + local_hwaddr, + buffer_pool, } } } pub struct TxBuffer { - buffer: Vec, + buffer: Buffer, network_file: Rc>, + input_queue: Rc>>, + local_hwaddr: smoltcp::wire::EthernetAddress, } impl AsRef<[u8]> for TxBuffer { @@ -45,7 +54,22 @@ impl AsMut<[u8]> for TxBuffer { impl Drop for TxBuffer { fn drop(&mut self) { - let _ = self.network_file.borrow_mut().write(&self.buffer); + let mut loopback = false; + + if let Ok(mut frame) = smoltcp::wire::EthernetFrame::new_checked(&mut self.buffer) { + if frame.dst_addr() == LOOPBACK_HWADDR { + frame.set_dst_addr(self.local_hwaddr); + loopback = true; + } + } + + if loopback { + self.input_queue + .borrow_mut() + .push_back(self.buffer.move_out()); + } else { + let _ = self.network_file.borrow_mut().write(&self.buffer); + } } } @@ -68,9 +92,13 @@ impl smoltcp::phy::Device for NetworkDevice { } fn transmit(&mut self, _timestamp: u64, length: usize) -> smoltcp::Result { + let mut buffer = self.buffer_pool.borrow_mut().get_buffer(); + buffer.resize(length); Ok(TxBuffer { network_file: Rc::clone(&self.network_file), - buffer: vec![0; length], + buffer, + input_queue: Rc::clone(&self.input_queue), + local_hwaddr: self.local_hwaddr, }) } } diff --git a/src/smolnetd/main.rs b/src/smolnetd/main.rs index 3ff7a568cd..e6f2131a4c 100644 --- a/src/smolnetd/main.rs +++ b/src/smolnetd/main.rs @@ -23,6 +23,7 @@ mod error; mod logger; mod port_set; mod scheme; +mod arp_cache; fn run() -> Result<()> { use syscall::flag::*; diff --git a/src/smolnetd/scheme/mod.rs b/src/smolnetd/scheme/mod.rs index a65296521b..ff6de1df43 100644 --- a/src/smolnetd/scheme/mod.rs +++ b/src/smolnetd/scheme/mod.rs @@ -1,5 +1,5 @@ use netutils::getcfg; -use smoltcp::iface::{ArpCache, EthernetInterface, SliceArpCache}; +use smoltcp::iface::{ArpCache, EthernetInterface}; use smoltcp::socket::SocketSet as SmoltcpSocketSet; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address}; use std::cell::RefCell; @@ -16,6 +16,7 @@ use syscall; use buffer_pool::{Buffer, BufferPool}; use device::NetworkDevice; use error::{Error, Result}; +use arp_cache::LoArpCache; use self::ip::IpScheme; use self::tcp::TcpScheme; use self::udp::UdpScheme; @@ -41,11 +42,11 @@ pub struct Smolnetd { tcp_scheme: TcpScheme, input_queue: Rc>>, - input_buffer_pool: BufferPool, + buffer_pool: Rc>, } impl Smolnetd { - const INGRESS_PACKET_SIZE: usize = 2048; + const MAX_PACKET_SIZE: usize = 2048; const SOCKET_BUFFER_SIZE: usize = 128; //packets const MIN_CHECK_TIMEOUT_MS: i64 = 10; const MAX_CHECK_TIMEOUT_MS: i64 = 500; @@ -57,17 +58,28 @@ impl Smolnetd { tcp_file: File, time_file: File, ) -> Smolnetd { - let arp_cache = SliceArpCache::new(vec![Default::default(); 8]); let hardware_addr = EthernetAddress::from_str(getcfg("mac").unwrap().trim()) .expect("Can't parse the 'mac' cfg"); let local_ip = IpAddress::from_str(getcfg("ip").unwrap().trim()).expect("Can't parse the 'ip' cfg."); - let protocol_addrs = [IpCidr::new(local_ip, 24)]; + let protocol_addrs = [ + IpCidr::new(local_ip, 24), + IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), + ]; let default_gw = Ipv4Address::from_str(getcfg("ip_router").unwrap().trim()) .expect("Can't parse the 'ip_router' cfg."); + + + let buffer_pool = Rc::new(RefCell::new(BufferPool::new(Self::MAX_PACKET_SIZE))); let input_queue = Rc::new(RefCell::new(VecDeque::new())); let network_file = Rc::new(RefCell::new(network_file)); - let network_device = NetworkDevice::new(Rc::clone(&network_file), Rc::clone(&input_queue)); + let network_device = NetworkDevice::new( + Rc::clone(&network_file), + Rc::clone(&input_queue), + hardware_addr, + buffer_pool.clone(), + ); + let arp_cache = LoArpCache::new(protocol_addrs.iter().map(IpCidr::address)); let iface = EthernetInterface::new( Box::new(network_device), Box::new(arp_cache) as Box, @@ -86,7 +98,7 @@ impl Smolnetd { tcp_scheme: TcpScheme::new(Rc::clone(&socket_set), tcp_file), input_queue, network_file, - input_buffer_pool: BufferPool::new(Self::INGRESS_PACKET_SIZE), + buffer_pool, } } @@ -173,7 +185,7 @@ impl Smolnetd { fn read_frames(&mut self) -> Result { let mut total_frames = 0; loop { - let mut buffer = self.input_buffer_pool.get_buffer(); + let mut buffer = self.buffer_pool.borrow_mut().get_buffer(); let count = self.network_file .borrow_mut() .read(&mut buffer)