Add support for multiple interfaces

- Smoltcp now talk to a IP device that does the routing
- Add RouteTable
- Add trait LinkDevice representing devices (eth0, loopback...)
- Remove old device
This commit is contained in:
Gartox
2023-09-04 10:11:02 +02:00
parent d47cdbf1fe
commit 0b2b4e41c3
16 changed files with 1337 additions and 333 deletions
-123
View File
@@ -1,123 +0,0 @@
use smoltcp;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fs::File;
use std::io::Write;
use std::rc::Rc;
use smoltcp::time::Instant;
use smoltcp::wire::EthernetAddress;
use crate::buffer_pool::{Buffer, BufferPool};
struct NetworkDeviceData {
network_file: Rc<RefCell<File>>,
input_queue: Rc<RefCell<VecDeque<Buffer>>>,
local_hwaddr: smoltcp::wire::EthernetAddress,
buffer_pool: Rc<RefCell<BufferPool>>,
}
pub struct NetworkDevice {
data: Rc<RefCell<NetworkDeviceData>>,
}
impl NetworkDevice {
pub const MTU: usize = 1520;
pub fn new(
network_file: Rc<RefCell<File>>,
input_queue: Rc<RefCell<VecDeque<Buffer>>>,
local_hwaddr: smoltcp::wire::EthernetAddress,
buffer_pool: Rc<RefCell<BufferPool>>,
) -> NetworkDevice {
NetworkDevice {
data: Rc::new(RefCell::new(NetworkDeviceData {
network_file,
input_queue,
local_hwaddr,
buffer_pool,
})),
}
}
}
pub struct RxToken {
buffer: Buffer,
}
impl smoltcp::phy::RxToken for RxToken {
fn consume<R, F>(mut self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(&mut self.buffer)
}
}
pub struct TxToken {
data: Rc<RefCell<NetworkDeviceData>>,
}
impl smoltcp::phy::TxToken for TxToken {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let data = self.data.borrow_mut();
let mut buffer = data.buffer_pool.borrow_mut().get_buffer();
buffer.resize(len);
let res = f(&mut buffer);
let mut loopback = false;
if let Ok(mut frame) = smoltcp::wire::EthernetFrame::new_checked(&mut buffer) {
if frame.dst_addr() == EthernetAddress::default() {
frame.set_dst_addr(data.local_hwaddr);
loopback = true;
}
}
if loopback {
data.input_queue.borrow_mut().push_back(buffer.move_out());
} else {
// TODO: Handle error
data.network_file
.borrow_mut()
.write(&buffer);
}
res
}
}
impl smoltcp::phy::Device for NetworkDevice {
type RxToken<'a> = RxToken;
type TxToken<'a> = TxToken;
fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
let mut limits = smoltcp::phy::DeviceCapabilities::default();
limits.max_transmission_unit = Self::MTU;
limits.max_burst_size = Some(20);
limits
}
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let data = self.data.borrow_mut();
let buffer = data.input_queue.borrow_mut().pop_front();
if let Some(buffer) = buffer {
Some((
RxToken { buffer },
TxToken {
data: Rc::clone(&self.data),
},
))
} else {
None
}
}
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
Some(TxToken {
data: Rc::clone(&self.data),
})
}
}
+355
View File
@@ -0,0 +1,355 @@
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
use std::rc::Rc;
use smoltcp::storage::PacketMetadata;
use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{
ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol,
EthernetRepr, IpAddress, Ipv4Address, Ipv4Cidr,
};
use super::LinkDevice;
struct Neighbor {
hardware_address: EthernetAddress,
expires_at: Instant,
}
#[derive(Debug, Default)]
enum ArpState {
#[default]
Discovered,
Discovering {
target: Ipv4Address,
tries: u32,
silent_until: Instant,
},
}
type PacketBuffer = smoltcp::storage::PacketBuffer<'static, IpAddress>;
pub struct EthernetLink {
name: Rc<str>,
neighbor_cache: BTreeMap<IpAddress, Neighbor>,
arp_state: ArpState,
waiting_packets: PacketBuffer,
input_buffer: Vec<u8>,
output_buffer: Vec<u8>,
network_file: File,
hardware_address: EthernetAddress,
ip_address: Ipv4Cidr,
}
impl EthernetLink {
// TODO: Review these constants
const MAX_WAITING_PACKET_COUNT: usize = 10;
const MTU: usize = 1500;
const WAITING_PACKET_BUFFER_SIZE: usize = Self::MTU * Self::MAX_WAITING_PACKET_COUNT;
const NEIGHBOR_LIVE_TIME: Duration = Duration::from_secs(60);
const ARP_SILENCE_TIME: Duration = Duration::from_secs(1);
pub fn new(
name: &str,
network_file: File,
hardware_address: EthernetAddress,
ip_address: Ipv4Cidr,
) -> Self {
let waiting_packets = PacketBuffer::new(
vec![PacketMetadata::EMPTY; Self::MAX_WAITING_PACKET_COUNT],
vec![0u8; Self::WAITING_PACKET_BUFFER_SIZE],
);
Self {
name: name.into(),
network_file,
waiting_packets,
hardware_address,
ip_address,
input_buffer: vec![0u8; Self::MTU],
output_buffer: Vec::with_capacity(Self::MTU),
arp_state: Default::default(),
neighbor_cache: Default::default(),
}
}
fn send_to<F>(&mut self, dst: EthernetAddress, size: usize, f: F, proto: EthernetProtocol)
where
F: FnOnce(&mut [u8]),
{
let repr = EthernetRepr {
src_addr: self.hardware_address,
dst_addr: dst,
ethertype: proto,
};
self.output_buffer.clear();
self.output_buffer.resize(repr.buffer_len() + size, 0);
let mut frame = EthernetFrame::new_unchecked(&mut self.output_buffer);
repr.emit(&mut frame);
f(frame.payload_mut());
if let Err(_) = self.network_file.write_all(&self.output_buffer) {
error!(
"Dropped outboud packet on {} (failed to write to network file)",
self.name
)
}
}
fn process_arp(&mut self, packet: &[u8], now: Instant) {
let Ok(repr) = ArpPacket::new_checked(packet).and_then(|packet| ArpRepr::parse(&packet)) else {
debug!("Dropped incomming arp packet on {} (Malformed)", self.name);
return;
};
match repr {
ArpRepr::EthernetIpv4 {
operation,
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
} => {
if self.hardware_address != target_hardware_addr {
// Only process packet that are for us
return;
}
if let ArpOperation::Unknown(_) = operation {
return;
}
if !source_hardware_addr.is_unicast() || !source_protocol_addr.is_unicast() {
return;
}
if !self.ip_address.contains_addr(&target_protocol_addr) {
return;
}
self.neighbor_cache.insert(
IpAddress::Ipv4(source_protocol_addr),
Neighbor {
hardware_address: source_hardware_addr,
expires_at: now + Self::NEIGHBOR_LIVE_TIME,
},
);
if let ArpOperation::Request = operation {
let response = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply,
source_hardware_addr: self.hardware_address,
source_protocol_addr: self.ip_address.address(),
target_hardware_addr: source_hardware_addr,
target_protocol_addr: source_protocol_addr,
};
self.send_to(
source_hardware_addr,
response.buffer_len(),
|buf| response.emit(&mut ArpPacket::new_unchecked(buf)),
EthernetProtocol::Arp,
);
}
self.check_waiting_packets(source_protocol_addr, source_hardware_addr, now);
}
_ => {}
}
}
fn check_waiting_packets(&mut self, ip: Ipv4Address, mac: EthernetAddress, now: Instant) {
let mut waiting_packets =
std::mem::replace(&mut self.waiting_packets, PacketBuffer::new(vec![], vec![]));
loop {
match waiting_packets.peek() {
Ok((IpAddress::Ipv4(dst), _)) if dst == &ip => {}
Ok((IpAddress::Ipv4(dst), _)) => {
self.arp_state = ArpState::Discovering {
target: *dst,
tries: 0,
silent_until: Instant::ZERO,
};
self.send_arp(now);
break;
}
Err(_) => {
self.arp_state = ArpState::Discovered;
break;
}
}
let (_, packet) = waiting_packets.dequeue().unwrap();
self.send_to(
mac,
packet.len(),
|buf| buf.copy_from_slice(packet),
EthernetProtocol::Ipv4,
);
}
self.waiting_packets = waiting_packets;
}
fn drop_waiting_packets(&mut self, ip: Ipv4Address, now: Instant) {
loop {
match self.waiting_packets.peek() {
Ok((IpAddress::Ipv4(dst), _)) if dst == &ip => {}
Ok((IpAddress::Ipv4(dst), _)) => {
self.arp_state = ArpState::Discovering {
target: *dst,
tries: 0,
silent_until: Instant::ZERO,
};
self.send_arp(now);
return;
}
Err(_) => {
self.arp_state = ArpState::Discovered;
return;
}
}
let _ = self.waiting_packets.dequeue();
debug!(
"Dropped packet on {} because neighbor was not found",
self.name
)
}
}
fn handle_missing_neighbor(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) {
let Ok(buf) = self.waiting_packets.enqueue(packet.len(), next_hop) else {
warn!("Dropped packet on {} because waiting queue was full", self.name);
return;
};
buf.copy_from_slice(packet);
let IpAddress::Ipv4(next_hop) = next_hop;
if let ArpState::Discovered = self.arp_state {
self.arp_state = ArpState::Discovering {
target: next_hop,
tries: 0,
silent_until: Instant::ZERO,
};
self.send_arp(now)
}
}
fn send_arp(&mut self, now: Instant) {
match self.arp_state {
ArpState::Discovered => {}
ArpState::Discovering { silent_until, .. } if silent_until > now => {}
ArpState::Discovering { target, tries, .. } if tries >= 3 => {
self.drop_waiting_packets(target, now)
}
ArpState::Discovering {
target,
ref mut tries,
ref mut silent_until,
} => {
let arp_repr = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Request,
source_hardware_addr: self.hardware_address,
source_protocol_addr: self.ip_address.address(),
target_hardware_addr: EthernetAddress::BROADCAST,
target_protocol_addr: target,
};
*tries += 1;
*silent_until = now + Self::ARP_SILENCE_TIME;
self.send_to(
EthernetAddress::BROADCAST,
arp_repr.buffer_len(),
|buf| arp_repr.emit(&mut ArpPacket::new_unchecked(buf)),
EthernetProtocol::Arp,
);
}
}
}
}
impl LinkDevice for EthernetLink {
fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) {
let local_broadcast = match self.ip_address.broadcast() {
Some(addr) => IpAddress::Ipv4(addr) == next_hop,
None => false
};
if local_broadcast || next_hop.is_broadcast() {
self.send_to(
EthernetAddress::BROADCAST,
packet.len(),
|buf| buf.copy_from_slice(packet),
EthernetProtocol::Ipv4,
);
return;
}
match self.neighbor_cache.entry(next_hop) {
Entry::Vacant(_) => self.handle_missing_neighbor(next_hop, packet, now),
Entry::Occupied(e) => {
if e.get().expires_at < now {
e.remove();
self.handle_missing_neighbor(next_hop, packet, now)
} else {
let mac = e.get().hardware_address;
self.send_to(
mac,
packet.len(),
|buf| buf.copy_from_slice(packet),
EthernetProtocol::Ipv4,
)
}
}
}
}
fn recv(&mut self, now: Instant) -> Option<&[u8]> {
let mut input_buffer = std::mem::replace(&mut self.input_buffer, Vec::new());
loop {
if let Err(e) = self.network_file.read(&mut input_buffer) {
if e.kind() != ErrorKind::WouldBlock {
error!("Failed to read ethernet device on link {}", self.name);
} else {
// No packet to read but we check if we have arp to send
self.send_arp(now);
}
self.input_buffer = input_buffer;
return None;
}
let packet = EthernetFrame::new_unchecked(&input_buffer[..]);
let Ok(repr) = EthernetRepr::parse(&packet) else {
debug!("Dropped incomming frame on {} (Malformed)", self.name);
continue;
};
if !repr.dst_addr.is_broadcast() && repr.dst_addr != self.hardware_address {
// Drop packets which are not for us
continue;
}
match repr.ethertype {
EthernetProtocol::Ipv4 => {
self.input_buffer = input_buffer;
return Some(EthernetFrame::new_unchecked(&self.input_buffer[..]).payload());
}
EthernetProtocol::Arp => self.process_arp(packet.payload(), now),
_ => continue,
}
}
}
fn name(&self) -> &Rc<str> {
&self.name
}
}
+45
View File
@@ -0,0 +1,45 @@
use std::rc::Rc;
use smoltcp::storage::PacketMetadata;
use smoltcp::time::Instant;
use crate::scheme::Smolnetd;
use super::LinkDevice;
pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>;
pub struct LoopbackDevice {
name: Rc<str>,
buffer: PacketBuffer,
}
impl Default for LoopbackDevice {
fn default() -> Self {
let buffer = PacketBuffer::new(
vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0u8; 1500 * Smolnetd::SOCKET_BUFFER_SIZE],
);
LoopbackDevice {
name: "loopback".into(),
buffer,
}
}
}
impl LinkDevice for LoopbackDevice {
fn send(&mut self, _next_hop: smoltcp::wire::IpAddress, packet: &[u8], _now: Instant) {
match self.buffer.enqueue(packet.len(), ()) {
Err(_) => warn!("loopback dropped packet because buffer was full"),
Ok(buf) => buf.copy_from_slice(packet),
}
}
fn recv(&mut self, _now: Instant) -> Option<&[u8]> {
self.buffer.dequeue().ok().map(|((), buf)| &*buf)
}
fn name(&self) -> &std::rc::Rc<str> {
&self.name
}
}
+57
View File
@@ -0,0 +1,57 @@
pub mod loopback;
pub mod ethernet;
use std::rc::Rc;
use smoltcp::time::Instant;
use smoltcp::wire::IpAddress;
/// Represent a link layer device (eth0, loopback...)
pub trait LinkDevice {
/// Send the given packet to the machine with the `next_hop` ip address
/// This method cannot fail so it's the implementor responsability
/// to buffer packets which can't be sent immediatly or decide to
/// drop them if necessary
fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant);
/// Returns None if nothing is received.
/// Returns an Ip packet otherwise
fn recv(&mut self, now: Instant) -> Option<&[u8]>;
/// Returns the LinkDevice display name used to refer to it and for lookups
fn name(&self) -> &Rc<str>;
}
#[derive(Default)]
pub struct DeviceList {
inner: Vec<Box<dyn LinkDevice>>,
}
impl DeviceList {
pub fn push<T: LinkDevice + 'static>(&mut self, dev: T) {
self.inner.push(Box::new(dev))
}
pub fn get(&self, device_name: &str) -> Option<&dyn LinkDevice> {
self.inner
.iter()
.find(|dev| dev.name().as_ref() == device_name)
.map(|device| device.as_ref())
}
pub fn get_mut(&mut self, device_name: &str) -> Option<&mut (dyn LinkDevice + 'static)> {
self.inner
.iter_mut()
.find(|dev| dev.name().as_ref() == device_name)
.map(|device| device.as_mut())
}
pub fn iter(&self) -> impl Iterator<Item = &(dyn LinkDevice + 'static)> {
self.inner.iter().map(|b| b.as_ref())
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn LinkDevice + 'static)> {
self.inner.iter_mut().map(|b| b.as_mut())
}
}
+7 -5
View File
@@ -3,11 +3,11 @@
extern crate event;
#[macro_use]
extern crate log;
extern crate byteorder;
extern crate netutils;
extern crate redox_netstack;
extern crate smoltcp;
extern crate syscall;
extern crate byteorder;
use std::cell::RefCell;
use std::fs::File;
@@ -15,14 +15,15 @@ use std::os::unix::io::{FromRawFd, RawFd};
use std::process;
use std::rc::Rc;
use event::EventQueue;
use redox_netstack::error::{Error, Result};
use redox_netstack::logger;
use event::EventQueue;
use scheme::Smolnetd;
mod buffer_pool;
mod device;
mod link;
mod port_set;
mod router;
mod scheme;
fn run(daemon: redox_daemon::Daemon) -> Result<()> {
@@ -143,7 +144,7 @@ fn run(daemon: redox_daemon::Daemon) -> Result<()> {
event_queue.trigger_all(event::Event {
fd: 0,
flags: EventFlags::empty()
flags: EventFlags::empty(),
})?;
event_queue.run()
@@ -158,5 +159,6 @@ fn main() {
process::exit(1);
}
process::exit(0);
}).expect("smoltcp: failed to daemonize");
})
.expect("smoltcp: failed to daemonize");
}
+187
View File
@@ -0,0 +1,187 @@
use std::cell::RefCell;
use std::rc::Rc;
use smoltcp::phy::{Device, DeviceCapabilities, Medium};
use smoltcp::storage::PacketMetadata;
use smoltcp::time::Instant;
use smoltcp::wire::IpAddress;
use self::route_table::RouteTable;
use crate::link::DeviceList;
use crate::scheme::Smolnetd;
pub mod route_table;
pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>;
pub struct Router {
rx_buffer: PacketBuffer,
tx_buffer: PacketBuffer,
devices: Rc<RefCell<DeviceList>>,
route_table: Rc<RefCell<RouteTable>>,
}
impl Router {
pub fn new(devices: Rc<RefCell<DeviceList>>, route_table: Rc<RefCell<RouteTable>>) -> Self {
let rx_buffer = PacketBuffer::new(
vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
);
let tx_buffer = PacketBuffer::new(
vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
);
Self {
rx_buffer,
tx_buffer,
devices,
route_table,
}
}
pub const MTU: usize = 1486;
pub fn poll(&mut self, now: Instant) {
self.dispatch_outgoing(now);
self.process_incoming(now);
}
fn process_incoming(&mut self, now: Instant) {
for dev in self.devices.borrow_mut().iter_mut() {
if self.rx_buffer.is_full() {
break;
}
loop {
if self.rx_buffer.is_full() {
break;
}
let Some(buf) = dev.recv(now) else {
break;
};
self.rx_buffer
.enqueue(buf.len(), ())
.expect("We checked if it was full")
.copy_from_slice(buf);
}
}
}
fn dispatch_outgoing(&mut self, now: Instant) {
while let Ok(((), packet)) = self.tx_buffer.dequeue() {
if let Ok(mut packet) = smoltcp::wire::Ipv4Packet::new_checked(packet) {
let dst_addr = IpAddress::Ipv4(packet.dst_addr());
if packet.dst_addr().is_broadcast() {
let buf = packet.into_inner();
for dev in self.devices.borrow_mut().iter_mut() {
dev.send(dst_addr, buf, now)
}
} else {
let route_table = self.route_table.borrow();
let Some(rule) = route_table.lookup_rule(&dst_addr) else {
warn!("No route found for destination: {}", dst_addr);
continue;
};
let next_hop = match rule.via {
Some(via) => via,
None => dst_addr,
};
let mut devices = self.devices.borrow_mut();
let Some(dev) = devices.get_mut(&rule.dev) else {
warn!("Device {} not found", rule.dev);
// TODO: Remove route if device doesn't exist anymore ?
continue;
};
let IpAddress::Ipv4(src) = rule.src;
if src != packet.src_addr() {
packet.set_src_addr(src);
packet.fill_checksum()
}
dev.send(next_hop, packet.into_inner(), now);
}
}
}
}
}
impl Device for Router {
type RxToken<'a> = RxToken<'a>;
type TxToken<'a> = TxToken<'a>;
fn receive(
&mut self,
_timestamp: smoltcp::time::Instant,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if self.rx_buffer.is_empty() || self.tx_buffer.is_full() {
None
} else {
Some((
RxToken {
rx_buffer: &mut self.rx_buffer,
},
TxToken {
tx_buffer: &mut self.tx_buffer,
},
))
}
}
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
if self.tx_buffer.is_full() {
None
} else {
Some(TxToken {
tx_buffer: &mut self.tx_buffer,
})
}
}
fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.medium = Medium::Ip;
caps.max_transmission_unit = Router::MTU;
caps.max_burst_size = Some(Smolnetd::SOCKET_BUFFER_SIZE);
caps
}
}
pub struct TxToken<'a> {
tx_buffer: &'a mut PacketBuffer,
}
impl smoltcp::phy::TxToken for TxToken<'_> {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(self
.tx_buffer
.enqueue(len, ())
.expect("This was checked before creating the TxToken"))
}
}
pub struct RxToken<'a> {
rx_buffer: &'a mut PacketBuffer,
}
impl<'a> smoltcp::phy::RxToken for RxToken<'a> {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let ((), buf) = self
.rx_buffer
.dequeue()
.expect("This was checked before creating the RxToken");
f(buf)
}
}
+55
View File
@@ -0,0 +1,55 @@
use std::rc::Rc;
use smoltcp::wire::{IpAddress, IpCidr};
#[derive(Debug)]
pub struct Rule {
pub filter: IpCidr,
pub via: Option<IpAddress>,
pub dev: Rc<str>,
pub src: IpAddress,
}
impl Rule {
pub fn new(filter: IpCidr, via: Option<IpAddress>, dev: Rc<str>, src: IpAddress) -> Self {
Self {
filter,
via,
dev,
src,
}
}
}
#[derive(Debug, Default)]
pub struct RouteTable {
rules: Vec<Rule>,
}
impl RouteTable {
pub fn lookup_rule(&self, dst: &IpAddress) -> Option<&Rule> {
self.rules
.iter()
.rev()
.find(|rule| rule.filter.contains_addr(dst))
}
pub fn lookup_src_addr(&self, dst: &IpAddress) -> Option<IpAddress> {
Some(self.lookup_rule(dst)?.src)
}
pub fn lookup_gateway(&self, dst: &IpAddress) -> Option<IpAddress> {
self.lookup_rule(dst)?.via
}
pub fn insert_rule(&mut self, new_rule: Rule) {
let i = match self
.rules
.binary_search_by_key(&new_rule.filter.prefix_len(), |rule| {
rule.filter.prefix_len()
}) {
Ok(i) | Err(i) => i,
};
self.rules.insert(i, new_rule);
}
}
+8 -8
View File
@@ -7,10 +7,10 @@ use syscall::{Error as SyscallError, Result as SyscallResult};
use syscall;
use byteorder::{ByteOrder, NetworkEndian};
use crate::device::NetworkDevice;
use crate::port_set::PortSet;
use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme};
use super::{Smolnetd, SocketSet, Interface};
use crate::router::Router;
use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme, Context};
use super::{Smolnetd, SocketSet};
pub type IcmpScheme = SocketScheme<IcmpSocket<'static>>;
@@ -75,7 +75,7 @@ impl<'a> SchemeSocket for IcmpSocket<'a> {
path: &str,
_uid: u32,
ident_set: &mut Self::SchemeDataT,
iface: &Interface
_context: &Context
) -> SyscallResult<(SocketHandle, Self::DataT)> {
use std::str::FromStr;
@@ -95,11 +95,11 @@ impl<'a> SchemeSocket for IcmpSocket<'a> {
let socket = IcmpSocket::new(
IcmpSocketBuffer::new(
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
),
IcmpSocketBuffer::new(
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
)
);
let handle = socket_set.add(socket);
@@ -127,11 +127,11 @@ impl<'a> SchemeSocket for IcmpSocket<'a> {
let socket = IcmpSocket::new(
IcmpSocketBuffer::new(
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
),
IcmpSocketBuffer::new(
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
)
);
let handle = socket_set.add(socket);
+7 -6
View File
@@ -5,9 +5,10 @@ use std::str;
use syscall::{Error as SyscallError, Result as SyscallResult};
use syscall;
use crate::device::NetworkDevice;
use super::{Smolnetd, SocketSet, Interface};
use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme};
use crate::router::Router;
use super::{Smolnetd, SocketSet};
use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme, Context};
pub type IpScheme = SocketScheme<RawSocket<'static>>;
@@ -59,7 +60,7 @@ impl<'a> SchemeSocket for RawSocket<'a> {
path: &str,
uid: u32,
_: &mut Self::SchemeDataT,
iface: &Interface
_context: &Context
) -> SyscallResult<(SocketHandle, Self::DataT)> {
if uid != 0 {
return Err(SyscallError::new(syscall::EACCES));
@@ -69,11 +70,11 @@ impl<'a> SchemeSocket for RawSocket<'a> {
let rx_buffer = RawSocketBuffer::new(
vec![RawPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
);
let tx_buffer = RawSocketBuffer::new(
vec![RawPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
);
let ip_socket = RawSocket::new(
IpVersion::Ipv4,
+116 -71
View File
@@ -1,35 +1,41 @@
use crate::link::ethernet::EthernetLink;
use crate::link::LinkDevice;
use crate::link::{loopback::LoopbackDevice, DeviceList};
use crate::router::route_table::{RouteTable, Rule};
use crate::router::Router;
use crate::scheme::smoltcp::iface::SocketSet as SmoltcpSocketSet;
use netutils::getcfg;
use smoltcp;
use smoltcp::iface::{Interface as SmoltcpInterface, Routes, Config};
use smoltcp::iface::{Config, Interface as SmoltcpInterface};
use smoltcp::phy::Tracer;
use crate::scheme::smoltcp::iface::SocketSet as SmoltcpSocketSet;
use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, IpListenEndpoint, HardwareAddress};
use smoltcp::wire::{
EthernetAddress, HardwareAddress, IpAddress, IpCidr, IpListenEndpoint, Ipv4Address, Ipv4Cidr,
};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fs::File;
use std::io::{self, Read, Write};
use std::io::{Read, Write};
use std::mem::size_of;
use std::rc::Rc;
use std::str::FromStr;
use syscall::data::TimeSpec;
use syscall;
use syscall::data::TimeSpec;
use crate::buffer_pool::{Buffer, BufferPool};
use crate::device::NetworkDevice;
use redox_netstack::error::{Error, Result};
use self::icmp::IcmpScheme;
use self::ip::IpScheme;
use self::netcfg::NetCfgScheme;
use self::tcp::TcpScheme;
use self::udp::UdpScheme;
use self::icmp::IcmpScheme;
use self::netcfg::NetCfgScheme;
use crate::buffer_pool::{Buffer, BufferPool};
use redox_netstack::error::{Error, Result};
mod icmp;
mod ip;
mod netcfg;
mod socket;
mod tcp;
mod udp;
mod icmp;
mod netcfg;
type SocketSet = SmoltcpSocketSet<'static>;
type Interface = Rc<RefCell<SmoltcpInterface>>;
@@ -38,11 +44,12 @@ const MAX_DURATION: Duration = Duration::from_millis(u64::MAX);
const MIN_DURATION: Duration = Duration::from_millis(0);
pub struct Smolnetd {
network_file: Rc<RefCell<File>>,
network_device: Tracer<NetworkDevice>,
router_device: Tracer<Router>,
iface: Interface,
link_devices: Rc<RefCell<DeviceList>>,
route_table: Rc<RefCell<RouteTable>>,
time_file: File,
iface: Interface,
socket_set: Rc<RefCell<SocketSet>>,
timer: ::std::time::Instant,
@@ -57,10 +64,10 @@ pub struct Smolnetd {
}
impl Smolnetd {
const MAX_PACKET_SIZE: usize = 2048;
const SOCKET_BUFFER_SIZE: usize = 128; //packets
const MIN_CHECK_TIMEOUT: Duration = Duration::from_millis(10);
const MAX_CHECK_TIMEOUT: Duration = Duration::from_millis(500);
pub const MAX_PACKET_SIZE: usize = 2048;
pub const SOCKET_BUFFER_SIZE: usize = 128; //packets
pub const MIN_CHECK_TIMEOUT: Duration = Duration::from_millis(10);
pub const MAX_CHECK_TIMEOUT: Duration = Duration::from_millis(500);
pub fn new(
network_file: File,
@@ -79,48 +86,104 @@ impl Smolnetd {
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 mut network_device = Tracer::new(NetworkDevice::new(
Rc::clone(&network_file),
Rc::clone(&input_queue),
hardware_addr,
Rc::clone(&buffer_pool),
), |_timestamp, printer| {
trace!("{}", printer)
});
let config = Config::new(HardwareAddress::Ethernet(hardware_addr));
let devices = Rc::new(RefCell::new(DeviceList::default()));
let route_table = Rc::new(RefCell::new(RouteTable::default()));
let mut network_device = Tracer::new(
Router::new(Rc::clone(&devices), Rc::clone(&route_table)),
|_timestamp, printer| trace!("{}", printer),
);
let config = Config::new(HardwareAddress::Ip);
let mut iface = SmoltcpInterface::new(config, &mut network_device, Instant::now());
iface.update_ip_addrs(|ip_addrs| ip_addrs.extend(protocol_addrs));
iface.routes_mut().add_default_ipv4_route(default_gw).expect("Failed to add default gateway");
iface
.routes_mut()
.add_default_ipv4_route(default_gw)
.expect("Failed to add default gateway");
let iface = Rc::new(RefCell::new(iface));
let socket_set = Rc::new(RefCell::new(SocketSet::new(vec![])));
let loopback = LoopbackDevice::default();
route_table.borrow_mut().insert_rule(Rule::new(
"127.0.0.0/8".parse().unwrap(),
None,
Rc::clone(loopback.name()),
"127.0.0.1".parse().unwrap(),
));
let IpAddress::Ipv4(local_ip) = local_ip;
let eth0 = EthernetLink::new(
"eth0",
network_file,
hardware_addr,
Ipv4Cidr::new(local_ip, 24),
);
route_table.borrow_mut().insert_rule(Rule::new(
IpCidr::new(IpAddress::Ipv4(local_ip), 24),
None,
Rc::clone(eth0.name()),
IpAddress::Ipv4(local_ip),
));
route_table.borrow_mut().insert_rule(Rule::new(
"0.0.0.0/0".parse().unwrap(),
Some(IpAddress::Ipv4(default_gw)),
Rc::clone(eth0.name()),
IpAddress::Ipv4(local_ip),
));
devices.borrow_mut().push(loopback);
devices.borrow_mut().push(eth0);
Smolnetd {
iface: Rc::clone(&iface),
network_device,
router_device: network_device,
route_table: Rc::clone(&route_table),
socket_set: Rc::clone(&socket_set),
timer: ::std::time::Instant::now(),
link_devices: devices,
time_file,
ip_scheme: IpScheme::new(Rc::clone(&iface), Rc::clone(&socket_set), ip_file),
udp_scheme: UdpScheme::new(Rc::clone(&iface), Rc::clone(&socket_set), udp_file),
tcp_scheme: TcpScheme::new(Rc::clone(&iface), Rc::clone(&socket_set), tcp_file),
icmp_scheme: IcmpScheme::new(Rc::clone(&iface), Rc::clone(&socket_set), icmp_file),
ip_scheme: IpScheme::new(
Rc::clone(&iface),
Rc::clone(&route_table),
Rc::clone(&socket_set),
ip_file,
),
udp_scheme: UdpScheme::new(
Rc::clone(&iface),
Rc::clone(&route_table),
Rc::clone(&socket_set),
udp_file,
),
tcp_scheme: TcpScheme::new(
Rc::clone(&iface),
Rc::clone(&route_table),
Rc::clone(&socket_set),
tcp_file,
),
icmp_scheme: IcmpScheme::new(
Rc::clone(&iface),
Rc::clone(&route_table),
Rc::clone(&socket_set),
icmp_file,
),
netcfg_scheme: NetCfgScheme::new(Rc::clone(&iface), netcfg_file),
input_queue,
network_file,
buffer_pool,
}
}
pub fn on_network_scheme_event(&mut self) -> Result<Option<()>> {
if self.read_frames()? > 0 {
self.poll()?;
}
self.poll()?;
Ok(None)
}
@@ -183,22 +246,23 @@ impl Smolnetd {
let mut iter_limit = 10usize;
let mut iface = self.iface.borrow_mut();
let mut socket_set = self.socket_set.borrow_mut();
let timestamp = Instant::from(self.timer);
loop {
let timestamp = Instant::from(self.timer);
if iter_limit == 0 {
break MIN_DURATION;
}
iter_limit -= 1;
// TODO: Check what if the bool returned by poll can be useful
iface.poll(timestamp, &mut self.network_device, &mut socket_set);
self.router_device.get_mut().poll(timestamp);
// TODO: Check what if the bool returned by poll can be useful
iface.poll(timestamp, &mut self.router_device, &mut socket_set);
match iface.poll_delay(timestamp, &socket_set) {
Some(delay) if delay == Duration::ZERO => { }
Some(delay) => {
break ::std::cmp::min(MAX_DURATION, delay)
}
None => break MAX_DURATION
Some(delay) if delay == Duration::ZERO => {}
Some(delay) => break ::std::cmp::min(MAX_DURATION, delay),
None => break MAX_DURATION,
};
}
};
@@ -211,26 +275,6 @@ impl Smolnetd {
))
}
fn read_frames(&mut self) -> Result<usize> {
let mut total_frames = 0;
loop {
let mut buffer = self.buffer_pool.borrow_mut().get_buffer();
let count = match self.network_file.borrow_mut().read(&mut buffer) {
Ok(count) => count,
Err(err) => match err.kind() {
io::ErrorKind::WouldBlock => break,
_ => return Err(
Error::from_io_error(err, "Failed to read from network file")
)
}
};
buffer.resize(count);
self.input_queue.borrow_mut().push_back(buffer);
total_frames += 1;
}
Ok(total_frames)
}
fn notify_sockets(&mut self) -> Result<()> {
self.ip_scheme.notify_sockets()?;
self.udp_scheme.notify_sockets()?;
@@ -257,14 +301,15 @@ fn post_fevent(scheme_file: &mut File, fd: usize, event: usize, data_len: usize)
fn parse_endpoint(socket: &str) -> IpListenEndpoint {
let mut socket_parts = socket.split(':');
let host =
Ipv4Address::from_str(socket_parts.next().unwrap_or("")).ok().filter(|addr| !addr.is_unspecified()).map(IpAddress::Ipv4);
let host = Ipv4Address::from_str(socket_parts.next().unwrap_or(""))
.ok()
.filter(|addr| !addr.is_unspecified())
.map(IpAddress::Ipv4);
let port = socket_parts
.next()
.unwrap_or("")
.parse::<u16>()
.unwrap_or(0);
IpListenEndpoint{ addr: host, port}
IpListenEndpoint { addr: host, port }
}
+3 -2
View File
@@ -158,7 +158,8 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe
"mac" => {
rw [iface, notifier] (Option<EthernetAddress>, None)
|| {
format!("{}\n", iface.borrow().hardware_addr())
// format!("{}\n", iface.borrow().hardware_addr())
String::new()
}
|cur_value, line| {
if cur_value.is_none() {
@@ -175,7 +176,7 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe
}
|cur_value| {
if let Some(mac) = *cur_value {
iface.borrow_mut().set_hardware_addr(smoltcp::wire::HardwareAddress::Ethernet(mac));
// iface.borrow_mut().set_hardware_addr(smoltcp::wire::HardwareAddress::Ethernet(mac));
notifier.borrow_mut().schedule_notify("ifaces/eth0/mac");
}
Ok(())
+25 -6
View File
@@ -19,12 +19,18 @@ use syscall::{
};
use super::Interface;
use crate::router::route_table::RouteTable;
use crate::scheme::smoltcp::iface::SocketHandle;
use redox_netstack::error::{Error, Result};
use smoltcp::socket::AnySocket;
use super::{post_fevent, SocketSet};
pub struct Context {
pub iface: Interface,
pub route_table: Rc<RefCell<RouteTable>>,
}
pub struct NullFile {
pub flags: usize,
pub uid: u32,
@@ -192,7 +198,7 @@ where
path: &str,
uid: u32,
data: &mut Self::SchemeDataT,
iface: &Interface,
context: &Context,
) -> SyscallResult<(SocketHandle, Self::DataT)>;
fn close_file(
@@ -231,7 +237,7 @@ where
nulls: BTreeMap<usize, NullFile>,
files: BTreeMap<usize, SchemeFile<SocketT>>,
ref_counts: BTreeMap<SocketHandle, usize>,
iface: Interface,
context: Context,
socket_set: Rc<RefCell<SocketSet>>,
scheme_file: File,
wait_queue: WaitQueue,
@@ -245,13 +251,13 @@ where
{
pub fn new(
iface: Interface,
route_table: Rc<RefCell<RouteTable>>,
socket_set: Rc<RefCell<SocketSet>>,
scheme_file: File,
) -> SocketScheme<SocketT> {
SocketScheme {
next_fd: 1,
nulls: BTreeMap::new(),
iface,
files: BTreeMap::new(),
ref_counts: BTreeMap::new(),
socket_set,
@@ -259,6 +265,7 @@ where
scheme_file,
wait_queue: Vec::new(),
_phantom_socket: PhantomData,
context: Context { iface, route_table },
}
}
@@ -514,7 +521,7 @@ where
path,
uid,
&mut self.scheme_data,
&self.iface,
&self.context,
)?;
let file = SchemeFile::Socket(SocketFile {
@@ -606,7 +613,12 @@ where
SchemeFile::Socket(ref mut file) => {
let mut socket_set = self.socket_set.borrow_mut();
let socket = socket_set.get_mut::<SocketT>(file.socket_handle);
return SocketT::write_buf(socket, file, buf);
let ret = SocketT::write_buf(socket, file, buf);
match ret {
Ok(None) => {}
_ => file.write_notified = false,
}
return ret;
}
}
};
@@ -626,7 +638,14 @@ where
SchemeFile::Socket(ref mut file) => {
let mut socket_set = self.socket_set.borrow_mut();
let socket = socket_set.get_mut::<SocketT>(file.socket_handle);
return SocketT::read_buf(socket, file, buf);
let ret = SocketT::read_buf(socket, file, buf);
match ret {
Ok(None) => {}
_ => file.read_notified = false
}
return ret;
}
}
};
+28 -9
View File
@@ -6,8 +6,8 @@ use std::str;
use syscall;
use syscall::{Error as SyscallError, Result as SyscallResult};
use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme};
use super::{parse_endpoint, Interface, SocketSet};
use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme};
use super::{parse_endpoint, SocketSet};
use crate::port_set::PortSet;
pub type TcpScheme = SocketScheme<TcpSocket<'static>>;
@@ -62,7 +62,7 @@ impl<'a> SchemeSocket for TcpSocket<'a> {
path: &str,
uid: u32,
port_set: &mut Self::SchemeDataT,
iface: &Interface,
context: &Context,
) -> SyscallResult<(SocketHandle, Self::DataT)> {
trace!("TCP open {}", path);
let mut parts = path.split('/');
@@ -92,10 +92,27 @@ impl<'a> SchemeSocket for TcpSocket<'a> {
let tcp_socket = socket_set.get_mut::<TcpSocket>(socket_handle);
let listen_enpoint = if remote_endpoint.is_specified() {
let local_endpoint_addr = match local_endpoint.addr {
Some(addr) if !addr.is_unspecified() => Some(addr),
_ => {
let route_table = context.route_table.borrow();
let addr = route_table
.lookup_src_addr(&remote_endpoint.addr.expect("Checked in is_specified"));
if matches!(addr, None) {
error!("Opening a TCP connection with a probably invalid source IP as no route have been found for destination: {}", remote_endpoint);
}
addr
}
};
let local_endpoint = IpListenEndpoint {
addr: local_endpoint_addr,
port: local_endpoint.port,
};
trace!("Connecting tcp {} {}", local_endpoint, remote_endpoint);
tcp_socket
.connect(
iface.borrow_mut().context(),
context.iface.borrow_mut().context(),
IpEndpoint::new(remote_endpoint.addr.unwrap(), remote_endpoint.port),
local_endpoint,
)
@@ -264,11 +281,13 @@ impl<'a> SchemeSocket for TcpSocket<'a> {
data: Some(endpoint),
..
}),
) => if endpoint.is_specified() {
write!(&mut path, "{}", endpoint).unwrap()
} else {
write!(&mut path, "0.0.0.0:{}", endpoint.port).unwrap()
},
) => {
if endpoint.is_specified() {
write!(&mut path, "{}", endpoint).unwrap()
} else {
write!(&mut path, "0.0.0.0:{}", endpoint.port).unwrap()
}
}
_ => path.push_str(unspecified),
}
trace!("fpath: {}", path);
+5 -5
View File
@@ -5,9 +5,9 @@ use std::str;
use syscall::{Error as SyscallError, Result as SyscallResult};
use syscall;
use crate::device::NetworkDevice;
use crate::port_set::PortSet;
use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme};
use crate::router::Router;
use super::socket::{DupResult, SchemeFile, SchemeSocket, SocketFile, SocketScheme, Context};
use super::{parse_endpoint, Smolnetd, SocketSet, Interface};
pub type UdpScheme = SocketScheme<UdpSocket<'static>>;
@@ -62,7 +62,7 @@ impl<'a> SchemeSocket for UdpSocket<'a> {
path: &str,
uid: u32,
port_set: &mut Self::SchemeDataT,
_iface: &Interface
_context: &Context
) -> SyscallResult<(SocketHandle, Self::DataT)> {
let mut parts = path.split('/');
let remote_endpoint = parse_endpoint(parts.next().unwrap_or(""));
@@ -74,11 +74,11 @@ impl<'a> SchemeSocket for UdpSocket<'a> {
let rx_buffer = UdpSocketBuffer::new(
vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
);
let tx_buffer = UdpSocketBuffer::new(
vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0; NetworkDevice::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE]
);
let udp_socket = UdpSocket::new(rx_buffer, tx_buffer);