Skip to content

Commit

Permalink
Add support for arbitrarily many routes instead of only gateways.
Browse files Browse the repository at this point in the history
  • Loading branch information
progval committed May 11, 2018
1 parent 52c6c2d commit 10c34f9
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 132 deletions.
5 changes: 3 additions & 2 deletions examples/client.rs
Expand Up @@ -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, Route, Routes};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::Instant;

Expand Down Expand Up @@ -40,11 +40,12 @@ 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 routes = [Route::new_ipv4_gateway(default_v4_gw)];
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::new(routes))
.finalize();

let mut sockets = SocketSet::new(vec![]);
Expand Down
7 changes: 4 additions & 3 deletions examples/httpclient.rs
Expand Up @@ -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, Route, Routes};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::Instant;

Expand Down Expand Up @@ -47,12 +47,13 @@ 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 routes = [Route::new_ipv4_gateway(default_v4_gw),
Route::new_ipv6_gateway(default_v6_gw)];
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::new(routes))
.finalize();

let mut sockets = SocketSet::new(vec![]);
Expand Down
7 changes: 4 additions & 3 deletions examples/ping.rs
Expand Up @@ -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, Route, Routes};
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint};
use std::collections::HashMap;
use byteorder::{ByteOrder, NetworkEndian};
Expand Down Expand Up @@ -99,11 +99,12 @@ 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 routes = [Route::new_ipv4_gateway(default_v4_gw),
Route::new_ipv6_gateway(default_v6_gw)];
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::new(routes))
.neighbor_cache(neighbor_cache)
.finalize();

Expand Down
156 changes: 34 additions & 122 deletions src/iface/ethernet.rs
Expand Up @@ -13,7 +13,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")]
Expand Down Expand Up @@ -41,15 +41,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<T>` 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.
Expand All @@ -59,31 +60,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<Ipv4Address>,
#[cfg(feature = "proto-ipv6")]
ipv6_gateway: Option<Ipv6Address>,
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<EthernetAddress>,
neighbor_cache: Option<NeighborCache<'b>>,
ip_addrs: ManagedSlice<'c, IpCidr>,
#[cfg(feature = "proto-ipv4")]
ipv4_gateway: Option<Ipv4Address>,
#[cfg(feature = "proto-ipv6")]
ipv6_gateway: Option<Ipv6Address>,
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.
Expand All @@ -110,16 +105,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(ManagedSlice::Borrowed(&mut [])),
}
}

Expand All @@ -130,7 +122,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
Expand All @@ -143,7 +135,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<T>(mut self, ip_addrs: T) -> InterfaceBuilder<'b, 'c, DeviceT>
pub fn ip_addrs<T>(mut self, ip_addrs: T) -> InterfaceBuilder<'b, 'c, 'e, DeviceT>
where T: Into<ManagedSlice<'c, IpCidr>>
{
let ip_addrs = ip_addrs.into();
Expand All @@ -152,43 +144,23 @@ 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<T>(mut self, gateway: T) -> InterfaceBuilder<'b, 'c, DeviceT>
where T: Into<Ipv4Address>
{
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.
/// This function panics if any of the addresses are not unicast.
///
/// [ipv6_gateway]: struct.EthernetInterface.html#method.ipv6_gateway
#[cfg(feature = "proto-ipv6")]
pub fn ipv6_gateway<T>(mut self, gateway: T) -> InterfaceBuilder<'b, 'c, DeviceT>
where T: Into<Ipv6Address>
/// [routes]: struct.EthernetInterface.html#method.routes
pub fn routes<T>(mut self, routes: T) -> InterfaceBuilder<'b, 'c, 'e, DeviceT>
where T: Into<Routes<'e>>
{
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
}
Expand All @@ -204,7 +176,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();
Expand All @@ -213,10 +185,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,
}
}
},
Expand Down Expand Up @@ -275,7 +244,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 {
Expand Down Expand Up @@ -310,40 +279,6 @@ 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<Ipv4Address> {
self.inner.ipv4_gateway
}

/// 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<GatewayAddrT>(&mut self, gateway: GatewayAddrT)
where GatewayAddrT: Into<Option<Ipv4Address>> {
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<Ipv6Address> {
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<GatewayAddrT>(&mut self, gateway: GatewayAddrT)
where GatewayAddrT: Into<Option<Ipv6Address>> {
self.inner.ipv6_gateway = gateway.into();
self.inner.ipv6_gateway.map(|addr| InterfaceInner::check_ipv6_gateway_addr(&addr));
}

/// Transmit packets queued in the given sockets, and receive packets queued
/// in the device.
///
Expand Down Expand Up @@ -516,7 +451,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)
Expand All @@ -531,20 +466,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.
Expand Down Expand Up @@ -1182,30 +1103,21 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
.is_some()
}

fn route(&self, addr: &IpAddress) -> Result<IpAddress> {
fn route(&self, addr: &IpAddress, timestamp: Instant) -> Result<IpAddress> {
// 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)
Expand Down Expand Up @@ -1253,7 +1165,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) =>
Expand Down Expand Up @@ -1377,8 +1289,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 = [
Expand Down
2 changes: 2 additions & 0 deletions src/iface/mod.rs
Expand Up @@ -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};

0 comments on commit 10c34f9

Please sign in to comment.