diff --git a/Cargo.toml b/Cargo.toml index e1e829f..caa7b6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "0BSD" autoexamples = false [dependencies] -managed = { version = "0.5", default-features = false, features = ["map"] } +managed = { version = "0.7", default-features = false, features = ["map"] } byteorder = { version = "1.0", default-features = false } log = { version = "0.3", default-features = false, optional = true } libc = { version = "0.2.18", optional = true } diff --git a/examples/client.rs b/examples/client.rs index 0d84efe..5c19ae7 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -11,7 +11,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; @@ -40,11 +40,14 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 2), 24)]; let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); + let mut routes_storage = [None; 1]; + let mut routes = Routes::new(&mut routes_storage[..]); + routes.add_default_ipv4_route(default_v4_gw).unwrap(); let mut iface = EthernetInterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) - .ipv4_gateway(default_v4_gw) + .routes(routes) .finalize(); let mut sockets = SocketSet::new(vec![]); diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 4828f8e..b007d44 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -14,7 +14,7 @@ use std::os::unix::io::AsRawFd; use url::Url; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; @@ -47,12 +47,15 @@ fn main() { IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)]; let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); + let mut routes_storage = [None; 2]; + let mut routes = Routes::new(&mut routes_storage[..]); + routes.add_default_ipv4_route(default_v4_gw).unwrap(); + routes.add_default_ipv6_route(default_v6_gw).unwrap(); let mut iface = EthernetInterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) - .ipv4_gateway(default_v4_gw) - .ipv6_gateway(default_v6_gw) + .routes(routes) .finalize(); let mut sockets = SocketSet::new(vec![]); diff --git a/examples/ping.rs b/examples/ping.rs index 91765ba..8f76bf9 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -17,7 +17,7 @@ use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv6Address, Icmpv6Repr, Icmpv6Packet, Ipv4Address, Icmpv4Repr, Icmpv4Packet}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint}; use std::collections::HashMap; use byteorder::{ByteOrder, NetworkEndian}; @@ -99,11 +99,14 @@ fn main() { IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)]; let default_v4_gw = Ipv4Address::new(192, 168, 69, 100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); + let mut routes_storage = [None; 2]; + let mut routes = Routes::new(&mut routes_storage[..]); + routes.add_default_ipv4_route(default_v4_gw).unwrap(); + routes.add_default_ipv6_route(default_v6_gw).unwrap(); let mut iface = EthernetInterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .ip_addrs(ip_addrs) - .ipv4_gateway(default_v4_gw) - .ipv6_gateway(default_v6_gw) + .routes(routes) .neighbor_cache(neighbor_cache) .finalize(); diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index d1bfb1c..e44abb4 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -3,7 +3,7 @@ // and RFCs 8200 and 4861 for any IPv6 and NDISC work. use core::cmp; -use managed::ManagedSlice; +use managed::{ManagedSlice, ManagedMap}; use {Error, Result}; use phy::{Device, DeviceCapabilities, RxToken, TxToken}; @@ -14,7 +14,7 @@ use wire::{IpAddress, IpProtocol, IpRepr, IpCidr}; #[cfg(feature = "proto-ipv6")] use wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, IPV6_MIN_MTU}; #[cfg(feature = "proto-ipv4")] -use wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, IPV4_MIN_MTU}; +use wire::{Ipv4Packet, Ipv4Repr, IPV4_MIN_MTU}; #[cfg(feature = "proto-ipv4")] use wire::{ArpPacket, ArpRepr, ArpOperation}; #[cfg(feature = "proto-ipv4")] @@ -46,15 +46,16 @@ use socket::UdpSocket; #[cfg(feature = "socket-tcp")] use socket::TcpSocket; use super::{NeighborCache, NeighborAnswer}; +use super::Routes; /// An Ethernet network interface. /// /// The network interface logically owns a number of other data structures; to avoid /// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be /// a `&mut [T]`, or `Vec` if a heap is available. -pub struct Interface<'b, 'c, DeviceT: for<'d> Device<'d>> { +pub struct Interface<'b, 'c, 'e, DeviceT: for<'d> Device<'d>> { device: DeviceT, - inner: InterfaceInner<'b, 'c>, + inner: InterfaceInner<'b, 'c, 'e>, } /// The device independent part of an Ethernet network interface. @@ -64,31 +65,25 @@ pub struct Interface<'b, 'c, DeviceT: for<'d> Device<'d>> { /// the `device` mutably until they're used, which makes it impossible to call other /// methods on the `Interface` in this time (since its `device` field is borrowed /// exclusively). However, it is still possible to call methods on its `inner` field. -struct InterfaceInner<'b, 'c> { +struct InterfaceInner<'b, 'c, 'e> { neighbor_cache: NeighborCache<'b>, ethernet_addr: EthernetAddress, ip_addrs: ManagedSlice<'c, IpCidr>, - #[cfg(feature = "proto-ipv4")] - ipv4_gateway: Option, - #[cfg(feature = "proto-ipv6")] - ipv6_gateway: Option, + routes: Routes<'e>, device_capabilities: DeviceCapabilities, } /// A builder structure used for creating a Ethernet network /// interface. -pub struct InterfaceBuilder <'b, 'c, DeviceT: for<'d> Device<'d>> { +pub struct InterfaceBuilder <'b, 'c, 'e, DeviceT: for<'d> Device<'d>> { device: DeviceT, ethernet_addr: Option, neighbor_cache: Option>, ip_addrs: ManagedSlice<'c, IpCidr>, - #[cfg(feature = "proto-ipv4")] - ipv4_gateway: Option, - #[cfg(feature = "proto-ipv6")] - ipv6_gateway: Option, + routes: Routes<'e>, } -impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT> +impl<'b, 'c, 'e, DeviceT> InterfaceBuilder<'b, 'c, 'e, DeviceT> where DeviceT: for<'d> Device<'d> { /// Create a builder used for creating a network interface using the /// given device and address. @@ -115,16 +110,13 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT> /// .ip_addrs(ip_addrs) /// .finalize(); /// ``` - pub fn new(device: DeviceT) -> InterfaceBuilder<'b, 'c, DeviceT> { + pub fn new(device: DeviceT) -> InterfaceBuilder<'b, 'c, 'e, DeviceT> { InterfaceBuilder { device: device, ethernet_addr: None, neighbor_cache: None, ip_addrs: ManagedSlice::Borrowed(&mut []), - #[cfg(feature = "proto-ipv4")] - ipv4_gateway: None, - #[cfg(feature = "proto-ipv6")] - ipv6_gateway: None, + routes: Routes::new(ManagedMap::Borrowed(&mut [])), } } @@ -135,7 +127,7 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT> /// This function panics if the address is not unicast. /// /// [ethernet_addr]: struct.EthernetInterface.html#method.ethernet_addr - pub fn ethernet_addr(mut self, addr: EthernetAddress) -> InterfaceBuilder<'b, 'c, DeviceT> { + pub fn ethernet_addr(mut self, addr: EthernetAddress) -> InterfaceBuilder<'b, 'c, 'e, DeviceT> { InterfaceInner::check_ethernet_addr(&addr); self.ethernet_addr = Some(addr); self @@ -148,7 +140,7 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT> /// This function panics if any of the addresses are not unicast. /// /// [ip_addrs]: struct.EthernetInterface.html#method.ip_addrs - pub fn ip_addrs(mut self, ip_addrs: T) -> InterfaceBuilder<'b, 'c, DeviceT> + pub fn ip_addrs(mut self, ip_addrs: T) -> InterfaceBuilder<'b, 'c, 'e, DeviceT> where T: Into> { let ip_addrs = ip_addrs.into(); @@ -157,43 +149,20 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT> self } - /// Set the IPv4 gateway the interface will use. See also - /// [ipv4_gateway]. + /// Set the IP routes the interface will use. See also + /// [routes]. /// - /// # Panics - /// This function panics if the given address is not unicast. - /// - /// [ipv4_gateway]: struct.EthernetInterface.html#method.ipv4_gateway - #[cfg(feature = "proto-ipv4")] - pub fn ipv4_gateway(mut self, gateway: T) -> InterfaceBuilder<'b, 'c, DeviceT> - where T: Into + /// [routes]: struct.EthernetInterface.html#method.routes + pub fn routes(mut self, routes: T) -> InterfaceBuilder<'b, 'c, 'e, DeviceT> + where T: Into> { - let addr = gateway.into(); - InterfaceInner::check_ipv4_gateway_addr(&addr); - self.ipv4_gateway = Some(addr); - self - } - - /// Set the IPv6 gateway the interface will use. See also - /// [ipv6_gateway]. - /// - /// # Panics - /// This function panics if the given address is not unicast. - /// - /// [ipv6_gateway]: struct.EthernetInterface.html#method.ipv6_gateway - #[cfg(feature = "proto-ipv6")] - pub fn ipv6_gateway(mut self, gateway: T) -> InterfaceBuilder<'b, 'c, DeviceT> - where T: Into - { - let addr = gateway.into(); - InterfaceInner::check_ipv6_gateway_addr(&addr); - self.ipv6_gateway = Some(addr); + self.routes = routes.into(); self } /// Set the Neighbor Cache the interface will use. pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'b>) -> - InterfaceBuilder<'b, 'c, DeviceT> { + InterfaceBuilder<'b, 'c, 'e, DeviceT> { self.neighbor_cache = Some(neighbor_cache); self } @@ -209,7 +178,7 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT> /// /// [ethernet_addr]: #method.ethernet_addr /// [neighbor_cache]: #method.neighbor_cache - pub fn finalize(self) -> Interface<'b, 'c, DeviceT> { + pub fn finalize(self) -> Interface<'b, 'c, 'e, DeviceT> { match (self.ethernet_addr, self.neighbor_cache) { (Some(ethernet_addr), Some(neighbor_cache)) => { let device_capabilities = self.device.capabilities(); @@ -218,10 +187,7 @@ impl<'b, 'c, DeviceT> InterfaceBuilder<'b, 'c, DeviceT> inner: InterfaceInner { ethernet_addr, device_capabilities, neighbor_cache, ip_addrs: self.ip_addrs, - #[cfg(feature = "proto-ipv4")] - ipv4_gateway: self.ipv4_gateway, - #[cfg(feature = "proto-ipv6")] - ipv6_gateway: self.ipv6_gateway, + routes: self.routes, } } }, @@ -280,7 +246,7 @@ fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize { cmp::min(len, mtu - header_len * 2 - 8) } -impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT> +impl<'b, 'c, 'e, DeviceT> Interface<'b, 'c, 'e, DeviceT> where DeviceT: for<'d> Device<'d> { /// Get the Ethernet address of the interface. pub fn ethernet_addr(&self) -> EthernetAddress { @@ -315,38 +281,12 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT> self.inner.has_ip_addr(addr) } - /// Get the IPv4 gateway of the interface. - #[cfg(feature = "proto-ipv4")] - pub fn ipv4_gateway(&self) -> Option { - self.inner.ipv4_gateway + pub fn routes(&self) -> &'e Routes { + &self.inner.routes } - /// Set the IPv4 gateway of the interface. - /// - /// # Panics - /// This function panics if the given address is not unicast. - #[cfg(feature = "proto-ipv4")] - pub fn set_ipv4_gateway(&mut self, gateway: GatewayAddrT) - where GatewayAddrT: Into> { - self.inner.ipv4_gateway = gateway.into(); - self.inner.ipv4_gateway.map(|addr| InterfaceInner::check_ipv4_gateway_addr(&addr)); - } - - /// Get the IPv6 gateway of the interface. - #[cfg(feature = "proto-ipv6")] - pub fn ipv6_gateway(&self) -> Option { - self.inner.ipv6_gateway - } - - /// Set the IPv6 gateway of the interface. - /// - /// # Panics - /// This function panics if the given address is not unicast. - #[cfg(feature = "proto-ipv6")] - pub fn set_ipv6_gateway(&mut self, gateway: GatewayAddrT) - where GatewayAddrT: Into> { - self.inner.ipv6_gateway = gateway.into(); - self.inner.ipv6_gateway.map(|addr| InterfaceInner::check_ipv6_gateway_addr(&addr)); + pub fn routes_mut(&mut self) -> &'e mut Routes { + &mut self.inner.routes } /// Transmit packets queued in the given sockets, and receive packets queued @@ -525,7 +465,7 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT> } } -impl<'b, 'c> InterfaceInner<'b, 'c> { +impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { fn check_ethernet_addr(addr: &EthernetAddress) { if addr.is_multicast() { panic!("Ethernet address {} is not unicast", addr) @@ -540,20 +480,6 @@ impl<'b, 'c> InterfaceInner<'b, 'c> { } } - #[cfg(feature = "proto-ipv4")] - fn check_ipv4_gateway_addr(addr: &Ipv4Address) { - if !addr.is_unicast() { - panic!("gateway IP address {} is not unicast", addr); - } - } - - #[cfg(feature = "proto-ipv6")] - fn check_ipv6_gateway_addr(addr: &Ipv6Address) { - if !addr.is_unicast() { - panic!("gateway IP address {} is not unicast", addr); - } - } - /// Determine if the given `Ipv6Address` is the solicited node /// multicast address for a IPv6 addresses assigned to the interface. /// See [RFC 4291 ยง 2.7.1] for more details. @@ -1238,30 +1164,21 @@ impl<'b, 'c> InterfaceInner<'b, 'c> { .is_some() } - fn route(&self, addr: &IpAddress) -> Result { + fn route(&self, addr: &IpAddress, timestamp: Instant) -> Result { // Send directly. if self.in_same_network(addr) || addr.is_broadcast() { return Ok(addr.clone()) } - // Route via a gateway. - match addr { - #[cfg(feature = "proto-ipv4")] - &IpAddress::Ipv4(_) => match self.ipv4_gateway { - Some(gateway) => Ok(gateway.into()), - None => Err(Error::Unaddressable), - } - #[cfg(feature = "proto-ipv6")] - &IpAddress::Ipv6(_) => match self.ipv6_gateway { - Some(gateway) => Ok(gateway.into()), - None => Err(Error::Unaddressable), - } - _ => Err(Error::Unaddressable) + // Route via a router. + match self.routes.lookup(addr, timestamp) { + Some(router_addr) => Ok(router_addr), + None => Err(Error::Unaddressable), } } fn has_neighbor<'a>(&self, addr: &'a IpAddress, timestamp: Instant) -> bool { - match self.route(addr) { + match self.route(addr, timestamp) { Ok(routed_addr) => { self.neighbor_cache .lookup_pure(&routed_addr, timestamp) @@ -1309,7 +1226,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> { } } - let dst_addr = self.route(dst_addr)?; + let dst_addr = self.route(dst_addr, timestamp)?; match self.neighbor_cache.lookup(&dst_addr, timestamp) { NeighborAnswer::Found(hardware_addr) => @@ -1435,8 +1352,8 @@ mod test { use super::Packet; - fn create_loopback<'a, 'b>() -> (EthernetInterface<'static, 'b, Loopback>, - SocketSet<'static, 'a, 'b>) { + fn create_loopback<'a, 'b, 'c>() -> (EthernetInterface<'static, 'b, 'c, Loopback>, + SocketSet<'static, 'a, 'b>) { // Create a basic device let device = Loopback::new(); let ip_addrs = [ diff --git a/src/iface/mod.rs b/src/iface/mod.rs index 6428788..09ece82 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -5,10 +5,12 @@ provides lookup and caching of hardware addresses, and handles management packet */ mod neighbor; +mod route; mod ethernet; pub use self::neighbor::Neighbor as Neighbor; pub(crate) use self::neighbor::Answer as NeighborAnswer; pub use self::neighbor::Cache as NeighborCache; +pub use self::route::{Route, Routes}; pub use self::ethernet::{Interface as EthernetInterface, InterfaceBuilder as EthernetInterfaceBuilder}; diff --git a/src/iface/route.rs b/src/iface/route.rs new file mode 100644 index 0000000..d684f18 --- /dev/null +++ b/src/iface/route.rs @@ -0,0 +1,229 @@ +use managed::ManagedMap; +use time::Instant; +use core::ops::Bound; + +use {Error, Result}; +use wire::{IpCidr, IpAddress}; +#[cfg(feature = "proto-ipv4")] +use wire::{Ipv4Address, Ipv4Cidr}; +#[cfg(feature = "proto-ipv6")] +use wire::{Ipv6Address, Ipv6Cidr}; + +/// A prefix of addresses that should be routed via a router +#[derive(Debug, Clone, Copy)] +pub struct Route { + pub via_router: IpAddress, + /// `None` means "forever". + pub preferred_until: Option, + /// `None` means "forever". + pub expires_at: Option, +} + +impl Route { + /// Returns a route to 0.0.0.0/0 via the `gateway`, with no expiry. + #[cfg(feature = "proto-ipv4")] + pub fn new_ipv4_gateway(gateway: Ipv4Address) -> Route { + Route { + via_router: gateway.into(), + preferred_until: None, + expires_at: None, + } + } + + /// Returns a route to ::/0 via the `gateway`, with no expiry. + #[cfg(feature = "proto-ipv6")] + pub fn new_ipv6_gateway(gateway: Ipv6Address) -> Route { + Route { + via_router: gateway.into(), + preferred_until: None, + expires_at: None, + } + } +} + +/// A routing table. +/// +/// # Examples +/// +/// On systems with heap, this table can be created with: +/// +/// ```rust +/// use std::collections::BTreeMap; +/// use smoltcp::iface::Routes; +/// let mut routes = Routes::new(BTreeMap::new()); +/// ``` +/// +/// On systems without heap, use: +/// +/// ```rust +/// use smoltcp::iface::Routes; +/// let mut routes_storage = []; +/// let mut routes = Routes::new(&mut routes_storage[..]); +/// ``` +#[derive(Debug)] +pub struct Routes<'a> { + storage: ManagedMap<'a, IpCidr, Route>, +} + +impl<'a> Routes<'a> { + /// Creates a routing tables. The backing storage is **not** cleared + /// upon creation. + pub fn new(storage: T) -> Routes<'a> + where T: Into> { + let storage = storage.into(); + Routes { storage } + } + + /// Update the routes of this node. + pub fn update)>(&mut self, f: F) { + f(&mut self.storage); + } + + /// Add a default ipv4 gateway (ie. "ip route add 0.0.0.0/0 via `gateway`"). + /// + /// On success, returns the previous default route, if any. + #[cfg(feature = "proto-ipv4")] + pub fn add_default_ipv4_route(&mut self, gateway: Ipv4Address) -> Result> { + let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0); + let route = Route::new_ipv4_gateway(gateway); + match self.storage.insert(cidr, route) { + Ok(route) => Ok(route), + Err((_cidr, _route)) => Err(Error::Exhausted) + } + } + + /// Add a default ipv6 gateway (ie. "ip -6 route add ::/0 via `gateway`"). + /// + /// On success, returns the previous default route, if any. + #[cfg(feature = "proto-ipv6")] + pub fn add_default_ipv6_route(&mut self, gateway: Ipv6Address) -> Result> { + let cidr = IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0), 0); + let route = Route::new_ipv6_gateway(gateway); + match self.storage.insert(cidr, route) { + Ok(route) => Ok(route), + Err((_cidr, _route)) => Err(Error::Exhausted) + } + } + + pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> + Option { + assert!(addr.is_unicast()); + + let cidr = match addr { + #[cfg(feature = "proto-ipv4")] + IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)), + #[cfg(feature = "proto-ipv6")] + IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)), + _ => unimplemented!() + }; + + for (prefix, route) in self.storage.range((Bound::Unbounded, Bound::Included(cidr))).rev() { + // TODO: do something with route.preferred_until + if let Some(expires_at) = route.expires_at { + if timestamp > expires_at { + continue; + } + } + + if prefix.contains_addr(addr) { + return Some(route.via_router); + } + } + + None + } +} + +#[cfg(test)] +mod test { + use super::*; + #[cfg(feature = "proto-ipv6")] + mod mock { + use super::super::*; + pub const ADDR_1A: Ipv6Address = Ipv6Address( + [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]); + pub const ADDR_1B: Ipv6Address = Ipv6Address( + [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]); + pub const ADDR_1C: Ipv6Address = Ipv6Address( + [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]); + pub fn cidr_1() -> Ipv6Cidr { + Ipv6Cidr::new(Ipv6Address( + [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]), 64) + } + + pub const ADDR_2A: Ipv6Address = Ipv6Address( + [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]); + pub const ADDR_2B: Ipv6Address = Ipv6Address( + [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]); + pub fn cidr_2() -> Ipv6Cidr { + Ipv6Cidr::new(Ipv6Address( + [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]), 64) + } + } + + #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] + mod mock { + use super::super::*; + pub const ADDR_1A: Ipv4Address = Ipv4Address([192, 0, 2, 1]); + pub const ADDR_1B: Ipv4Address = Ipv4Address([192, 0, 2, 13]); + pub const ADDR_1C: Ipv4Address = Ipv4Address([192, 0, 2, 42]); + pub fn cidr_1() -> Ipv4Cidr { + Ipv4Cidr::new(Ipv4Address([192, 0, 2, 0]), 24) + } + + pub const ADDR_2A: Ipv4Address = Ipv4Address([198, 51, 100, 1]); + pub const ADDR_2B: Ipv4Address = Ipv4Address([198, 51, 100, 21]); + pub fn cidr_2() -> Ipv4Cidr { + Ipv4Cidr::new(Ipv4Address([198, 51, 100, 0]), 24) + } + } + + use self::mock::*; + + #[test] + fn test_fill() { + let mut routes_storage = [None, None, None]; + let mut routes = Routes::new(&mut routes_storage[..]); + + assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), None); + assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), None); + assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), None); + assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None); + assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None); + + let route = Route { + via_router: ADDR_1A.into(), + preferred_until: None, expires_at: None, + }; + routes.update(|storage| { + storage.insert(cidr_1().into(), route).unwrap(); + }); + + assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None); + assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None); + + let route2 = Route { + via_router: ADDR_2A.into(), + preferred_until: Some(Instant::from_millis(10)), + expires_at: Some(Instant::from_millis(10)), + }; + routes.update(|storage| { + storage.insert(cidr_2().into(), route2).unwrap(); + }); + + assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), Some(ADDR_2A.into())); + assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), Some(ADDR_2A.into())); + + assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)), Some(ADDR_1A.into())); + assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)), Some(ADDR_2A.into())); + assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)), Some(ADDR_2A.into())); + } +} diff --git a/src/wire/ip.rs b/src/wire/ip.rs index 00d3b31..e62549b 100644 --- a/src/wire/ip.rs +++ b/src/wire/ip.rs @@ -325,6 +325,20 @@ impl Cidr { } } +#[cfg(feature = "proto-ipv4")] +impl From for Cidr { + fn from(addr: Ipv4Cidr) -> Self { + Cidr::Ipv4(addr) + } +} + +#[cfg(feature = "proto-ipv6")] +impl From for Cidr { + fn from(addr: Ipv6Cidr) -> Self { + Cidr::Ipv6(addr) + } +} + impl fmt::Display for Cidr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { diff --git a/src/wire/ndisc.rs b/src/wire/ndisc.rs index fc818a2..a940390 100644 --- a/src/wire/ndisc.rs +++ b/src/wire/ndisc.rs @@ -243,7 +243,7 @@ impl<'a> Repr<'a> { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), - _ => None, + _ => { return Err(Error::Unrecognized); } } } else { None @@ -260,7 +260,7 @@ impl<'a> Repr<'a> { NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), NdiscOptionRepr::Mtu(val) => mtu = Some(val), NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info), - _ => () + _ => { return Err(Error::Unrecognized); } } offset += opt.buffer_len(); } @@ -278,7 +278,7 @@ impl<'a> Repr<'a> { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), - _ => None, + _ => { return Err(Error::Unrecognized); } } } else { None @@ -292,7 +292,7 @@ impl<'a> Repr<'a> { let opt = NdiscOption::new_checked(packet.payload())?; match opt.option_type() { NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()), - _ => None, + _ => { return Err(Error::Unrecognized); } } } else { None @@ -326,7 +326,7 @@ impl<'a> Repr<'a> { offset += 8 + ip_repr.buffer_len() + data.len(); } } - _ => () + _ => { return Err(Error::Unrecognized); } } } Ok(Repr::Redirect {