From 2645eeadb4464bc0d644ed9bc64bd3abf066b523 Mon Sep 17 00:00:00 2001 From: Gartox Date: Thu, 7 Sep 2023 22:03:13 +0200 Subject: [PATCH] Change netcfg to adapt to the new router --- src/smolnetd/link/ethernet.rs | 86 +++++--- src/smolnetd/link/loopback.rs | 17 ++ src/smolnetd/link/mod.rs | 8 +- src/smolnetd/router/mod.rs | 1 + src/smolnetd/router/route_table.rs | 44 ++++ src/smolnetd/scheme/mod.rs | 35 +-- src/smolnetd/scheme/netcfg/mod.rs | 329 +++++++++++++++-------------- 7 files changed, 309 insertions(+), 211 deletions(-) diff --git a/src/smolnetd/link/ethernet.rs b/src/smolnetd/link/ethernet.rs index 4d057956aa..111569aee9 100644 --- a/src/smolnetd/link/ethernet.rs +++ b/src/smolnetd/link/ethernet.rs @@ -8,7 +8,7 @@ use smoltcp::storage::PacketMetadata; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{ ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol, - EthernetRepr, IpAddress, Ipv4Address, Ipv4Cidr, + EthernetRepr, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr, }; use super::LinkDevice; @@ -31,7 +31,7 @@ enum ArpState { type PacketBuffer = smoltcp::storage::PacketBuffer<'static, IpAddress>; -const EMPTY_MAC: EthernetAddress = EthernetAddress([0;6]); +const EMPTY_MAC: EthernetAddress = EthernetAddress([0; 6]); pub struct EthernetLink { name: Rc, @@ -41,8 +41,8 @@ pub struct EthernetLink { input_buffer: Vec, output_buffer: Vec, network_file: File, - hardware_address: EthernetAddress, - ip_address: Ipv4Cidr, + hardware_address: Option, + ip_address: Option, } impl EthernetLink { @@ -54,12 +54,7 @@ impl EthernetLink { 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 { + pub fn new(name: &str, network_file: File) -> Self { let waiting_packets = PacketBuffer::new( vec![PacketMetadata::EMPTY; Self::MAX_WAITING_PACKET_COUNT], vec![0u8; Self::WAITING_PACKET_BUFFER_SIZE], @@ -69,8 +64,8 @@ impl EthernetLink { name: name.into(), network_file, waiting_packets, - hardware_address, - ip_address, + hardware_address: None, + ip_address: None, input_buffer: vec![0u8; Self::MTU], output_buffer: Vec::with_capacity(Self::MTU), arp_state: Default::default(), @@ -82,8 +77,12 @@ impl EthernetLink { where F: FnOnce(&mut [u8]), { + let Some(hardware_address) = self.hardware_address else { + return; + }; + let repr = EthernetRepr { - src_addr: self.hardware_address, + src_addr: hardware_address, dst_addr: dst, ethertype: proto, }; @@ -104,6 +103,14 @@ impl EthernetLink { } fn process_arp(&mut self, packet: &[u8], now: Instant) { + let Some(hardware_address) = self.hardware_address else { + return; + }; + + let Some(ip_addr) = self.ip_address else { + return; + }; + let Ok(repr) = ArpPacket::new_checked(packet).and_then(|packet| ArpRepr::parse(&packet)) else { debug!("Dropped incomming arp packet on {} (Malformed)", self.name); return; @@ -117,7 +124,7 @@ impl EthernetLink { target_hardware_addr, target_protocol_addr, } => { - if self.hardware_address != target_hardware_addr { + if hardware_address != target_hardware_addr { // Only process packet that are for us return; } @@ -129,8 +136,8 @@ impl EthernetLink { if !source_hardware_addr.is_unicast() || !source_protocol_addr.is_unicast() { return; } - - if !self.ip_address.contains_addr(&target_protocol_addr) { + + if !ip_addr.contains_addr(&target_protocol_addr) { return; } @@ -145,8 +152,8 @@ impl EthernetLink { 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(), + source_hardware_addr: hardware_address, + source_protocol_addr: ip_addr.address(), target_hardware_addr: source_hardware_addr, target_protocol_addr: source_protocol_addr, }; @@ -246,6 +253,14 @@ impl EthernetLink { } fn send_arp(&mut self, now: Instant) { + let Some(hardware_address) = self.hardware_address else { + return; + }; + + let Some(ip_address) = self.ip_address else { + return; + }; + match self.arp_state { ArpState::Discovered => {} ArpState::Discovering { silent_until, .. } if silent_until > now => {} @@ -259,8 +274,8 @@ impl EthernetLink { } => { let arp_repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, - source_hardware_addr: self.hardware_address, - source_protocol_addr: self.ip_address.address(), + source_hardware_addr: hardware_address, + source_protocol_addr: ip_address.address(), target_hardware_addr: EthernetAddress::BROADCAST, target_protocol_addr: target, }; @@ -281,10 +296,9 @@ impl EthernetLink { impl LinkDevice for EthernetLink { fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) { - - let local_broadcast = match self.ip_address.broadcast() { + let local_broadcast = match self.ip_address.and_then(|cidr| cidr.broadcast()) { Some(addr) => IpAddress::Ipv4(addr) == next_hop, - None => false + None => false, }; if local_broadcast || next_hop.is_broadcast() { @@ -317,6 +331,10 @@ impl LinkDevice for EthernetLink { } fn recv(&mut self, now: Instant) -> Option<&[u8]> { + let Some(hardware_address) = self.hardware_address else { + return None; + }; + 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) { @@ -336,7 +354,10 @@ impl LinkDevice for EthernetLink { }; // We let EMPTY_MAC pass because somehow this is the mac used when net=redir is used - if !repr.dst_addr.is_broadcast() && repr.dst_addr != EMPTY_MAC && repr.dst_addr != self.hardware_address { + if !repr.dst_addr.is_broadcast() + && repr.dst_addr != EMPTY_MAC + && repr.dst_addr != hardware_address + { // Drop packets which are not for us continue; } @@ -360,4 +381,21 @@ impl LinkDevice for EthernetLink { // We don't buffer any packets so we can't receive immediatly false } + + fn mac_address(&self) -> Option { + self.hardware_address + } + + fn set_mac_address(&mut self, addr: EthernetAddress) { + self.hardware_address = Some(addr) + } + + fn ip_address(&self) -> Option { + Some(IpCidr::Ipv4(self.ip_address?)) + } + + fn set_ip_address(&mut self, addr: IpCidr) { + let IpCidr::Ipv4(addr) = addr; + self.ip_address = Some(addr); + } } diff --git a/src/smolnetd/link/loopback.rs b/src/smolnetd/link/loopback.rs index 6e9ff4450d..e0cc8c0aff 100644 --- a/src/smolnetd/link/loopback.rs +++ b/src/smolnetd/link/loopback.rs @@ -46,4 +46,21 @@ impl LinkDevice for LoopbackDevice { fn can_recv(&self) -> bool { !self.buffer.is_empty() } + + fn mac_address(&self) -> Option { + None + } + + fn set_mac_address(&mut self, _addr: smoltcp::wire::EthernetAddress) { + } + + fn ip_address(&self) -> Option { + Some("127.0.0.1/8".parse().unwrap()) + } + + fn set_ip_address(&mut self, _addr: smoltcp::wire::IpCidr) { + todo!() + } + + } diff --git a/src/smolnetd/link/mod.rs b/src/smolnetd/link/mod.rs index 8c4a9095a4..bf135a36a3 100644 --- a/src/smolnetd/link/mod.rs +++ b/src/smolnetd/link/mod.rs @@ -4,7 +4,7 @@ pub mod ethernet; use std::rc::Rc; use smoltcp::time::Instant; -use smoltcp::wire::IpAddress; +use smoltcp::wire::{IpAddress, EthernetAddress, IpCidr}; /// Represent a link layer device (eth0, loopback...) pub trait LinkDevice { @@ -24,6 +24,12 @@ pub trait LinkDevice { /// Returns wether this device have packets pending fn can_recv(&self) -> bool; + + fn mac_address(&self) -> Option; + fn set_mac_address(&mut self, addr: EthernetAddress); + + fn ip_address(&self) -> Option; + fn set_ip_address(&mut self, addr: IpCidr); } #[derive(Default)] diff --git a/src/smolnetd/router/mod.rs b/src/smolnetd/router/mod.rs index 40a360ecf0..d639edbe4e 100644 --- a/src/smolnetd/router/mod.rs +++ b/src/smolnetd/router/mod.rs @@ -102,6 +102,7 @@ impl Router { let IpAddress::Ipv4(src) = rule.src; if src != packet.src_addr() { + error!("Changed packet source {} -> {}", packet.src_addr(), src); packet.set_src_addr(src); packet.fill_checksum() } diff --git a/src/smolnetd/router/route_table.rs b/src/smolnetd/router/route_table.rs index e51127720a..51943c932f 100644 --- a/src/smolnetd/router/route_table.rs +++ b/src/smolnetd/router/route_table.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::rc::Rc; use smoltcp::wire::{IpAddress, IpCidr}; @@ -21,6 +22,25 @@ impl Rule { } } +impl Display for Rule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.filter.prefix_len() == 0 { + write!(f, "default")?; + } else { + write!(f, "{} ", self.filter)?; + } + + if let Some(via) = self.via { + write!(f, " via {}", via)?; + } + + write!(f, " dev {}", self.dev)?; + write!(f, " src {}", self.src)?; + + Ok(()) + } +} + #[derive(Debug, Default)] pub struct RouteTable { rules: Vec, @@ -42,6 +62,10 @@ impl RouteTable { self.lookup_rule(dst)?.via } + pub fn lookup_device(&self, dst: &IpAddress) -> Option> { + Some(self.lookup_rule(dst)?.dev.clone()) + } + pub fn insert_rule(&mut self, new_rule: Rule) { let i = match self .rules @@ -52,4 +76,24 @@ impl RouteTable { }; self.rules.insert(i, new_rule); } + + pub fn remove_rule(&mut self, filter: IpCidr) { + self.rules.retain(|rule| rule.filter != filter); + } + + pub fn change_src(&mut self, old_src: IpAddress, new_src: IpAddress) { + for rule in self.rules.iter_mut().filter(|rule| rule.src == old_src) { + rule.src = new_src; + } + } +} + +impl Display for RouteTable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for rule in self.rules.iter() { + writeln!(f, "{}", rule)?; + } + + Ok(()) + } } diff --git a/src/smolnetd/scheme/mod.rs b/src/smolnetd/scheme/mod.rs index f610879f50..1637b51e4e 100644 --- a/src/smolnetd/scheme/mod.rs +++ b/src/smolnetd/scheme/mod.rs @@ -80,10 +80,7 @@ impl Smolnetd { ) -> Smolnetd { 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 = vec![ - IpCidr::new(local_ip, 24), IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), ]; @@ -119,27 +116,12 @@ impl Smolnetd { "127.0.0.1".parse().unwrap(), )); - let IpAddress::Ipv4(local_ip) = local_ip; - let eth0 = EthernetLink::new( + + let mut 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), - )); + eth0.set_mac_address(hardware_addr); devices.borrow_mut().push(loopback); devices.borrow_mut().push(eth0); @@ -150,7 +132,7 @@ impl Smolnetd { route_table: Rc::clone(&route_table), socket_set: Rc::clone(&socket_set), timer: ::std::time::Instant::now(), - link_devices: devices, + link_devices: Rc::clone(&devices), time_file, ip_scheme: IpScheme::new( Rc::clone(&iface), @@ -176,7 +158,12 @@ impl Smolnetd { Rc::clone(&socket_set), icmp_file, ), - netcfg_scheme: NetCfgScheme::new(Rc::clone(&iface), netcfg_file), + netcfg_scheme: NetCfgScheme::new( + Rc::clone(&iface), + netcfg_file, + Rc::clone(&route_table), + Rc::clone(&devices), + ), input_queue, buffer_pool, } @@ -267,7 +254,7 @@ impl Smolnetd { Some(delay) => break ::std::cmp::min(MAX_DURATION, delay), None => break MAX_DURATION, }; - } + } } }; diff --git a/src/smolnetd/scheme/netcfg/mod.rs b/src/smolnetd/scheme/netcfg/mod.rs index 888f4830e9..d95b24a9f9 100644 --- a/src/smolnetd/scheme/netcfg/mod.rs +++ b/src/smolnetd/scheme/netcfg/mod.rs @@ -2,24 +2,30 @@ mod nodes; mod notifier; -use smoltcp::wire::{IpAddress, EthernetAddress, IpCidr, Ipv4Address}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; use std::cell::RefCell; use std::collections::BTreeMap; use std::fs::File; use std::io::{ErrorKind, Read, Write}; -use std::rc::Rc; use std::mem; -use std::str::FromStr; +use std::rc::Rc; use std::str; +use std::str::FromStr; +use syscall; use syscall::data::Stat; use syscall::flag::{MODE_DIR, MODE_FILE}; -use syscall::{Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; -use syscall; +use syscall::{ + Error as SyscallError, EventFlags as SyscallEventFlags, Packet as SyscallPacket, + Result as SyscallResult, SchemeMut, +}; + +use crate::link::DeviceList; +use crate::router::route_table::{self, RouteTable, Rule}; use self::nodes::*; use self::notifier::*; -use redox_netstack::error::{Error, Result}; use super::{post_fevent, Interface}; +use redox_netstack::error::{Error, Result}; const WRITE_BUFFER_MAX_SIZE: usize = 0xffff; @@ -28,27 +34,44 @@ fn gateway_cidr() -> IpCidr { IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0) } -fn parse_default_gw(value: &str) -> SyscallResult { - let mut routes = value.lines(); - if let Some(route) = routes.next() { - if !routes.next().is_none() { - return Err(SyscallError::new(syscall::EINVAL)); - } - let mut words = route.split_whitespace(); - if let Some("default") = words.next() { - if let Some("via") = words.next() { - if let Some(ip) = words.next() { - return Ipv4Address::from_str(ip) - .map_err(|_| SyscallError::new(syscall::EINVAL)); - } - } - } +fn parse_route(value: &str, route_table: &RouteTable) -> SyscallResult { + let mut parts = value.split_whitespace(); + let cidr_str = parts.next().ok_or(SyscallError::new(syscall::EINVAL))?; + let cidr = match cidr_str { + "default" => gateway_cidr(), + cidr_str => cidr_str + .parse() + .map_err(|_| SyscallError::new(syscall::EINVAL))?, + }; + + let via: IpAddress = match parts.next().ok_or(SyscallError::new(syscall::EINVAL))? { + "via" => parts + .next() + .ok_or(SyscallError::new(syscall::EINVAL))? + .parse() + .map_err(|_| SyscallError::new(syscall::EINVAL))?, + _ => return Err(SyscallError::new(syscall::EINVAL)), + }; + + if !via.is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); } - Err(SyscallError::new(syscall::EINVAL)) + + let rule = route_table + .lookup_rule(&via) + .ok_or(SyscallError::new(syscall::EINVAL))?; + + Ok(Rule::new(cidr, Some(via), rule.dev.clone(), rule.src)) } -fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRef) -> CfgNodeRef { - cfg_node!{ +fn mk_root_node( + iface: Interface, + notifier: NotifierRef, + dns_config: DNSConfigRef, + route_table: Rc>, + devices: Rc>, +) -> CfgNodeRef { + cfg_node! { "resolv" => { "nameserver" => { rw [dns_config, notifier] (Option, None) @@ -79,37 +102,24 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe }, "route" => { "list" => { - ro [iface] || { - let mut gateway = None; - iface.borrow_mut().routes_mut().update(|map| { - gateway = map.iter().find(|route| route.cidr == gateway_cidr()).map(|route| route.via_router) - }); - if let Some(ip) = gateway { - format!("default via {}\n", ip) - } else { - String::new() - } + ro [route_table] || { + format!("{}", route_table.borrow()) } }, "add" => { - wo [iface, notifier] (Option, None) + wo [iface, notifier, route_table] (Option, None) |cur_value, line| { if cur_value.is_none() { - let default_gw = parse_default_gw(line)?; - if !default_gw.is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - *cur_value = Some(default_gw); + let route = parse_route(line, &route_table.borrow())?; + *cur_value = Some(route); Ok(()) } else { Err(SyscallError::new(syscall::EINVAL)) } } |cur_value| { - if let Some(default_gw) = *cur_value { - if iface.borrow_mut().routes_mut().add_default_ipv4_route(default_gw).is_err() { - return Err(SyscallError::new(syscall::EINVAL)); - } + if let Some(route) = cur_value.take() { + route_table.borrow_mut().insert_rule(route); notifier.borrow_mut().schedule_notify("route/list"); Ok(()) } else { @@ -118,35 +128,25 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe } }, "rm" => { - wo [iface, notifier] (Option, None) + wo [iface, notifier, route_table] (Option, None) |cur_value, line| { if cur_value.is_none() { - let default_gw = parse_default_gw(line)?; - if !default_gw.is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); + match line.parse() { + Ok(cidr) => { + *cur_value = Some(cidr); + Ok(()) + } + Err(_) => Err(SyscallError::new(syscall::EINVAL)) } - *cur_value = Some(default_gw); - Ok(()) } else { Err(SyscallError::new(syscall::EINVAL)) } } |cur_value| { - if let Some(default_gw) = *cur_value { - let mut iface = iface.borrow_mut(); - let mut gateway = None; - iface.routes_mut().update(|map| { - gateway = map.iter().find(|route| route.cidr == gateway_cidr()).map(|route| route.via_router) - }); - match gateway { - None => Err(SyscallError::new(syscall::EINVAL)), - Some(addr) if addr != IpAddress::Ipv4(default_gw) => Err(SyscallError::new(syscall::EINVAL)), - Some(_) => { - iface.routes_mut().remove_default_ipv4_route(); - notifier.borrow_mut().schedule_notify("route/list"); - Ok(()) - } - } + if let Some(cidr) = *cur_value { + route_table.borrow_mut().remove_rule(cidr); + notifier.borrow_mut().schedule_notify("route/list"); + Ok(()) } else { Err(SyscallError::new(syscall::EINVAL)) } @@ -156,10 +156,17 @@ fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRe "ifaces" => { "eth0" => { "mac" => { - rw [iface, notifier] (Option, None) + rw [iface, notifier, devices] (Option, None) || { - // format!("{}\n", iface.borrow().hardware_addr()) - String::new() + match devices.borrow().get("eth0") { + Some(dev) => { + match dev.mac_address() { + Some(addr) => format!("{addr}\n"), + None => "Not configured\n".into(), + } + } + None => "Device not found\n".into(), + } } |cur_value, line| { if cur_value.is_none() { @@ -176,96 +183,76 @@ 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)); - notifier.borrow_mut().schedule_notify("ifaces/eth0/mac"); + if let Some(dev) = devices.borrow_mut().get_mut("eth0") { + dev.set_mac_address(mac); + notifier.borrow_mut().schedule_notify("ifaces/eth0/mac"); + } } Ok(()) } }, "addr" => { "list" => { - ro [iface] + ro [devices] || { - let mut ips = String::new(); - for cidr in iface.borrow().ip_addrs() { - ips += &format!("{}\n", cidr); - } - ips + let res = match devices.borrow().get("eth0") { + Some(dev) => { + match dev.ip_address() { + Some(addr) => format!("{addr}\n"), + None => "Not configured\n".into(), + } + } + None => "Device not found\n".into(), + }; + res } }, "set" => { - wo [iface, notifier] (Vec, Vec::new()) + wo [iface, notifier, devices, route_table] (Option, None) |cur_value, line| { - let cidr = IpCidr::from_str(line) - .map_err(|_| SyscallError::new(syscall::EINVAL))?; - if !cidr.address().is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - cur_value.push(cidr); - Ok(()) - } - |cur_value| { - if !cur_value.is_empty() { - let mut iface = iface.borrow_mut(); - let mut cidrs = vec![]; - mem::swap(cur_value, &mut cidrs); - iface.update_ip_addrs(|s| { - *s = cidrs.into_iter().collect(); - }); - notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); - } - Ok(()) - } - }, - "add" => { - wo [iface, notifier] (Vec, Vec::new()) - |cur_value, line| { - let cidr = IpCidr::from_str(line) - .map_err(|_| SyscallError::new(syscall::EINVAL))?; - if !cidr.address().is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - cur_value.push(cidr); - Ok(()) - } - |cur_value| { - let mut iface = iface.borrow_mut(); - let mut cidrs = iface.ip_addrs().to_vec(); - for cidr in cur_value { - cidrs.insert(0, *cidr); - } - iface.update_ip_addrs(|s| { - *s = cidrs.into_iter().collect(); - }); - notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); - Ok(()) - } - }, - "rm" => { - wo [iface, notifier] (Vec, Vec::new()) - |cur_value, line| { - let cidr = IpCidr::from_str(line) - .map_err(|_| SyscallError::new(syscall::EINVAL))?; - if !cidr.address().is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - cur_value.push(cidr); - Ok(()) - } - |cur_value| { - let mut iface = iface.borrow_mut(); - let mut cidrs = iface.ip_addrs().to_vec(); - for cidr in cur_value { - let pre_retain_len = cidrs.len(); - cidrs.retain(|&c| c != *cidr); - if pre_retain_len == cidrs.len() { + if cur_value.is_none() { + let cidr = IpCidr::from_str(line) + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !cidr.address().is_unicast() { return Err(SyscallError::new(syscall::EINVAL)); } + *cur_value = Some(cidr); + Ok(()) + } else { + Err(SyscallError::new(syscall::EINVAL)) + } + } + |cur_value| { + // TODO: Multiple IPs + if let Some(cidr) = cur_value.take() { + if let Some(dev) = devices.borrow_mut().get_mut("eth0") { + + let mut route_table = route_table.borrow_mut(); + if let Some(old_addr) = dev.ip_address() { + let IpCidr::Ipv4(old_v4_cidr) = old_addr; + let old_network = IpCidr::Ipv4(old_v4_cidr.network()); + + route_table.remove_rule(old_network); + route_table.change_src(old_addr.address(), cidr.address()); + iface.borrow_mut().update_ip_addrs(|addrs| addrs.retain(|addr| *addr != old_addr)) + } + + dev.set_ip_address(cidr); + // FIXME: Here, the insert 0 is a workaround to let UDP sockets + // work with this interface only. + // Smoltcp takes the first ip address when looking for a source + // ip address when sending UDP packets. + // This behavior will have to be fixed as it's our route table + // job to find give this source. + iface.borrow_mut().update_ip_addrs(|addrs| addrs.insert(0, cidr).unwrap()); + + let IpCidr::Ipv4(v4_cidr) = cidr; + let network_cidr = IpCidr::Ipv4(v4_cidr.network()); + route_table.insert_rule(Rule::new(network_cidr, None, dev.name().clone(), cidr.address())) + } + notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); + notifier.borrow_mut().schedule_notify("route/list"); } - iface.update_ip_addrs(|s| { - *s = cidrs.into_iter().collect(); - }); - notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); Ok(()) } }, @@ -347,7 +334,12 @@ pub struct NetCfgScheme { } impl NetCfgScheme { - pub fn new(iface: Interface, scheme_file: File) -> NetCfgScheme { + pub fn new( + iface: Interface, + scheme_file: File, + route_table: Rc>, + devices: Rc>, + ) -> NetCfgScheme { let notifier = Notifier::new_ref(); let dns_config = Rc::new(RefCell::new(DNSConfig { name_server: Ipv4Address::new(8, 8, 8, 8), @@ -356,7 +348,13 @@ impl NetCfgScheme { scheme_file, next_fd: 1, files: BTreeMap::new(), - root_node: mk_root_node(iface, Rc::clone(¬ifier), dns_config), + root_node: mk_root_node( + iface, + Rc::clone(¬ifier), + dns_config, + route_table, + devices, + ), notifier, } } @@ -368,12 +366,14 @@ impl NetCfgScheme { Ok(0) => { //TODO: Cleanup must occur break Some(()); - }, + } Ok(_) => (), - Err(err) => if err.kind() == ErrorKind::WouldBlock { - break None; - } else { - return Err(Error::from(err)); + Err(err) => { + if err.kind() == ErrorKind::WouldBlock { + break None; + } else { + return Err(Error::from(err)); + } } } self.handle(&mut packet); @@ -416,7 +416,11 @@ impl SchemeMut for NetCfgScheme { is_dir: current_node.is_dir(), is_writable: current_node.is_writable(), is_readable: current_node.is_readable(), - node_writer: if current_node.is_writable() { current_node.new_writer() } else { None }, + node_writer: if current_node.is_writable() { + current_node.new_writer() + } else { + None + }, uid, pos: 0, read_buf, @@ -442,7 +446,8 @@ impl SchemeMut for NetCfgScheme { } fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; @@ -470,7 +475,8 @@ impl SchemeMut for NetCfgScheme { } fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; @@ -484,15 +490,12 @@ impl SchemeMut for NetCfgScheme { } fn fstat(&mut self, fd: usize, stat: &mut Stat) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - stat.st_mode = if file.is_dir { - MODE_DIR - } else { - MODE_FILE - }; + stat.st_mode = if file.is_dir { MODE_DIR } else { MODE_FILE }; if file.is_writable { stat.st_mode |= 0o222; } @@ -507,7 +510,8 @@ impl SchemeMut for NetCfgScheme { } fn fevent(&mut self, fd: usize, events: SyscallEventFlags) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; if events.contains(syscall::EVENT_READ) { @@ -519,7 +523,8 @@ impl SchemeMut for NetCfgScheme { } fn fsync(&mut self, fd: usize) -> SyscallResult { - let file = self.files + let file = self + .files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?;