Add support for TUN interfaces.

This commit is contained in:
Dario Nieuwenhuis 2021-03-24 18:04:42 +01:00
parent b6220a04c8
commit 9e3b373e36
16 changed files with 172 additions and 119 deletions

View File

@ -27,7 +27,7 @@ jobs:
# Test features chosen to be as orthogonal as possible. # Test features chosen to be as orthogonal as possible.
- std medium-ethernet phy-raw_socket proto-ipv6 socket-udp - std medium-ethernet phy-raw_socket proto-ipv6 socket-udp
- std medium-ethernet phy-tap_interface proto-ipv6 socket-udp - std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp
- std medium-ethernet proto-ipv4 proto-igmp socket-raw - std medium-ethernet proto-ipv4 proto-igmp socket-raw
- std medium-ethernet proto-ipv4 socket-udp socket-tcp - std medium-ethernet proto-ipv4 socket-udp socket-tcp
- std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp

View File

@ -34,11 +34,11 @@ alloc = ["managed/alloc"]
verbose = [] verbose = []
"medium-ethernet" = ["socket"] "medium-ethernet" = ["socket"]
"medium-ip" = ["socket"] "medium-ip" = ["socket"]
"phy-raw_socket" = ["std", "libc", "ethernet"] "phy-raw_socket" = ["std", "libc", "medium-ethernet"]
"phy-tap_interface" = ["std", "libc", "ethernet"] "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"]
"proto-ipv4" = [] "proto-ipv4" = []
"proto-igmp" = ["proto-ipv4"] "proto-igmp" = ["proto-ipv4"]
"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "ethernet"] "proto-dhcpv4" = ["proto-ipv4", "socket-raw", "medium-ethernet"]
"proto-ipv6" = [] "proto-ipv6" = []
"socket" = [] "socket" = []
"socket-raw" = ["socket"] "socket-raw" = ["socket"]
@ -49,7 +49,7 @@ verbose = []
default = [ default = [
"std", "log", # needed for `cargo test --no-default-features --features default` :/ "std", "log", # needed for `cargo test --no-default-features --features default` :/
"medium-ethernet", "medium-ip", "medium-ethernet", "medium-ip",
"phy-raw_socket", "phy-tap_interface", "phy-raw_socket", "phy-tuntap_interface",
"proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6",
"socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp",
"async" "async"
@ -69,35 +69,35 @@ required-features = ["std", "phy-raw_socket", "proto-ipv4"]
[[example]] [[example]]
name = "httpclient" name = "httpclient"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"] required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"]
[[example]] [[example]]
name = "ping" name = "ping"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"] required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"]
[[example]] [[example]]
name = "server" name = "server"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"] required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
[[example]] [[example]]
name = "client" name = "client"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"] required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
[[example]] [[example]]
name = "loopback" name = "loopback"
required-features = ["log", "proto-ipv4", "socket-tcp"] required-features = ["log", "medium-ethernet", "proto-ipv4", "socket-tcp"]
[[example]] [[example]]
name = "multicast" name = "multicast"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-igmp", "socket-udp"] required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "socket-udp"]
[[example]] [[example]]
name = "benchmark" name = "benchmark"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-raw", "socket-udp"] required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-raw", "socket-udp"]
[[example]] [[example]]
name = "dhcp_client" name = "dhcp_client"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"] required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"]
[profile.release] [profile.release]
debug = 2 debug = 2

View File

@ -170,9 +170,9 @@ or `BufWriter` is used, which are of course not available on heap-less systems.
This feature is disabled by default. This feature is disabled by default.
### Features `phy-raw_socket` and `phy-tap_interface` ### Features `phy-raw_socket` and `phy-tuntap_interface`
Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TapInterface`, respectively. Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TunTapInterface`, respectively.
These features are enabled by default. These features are enabled by default.

View File

@ -11,7 +11,7 @@ use std::net::TcpStream;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use log::debug; use log::debug;
use smoltcp::phy::wait as phy_wait; use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::iface::{NeighborCache, InterfaceBuilder};
use smoltcp::socket::SocketSet; use smoltcp::socket::SocketSet;
@ -62,12 +62,12 @@ fn main() {
utils::setup_logging("info"); utils::setup_logging("info");
let (mut opts, mut free) = utils::create_options(); let (mut opts, mut free) = utils::create_options();
utils::add_tap_options(&mut opts, &mut free); utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free);
free.push("MODE"); free.push("MODE");
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let mode = match matches.free[0].as_ref() { let mode = match matches.free[0].as_ref() {
@ -90,11 +90,15 @@ fn main() {
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
let mut iface = InterfaceBuilder::new(device) let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs);
if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr) .ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache) .neighbor_cache(neighbor_cache);
.ip_addrs(ip_addrs) }
.finalize(); let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]); let mut sockets = SocketSet::new(vec![]);
let tcp1_handle = sockets.add(tcp1_socket); let tcp1_handle = sockets.add(tcp1_socket);

View File

@ -5,7 +5,7 @@ use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use log::debug; use log::debug;
use smoltcp::phy::wait as phy_wait; use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr}; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
@ -15,13 +15,14 @@ fn main() {
utils::setup_logging(""); utils::setup_logging("");
let (mut opts, mut free) = utils::create_options(); let (mut opts, mut free) = utils::create_options();
utils::add_tap_options(&mut opts, &mut free); utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free);
free.push("ADDRESS"); free.push("ADDRESS");
free.push("PORT"); free.push("PORT");
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
@ -39,12 +40,17 @@ fn main() {
let mut routes_storage = [None; 1]; let mut routes_storage = [None; 1];
let mut routes = Routes::new(&mut routes_storage[..]); let mut routes = Routes::new(&mut routes_storage[..]);
routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv4_route(default_v4_gw).unwrap();
let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr) let medium = device.capabilities().medium;
.neighbor_cache(neighbor_cache) let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs) .ip_addrs(ip_addrs)
.routes(routes) .routes(routes);
.finalize(); if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]); let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket); let tcp_handle = sockets.add(tcp_socket);

View File

@ -3,7 +3,7 @@ mod utils;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use smoltcp::phy::wait as phy_wait; use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr}; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata}; use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata};
@ -15,11 +15,11 @@ fn main() {
utils::setup_logging(""); utils::setup_logging("");
let (mut opts, mut free) = utils::create_options(); let (mut opts, mut free) = utils::create_options();
utils::add_tap_options(&mut opts, &mut free); utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
@ -28,12 +28,17 @@ fn main() {
let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)]; let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)];
let mut routes_storage = [None; 1]; let mut routes_storage = [None; 1];
let routes = Routes::new(&mut routes_storage[..]); let routes = Routes::new(&mut routes_storage[..]);
let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr) let medium = device.capabilities().medium;
.neighbor_cache(neighbor_cache) let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs) .ip_addrs(ip_addrs)
.routes(routes) .routes(routes);
.finalize(); if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]); let mut sockets = SocketSet::new(vec![]);
let dhcp_rx_buffer = RawSocketBuffer::new( let dhcp_rx_buffer = RawSocketBuffer::new(

View File

@ -6,7 +6,7 @@ use std::os::unix::io::AsRawFd;
use url::Url; use url::Url;
use log::debug; use log::debug;
use smoltcp::phy::wait as phy_wait; use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr}; use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
@ -16,13 +16,13 @@ fn main() {
utils::setup_logging(""); utils::setup_logging("");
let (mut opts, mut free) = utils::create_options(); let (mut opts, mut free) = utils::create_options();
utils::add_tap_options(&mut opts, &mut free); utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free);
free.push("ADDRESS"); free.push("ADDRESS");
free.push("URL"); free.push("URL");
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
@ -45,12 +45,17 @@ fn main() {
let mut routes = Routes::new(&mut routes_storage[..]); let mut routes = Routes::new(&mut routes_storage[..]);
routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv4_route(default_v4_gw).unwrap();
routes.add_default_ipv6_route(default_v6_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap();
let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr) let medium = device.capabilities().medium;
.neighbor_cache(neighbor_cache) let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs) .ip_addrs(ip_addrs)
.routes(routes) .routes(routes);
.finalize(); if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]); let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket); let tcp_handle = sockets.add(tcp_socket);

View File

@ -20,11 +20,11 @@ fn main() {
utils::setup_logging("warn"); utils::setup_logging("warn");
let (mut opts, mut free) = utils::create_options(); let (mut opts, mut free) = utils::create_options();
utils::add_tap_options(&mut opts, &mut free); utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, let device = utils::parse_middleware_options(&mut matches,
device, device,

View File

@ -8,7 +8,7 @@ use std::collections::HashMap;
use log::debug; use log::debug;
use byteorder::{ByteOrder, NetworkEndian}; use byteorder::{ByteOrder, NetworkEndian};
use smoltcp::time::{Duration, Instant}; use smoltcp::{phy::Medium, time::{Duration, Instant}};
use smoltcp::phy::Device; use smoltcp::phy::Device;
use smoltcp::phy::wait as phy_wait; use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr,
@ -55,7 +55,7 @@ fn main() {
utils::setup_logging("warn"); utils::setup_logging("warn");
let (mut opts, mut free) = utils::create_options(); let (mut opts, mut free) = utils::create_options();
utils::add_tap_options(&mut opts, &mut free); utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free);
opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT"); opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT");
opts.optopt("i", "interval", opts.optopt("i", "interval",
@ -66,7 +66,7 @@ fn main() {
free.push("ADDRESS"); free.push("ADDRESS");
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device_caps = device.capabilities(); let device_caps = device.capabilities();
@ -98,12 +98,17 @@ fn main() {
let mut routes = Routes::new(&mut routes_storage[..]); let mut routes = Routes::new(&mut routes_storage[..]);
routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv4_route(default_v4_gw).unwrap();
routes.add_default_ipv6_route(default_v6_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap();
let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr) let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs) .ip_addrs(ip_addrs)
.routes(routes) .routes(routes);
.neighbor_cache(neighbor_cache) if medium == Medium::Ethernet {
.finalize(); builder = builder
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]); let mut sockets = SocketSet::new(vec![]);
let icmp_handle = sockets.add(icmp_socket); let icmp_handle = sockets.add(icmp_socket);

View File

@ -6,7 +6,7 @@ use std::fmt::Write;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use log::debug; use log::debug;
use smoltcp::phy::wait as phy_wait; use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::iface::{NeighborCache, InterfaceBuilder};
use smoltcp::socket::SocketSet; use smoltcp::socket::SocketSet;
@ -18,11 +18,11 @@ fn main() {
utils::setup_logging(""); utils::setup_logging("");
let (mut opts, mut free) = utils::create_options(); let (mut opts, mut free) = utils::create_options();
utils::add_tap_options(&mut opts, &mut free); utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
@ -54,11 +54,16 @@ fn main() {
IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64) IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)
]; ];
let mut iface = InterfaceBuilder::new(device)
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs);
if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr) .ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache) .neighbor_cache(neighbor_cache);
.ip_addrs(ip_addrs) }
.finalize(); let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]); let mut sockets = SocketSet::new(vec![]);
let udp_handle = sockets.add(udp_socket); let udp_handle = sockets.add(udp_socket);

View File

@ -14,9 +14,9 @@ use log::{Level, LevelFilter, trace};
use env_logger::Builder; use env_logger::Builder;
use getopts::{Options, Matches}; use getopts::{Options, Matches};
use smoltcp::phy::{Device, EthernetTracer, FaultInjector}; use smoltcp::phy::{Device, EthernetTracer, FaultInjector, Medium};
#[cfg(feature = "phy-tap_interface")] #[cfg(feature = "phy-tuntap_interface")]
use smoltcp::phy::TapInterface; use smoltcp::phy::TunTapInterface;
use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType}; use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType};
use smoltcp::phy::RawSocket; use smoltcp::phy::RawSocket;
use smoltcp::time::{Duration, Instant}; use smoltcp::time::{Duration, Instant};
@ -77,14 +77,20 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches {
} }
} }
pub fn add_tap_options(_opts: &mut Options, free: &mut Vec<&str>) { pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) {
free.push("INTERFACE"); opts.optopt("", "tun", "TUN interface to use", "tun0");
opts.optopt("", "tap", "TAP interface to use", "tap0");
} }
#[cfg(feature = "phy-tap_interface")] #[cfg(feature = "phy-tuntap_interface")]
pub fn parse_tap_options(matches: &mut Matches) -> TapInterface { pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface {
let interface = matches.free.remove(0); let tun = matches.opt_str("tun");
TapInterface::new(&interface).unwrap() let tap = matches.opt_str("tap");
match(tun,tap) {
(Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(),
(None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(),
_ => panic!("You must specify exactly one of --tun or --tap"),
}
} }
pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket { pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket {

View File

@ -8,7 +8,7 @@ and implementations of it:
* _middleware_ [Tracer](struct.Tracer.html) and * _middleware_ [Tracer](struct.Tracer.html) and
[FaultInjector](struct.FaultInjector.html), to facilitate debugging; [FaultInjector](struct.FaultInjector.html), to facilitate debugging;
* _adapters_ [RawSocket](struct.RawSocket.html) and * _adapters_ [RawSocket](struct.RawSocket.html) and
[TapInterface](struct.TapInterface.html), to transmit and receive frames [TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames
on the host OS. on the host OS.
*/ */
#![cfg_attr(feature = "medium-ethernet", doc = r##" #![cfg_attr(feature = "medium-ethernet", doc = r##"
@ -89,7 +89,7 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> {
use crate::Result; use crate::Result;
use crate::time::Instant; use crate::time::Instant;
#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))] #[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))]
mod sys; mod sys;
mod tracer; mod tracer;
@ -100,10 +100,10 @@ mod pcap_writer;
mod loopback; mod loopback;
#[cfg(all(feature = "phy-raw_socket", unix))] #[cfg(all(feature = "phy-raw_socket", unix))]
mod raw_socket; mod raw_socket;
#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] #[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
mod tap_interface; mod tuntap_interface;
#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))] #[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))]
pub use self::sys::wait; pub use self::sys::wait;
pub use self::tracer::Tracer; pub use self::tracer::Tracer;
@ -114,8 +114,9 @@ pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
pub use self::loopback::Loopback; pub use self::loopback::Loopback;
#[cfg(all(feature = "phy-raw_socket", unix))] #[cfg(all(feature = "phy-raw_socket", unix))]
pub use self::raw_socket::RawSocket; pub use self::raw_socket::RawSocket;
#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] #[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
pub use self::tap_interface::TapInterface; pub use self::tuntap_interface::TunTapInterface;
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
/// A tracer device for Ethernet frames. /// A tracer device for Ethernet frames.

View File

@ -1,14 +1,10 @@
#[cfg(any(feature = "phy-raw_socket", #![allow(unused)]
feature = "phy-tap_interface"))]
pub const SIOCGIFMTU: libc::c_ulong = 0x8921; pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
#[cfg(any(feature = "phy-raw_socket"))]
pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
#[cfg(any(feature = "phy-raw_socket"))]
pub const ETH_P_ALL: libc::c_short = 0x0003; pub const ETH_P_ALL: libc::c_short = 0x0003;
#[cfg(feature = "phy-tap_interface")]
pub const TUNSETIFF: libc::c_ulong = 0x400454CA; pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
#[cfg(feature = "phy-tap_interface")] pub const IFF_TUN: libc::c_int = 0x0001;
pub const IFF_TAP: libc::c_int = 0x0002; pub const IFF_TAP: libc::c_int = 0x0002;
#[cfg(feature = "phy-tap_interface")]
pub const IFF_NO_PI: libc::c_int = 0x1000; pub const IFF_NO_PI: libc::c_int = 0x1000;

View File

@ -12,15 +12,15 @@ mod imp;
pub mod raw_socket; pub mod raw_socket;
#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] #[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
pub mod bpf; pub mod bpf;
#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] #[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
pub mod tap_interface; pub mod tuntap_interface;
#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))] #[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))]
pub use self::raw_socket::RawSocketDesc; pub use self::raw_socket::RawSocketDesc;
#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] #[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
pub use self::bpf::BpfDevice as RawSocketDesc; pub use self::bpf::BpfDevice as RawSocketDesc;
#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] #[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
pub use self::tap_interface::TapInterfaceDesc; pub use self::tuntap_interface::TunTapInterfaceDesc;
/// Wait until given file descriptor becomes readable, but no longer than given timeout. /// Wait until given file descriptor becomes readable, but no longer than given timeout.
pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> { pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
@ -60,7 +60,7 @@ pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
} }
} }
#[cfg(all(any(feature = "phy-tap_interface", feature = "phy-raw_socket"), unix))] #[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))]
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
struct ifreq { struct ifreq {
@ -68,7 +68,7 @@ struct ifreq {
ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */ ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
} }
#[cfg(all(any(feature = "phy-tap_interface", feature = "phy-raw_socket"), unix))] #[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))]
fn ifreq_for(name: &str) -> ifreq { fn ifreq_for(name: &str) -> ifreq {
let mut ifreq = ifreq { let mut ifreq = ifreq {
ifr_name: [0; libc::IF_NAMESIZE], ifr_name: [0; libc::IF_NAMESIZE],
@ -81,7 +81,7 @@ fn ifreq_for(name: &str) -> ifreq {
} }
#[cfg(all(any(target_os = "linux", target_os = "android"), #[cfg(all(any(target_os = "linux", target_os = "android"),
any(feature = "phy-tap_interface", feature = "phy-raw_socket")))] any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")))]
fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq, fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq,
cmd: libc::c_ulong) -> io::Result<libc::c_int> { cmd: libc::c_ulong) -> io::Result<libc::c_int> {
unsafe { unsafe {

View File

@ -1,22 +1,23 @@
use std::io; use std::io;
use std::os::unix::io::{RawFd, AsRawFd}; use std::os::unix::io::{RawFd, AsRawFd};
use super::*; use super::*;
use crate::wire::EthernetFrame; use crate::{phy::Medium, wire::EthernetFrame};
#[derive(Debug)] #[derive(Debug)]
pub struct TapInterfaceDesc { pub struct TunTapInterfaceDesc {
lower: libc::c_int, lower: libc::c_int,
ifreq: ifreq ifreq: ifreq,
medium: Medium,
} }
impl AsRawFd for TapInterfaceDesc { impl AsRawFd for TunTapInterfaceDesc {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.lower self.lower
} }
} }
impl TapInterfaceDesc { impl TunTapInterfaceDesc {
pub fn new(name: &str) -> io::Result<TapInterfaceDesc> { pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
let lower = unsafe { let lower = unsafe {
let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char, let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char,
libc::O_RDWR | libc::O_NONBLOCK); libc::O_RDWR | libc::O_NONBLOCK);
@ -24,14 +25,21 @@ impl TapInterfaceDesc {
lower lower
}; };
Ok(TapInterfaceDesc { Ok(TunTapInterfaceDesc {
lower: lower, lower,
ifreq: ifreq_for(name) ifreq: ifreq_for(name),
medium,
}) })
} }
pub fn attach_interface(&mut self) -> io::Result<()> { pub fn attach_interface(&mut self) -> io::Result<()> {
self.ifreq.ifr_data = imp::IFF_TAP | imp::IFF_NO_PI; let mode = match self.medium {
#[cfg(feature = "medium-ip")]
Medium::Ip => imp::IFF_TUN,
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => imp::IFF_TAP,
};
self.ifreq.ifr_data = mode | imp::IFF_NO_PI;
ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ()) ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
} }
@ -46,9 +54,19 @@ impl TapInterfaceDesc {
unsafe { libc::close(lower); } unsafe { libc::close(lower); }
// Propagate error after close, to ensure we always close.
let ip_mtu = ip_mtu?;
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
// smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
Ok(ip_mtu? + EthernetFrame::<&[u8]>::header_len()) let mtu = match self.medium {
#[cfg(feature = "medium-ip")]
Medium::Ip => ip_mtu,
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(),
};
Ok(mtu)
} }
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> { pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
@ -70,7 +88,7 @@ impl TapInterfaceDesc {
} }
} }
impl Drop for TapInterfaceDesc { impl Drop for TunTapInterfaceDesc {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { libc::close(self.lower); } unsafe { libc::close(self.lower); }
} }

View File

@ -8,44 +8,46 @@ use crate::Result;
use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; use crate::phy::{self, sys, DeviceCapabilities, Device, Medium};
use crate::time::Instant; use crate::time::Instant;
/// A virtual Ethernet interface. /// A virtual TUN (IP) or TAP (Ethernet) interface.
#[derive(Debug)] #[derive(Debug)]
pub struct TapInterface { pub struct TunTapInterface {
lower: Rc<RefCell<sys::TapInterfaceDesc>>, lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
mtu: usize mtu: usize,
medium: Medium,
} }
impl AsRawFd for TapInterface { impl AsRawFd for TunTapInterface {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.lower.borrow().as_raw_fd() self.lower.borrow().as_raw_fd()
} }
} }
impl TapInterface { impl TunTapInterface {
/// Attaches to a TAP interface called `name`, or creates it if it does not exist. /// Attaches to a TUN/TAP interface called `name`, or creates it if it does not exist.
/// ///
/// If `name` is a persistent interface configured with UID of the current user, /// If `name` is a persistent interface configured with UID of the current user,
/// no special privileges are needed. Otherwise, this requires superuser privileges /// no special privileges are needed. Otherwise, this requires superuser privileges
/// or a corresponding capability set on the executable. /// or a corresponding capability set on the executable.
pub fn new(name: &str) -> io::Result<TapInterface> { pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterface> {
let mut lower = sys::TapInterfaceDesc::new(name)?; let mut lower = sys::TunTapInterfaceDesc::new(name, medium)?;
lower.attach_interface()?; lower.attach_interface()?;
let mtu = lower.interface_mtu()?; let mtu = lower.interface_mtu()?;
Ok(TapInterface { Ok(TunTapInterface {
lower: Rc::new(RefCell::new(lower)), lower: Rc::new(RefCell::new(lower)),
mtu: mtu mtu,
medium,
}) })
} }
} }
impl<'a> Device<'a> for TapInterface { impl<'a> Device<'a> for TunTapInterface {
type RxToken = RxToken; type RxToken = RxToken;
type TxToken = TxToken; type TxToken = TxToken;
fn capabilities(&self) -> DeviceCapabilities { fn capabilities(&self) -> DeviceCapabilities {
DeviceCapabilities { DeviceCapabilities {
max_transmission_unit: self.mtu, max_transmission_unit: self.mtu,
medium: Medium::Ethernet, medium: self.medium,
..DeviceCapabilities::default() ..DeviceCapabilities::default()
} }
} }
@ -89,7 +91,7 @@ impl phy::RxToken for RxToken {
#[doc(hidden)] #[doc(hidden)]
pub struct TxToken { pub struct TxToken {
lower: Rc<RefCell<sys::TapInterfaceDesc>>, lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
} }
impl phy::TxToken for TxToken { impl phy::TxToken for TxToken {