diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index adf2bd1..e1f45ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: # Test features chosen to be as orthogonal as possible. - 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 socket-udp socket-tcp - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp diff --git a/Cargo.toml b/Cargo.toml index a5b9833..dfa9917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,11 +34,11 @@ alloc = ["managed/alloc"] verbose = [] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] -"phy-raw_socket" = ["std", "libc", "ethernet"] -"phy-tap_interface" = ["std", "libc", "ethernet"] +"phy-raw_socket" = ["std", "libc", "medium-ethernet"] +"phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] "proto-ipv4" = [] "proto-igmp" = ["proto-ipv4"] -"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "ethernet"] +"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "medium-ethernet"] "proto-ipv6" = [] "socket" = [] "socket-raw" = ["socket"] @@ -49,7 +49,7 @@ verbose = [] default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "medium-ethernet", "medium-ip", - "phy-raw_socket", "phy-tap_interface", + "phy-raw_socket", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "async" @@ -69,35 +69,35 @@ required-features = ["std", "phy-raw_socket", "proto-ipv4"] [[example]] 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]] 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]] 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]] 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]] name = "loopback" -required-features = ["log", "proto-ipv4", "socket-tcp"] +required-features = ["log", "medium-ethernet", "proto-ipv4", "socket-tcp"] [[example]] 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]] 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]] 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] debug = 2 diff --git a/README.md b/README.md index 331f7aa..6098989 100644 --- a/README.md +++ b/README.md @@ -170,9 +170,9 @@ or `BufWriter` is used, which are of course not available on heap-less systems. 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. diff --git a/examples/benchmark.rs b/examples/benchmark.rs index 9ce2f57..6527ba4 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -11,7 +11,7 @@ use std::net::TcpStream; use std::os::unix::io::AsRawFd; 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::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::SocketSet; @@ -62,12 +62,12 @@ fn main() { utils::setup_logging("info"); 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); free.push("MODE"); 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 device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); 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 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) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs) - .finalize(); + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); let tcp1_handle = sockets.add(tcp1_socket); diff --git a/examples/client.rs b/examples/client.rs index ecce1c7..308ec99 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; 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::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; @@ -15,13 +15,14 @@ fn main() { utils::setup_logging(""); 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); free.push("ADDRESS"); free.push("PORT"); 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 device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); 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 = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); - let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .routes(routes) - .finalize(); + .routes(routes); + 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 tcp_handle = sockets.add(tcp_socket); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 6e9a670..81d685f 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -3,7 +3,7 @@ mod utils; use std::collections::BTreeMap; 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::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata}; @@ -15,11 +15,11 @@ fn main() { utils::setup_logging(""); 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); 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 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 mut routes_storage = [None; 1]; let routes = Routes::new(&mut routes_storage[..]); - let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .routes(routes) - .finalize(); + .routes(routes); + 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 dhcp_rx_buffer = RawSocketBuffer::new( diff --git a/examples/httpclient.rs b/examples/httpclient.rs index be52306..f41bb32 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -6,7 +6,7 @@ use std::os::unix::io::AsRawFd; use url::Url; 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::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; @@ -16,13 +16,13 @@ fn main() { utils::setup_logging(""); 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); free.push("ADDRESS"); free.push("URL"); 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 device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); 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[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); - let mut iface = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) - .neighbor_cache(neighbor_cache) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .routes(routes) - .finalize(); + .routes(routes); + 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 tcp_handle = sockets.add(tcp_socket); diff --git a/examples/multicast.rs b/examples/multicast.rs index cffb73a..33d2426 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -20,11 +20,11 @@ fn main() { utils::setup_logging("warn"); 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); 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 device = utils::parse_middleware_options(&mut matches, device, diff --git a/examples/ping.rs b/examples/ping.rs index 69f3de4..816470c 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use log::debug; use byteorder::{ByteOrder, NetworkEndian}; -use smoltcp::time::{Duration, Instant}; +use smoltcp::{phy::Medium, time::{Duration, Instant}}; use smoltcp::phy::Device; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, @@ -55,7 +55,7 @@ fn main() { utils::setup_logging("warn"); 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); opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT"); opts.optopt("i", "interval", @@ -66,7 +66,7 @@ fn main() { free.push("ADDRESS"); 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 device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false); let device_caps = device.capabilities(); @@ -98,12 +98,17 @@ fn main() { 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 = InterfaceBuilder::new(device) - .ethernet_addr(ethernet_addr) + + let medium = device.capabilities().medium; + let mut builder = InterfaceBuilder::new(device) .ip_addrs(ip_addrs) - .routes(routes) - .neighbor_cache(neighbor_cache) - .finalize(); + .routes(routes); + 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 icmp_handle = sockets.add(icmp_socket); diff --git a/examples/server.rs b/examples/server.rs index 832f975..944cde4 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -6,7 +6,7 @@ use std::fmt::Write; use std::os::unix::io::AsRawFd; 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::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::SocketSet; @@ -18,11 +18,11 @@ fn main() { utils::setup_logging(""); 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); 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 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(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) - .neighbor_cache(neighbor_cache) - .ip_addrs(ip_addrs) - .finalize(); + .neighbor_cache(neighbor_cache); + } + let mut iface = builder.finalize(); let mut sockets = SocketSet::new(vec![]); let udp_handle = sockets.add(udp_socket); diff --git a/examples/utils.rs b/examples/utils.rs index 02b2de0..497ad97 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -14,9 +14,9 @@ use log::{Level, LevelFilter, trace}; use env_logger::Builder; use getopts::{Options, Matches}; -use smoltcp::phy::{Device, EthernetTracer, FaultInjector}; -#[cfg(feature = "phy-tap_interface")] -use smoltcp::phy::TapInterface; +use smoltcp::phy::{Device, EthernetTracer, FaultInjector, Medium}; +#[cfg(feature = "phy-tuntap_interface")] +use smoltcp::phy::TunTapInterface; use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType}; use smoltcp::phy::RawSocket; 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>) { - free.push("INTERFACE"); +pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) { + opts.optopt("", "tun", "TUN interface to use", "tun0"); + opts.optopt("", "tap", "TAP interface to use", "tap0"); } -#[cfg(feature = "phy-tap_interface")] -pub fn parse_tap_options(matches: &mut Matches) -> TapInterface { - let interface = matches.free.remove(0); - TapInterface::new(&interface).unwrap() +#[cfg(feature = "phy-tuntap_interface")] +pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface { + let tun = matches.opt_str("tun"); + 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 { diff --git a/src/phy/mod.rs b/src/phy/mod.rs index eadd0dc..df4a2e8 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -8,7 +8,7 @@ and implementations of it: * _middleware_ [Tracer](struct.Tracer.html) and [FaultInjector](struct.FaultInjector.html), to facilitate debugging; * _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. */ #![cfg_attr(feature = "medium-ethernet", doc = r##" @@ -89,7 +89,7 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> { use crate::Result; 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 tracer; @@ -100,10 +100,10 @@ mod pcap_writer; mod loopback; #[cfg(all(feature = "phy-raw_socket", unix))] mod raw_socket; -#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] -mod tap_interface; +#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +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::tracer::Tracer; @@ -114,8 +114,9 @@ pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter}; pub use self::loopback::Loopback; #[cfg(all(feature = "phy-raw_socket", unix))] pub use self::raw_socket::RawSocket; -#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] -pub use self::tap_interface::TapInterface; +#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +pub use self::tuntap_interface::TunTapInterface; + #[cfg(feature = "medium-ethernet")] /// A tracer device for Ethernet frames. diff --git a/src/phy/sys/linux.rs b/src/phy/sys/linux.rs index f582770..d42d83f 100644 --- a/src/phy/sys/linux.rs +++ b/src/phy/sys/linux.rs @@ -1,14 +1,10 @@ -#[cfg(any(feature = "phy-raw_socket", - feature = "phy-tap_interface"))] +#![allow(unused)] + pub const SIOCGIFMTU: libc::c_ulong = 0x8921; -#[cfg(any(feature = "phy-raw_socket"))] pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; -#[cfg(any(feature = "phy-raw_socket"))] pub const ETH_P_ALL: libc::c_short = 0x0003; -#[cfg(feature = "phy-tap_interface")] 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; -#[cfg(feature = "phy-tap_interface")] pub const IFF_NO_PI: libc::c_int = 0x1000; diff --git a/src/phy/sys/mod.rs b/src/phy/sys/mod.rs index 0730d1b..8021879 100644 --- a/src/phy/sys/mod.rs +++ b/src/phy/sys/mod.rs @@ -12,15 +12,15 @@ mod imp; pub mod raw_socket; #[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] pub mod bpf; -#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] -pub mod tap_interface; +#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +pub mod tuntap_interface; #[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))] pub use self::raw_socket::RawSocketDesc; #[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))] pub use self::bpf::BpfDevice as RawSocketDesc; -#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))] -pub use self::tap_interface::TapInterfaceDesc; +#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] +pub use self::tuntap_interface::TunTapInterfaceDesc; /// Wait until given file descriptor becomes readable, but no longer than given timeout. pub fn wait(fd: RawFd, duration: Option) -> io::Result<()> { @@ -60,7 +60,7 @@ pub fn wait(fd: RawFd, duration: Option) -> 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)] #[derive(Debug)] struct ifreq { @@ -68,7 +68,7 @@ struct ifreq { 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 { let mut ifreq = ifreq { 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"), - 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, cmd: libc::c_ulong) -> io::Result { unsafe { diff --git a/src/phy/sys/tap_interface.rs b/src/phy/sys/tuntap_interface.rs similarity index 65% rename from src/phy/sys/tap_interface.rs rename to src/phy/sys/tuntap_interface.rs index 9608e6e..6b42980 100644 --- a/src/phy/sys/tap_interface.rs +++ b/src/phy/sys/tuntap_interface.rs @@ -1,22 +1,23 @@ use std::io; use std::os::unix::io::{RawFd, AsRawFd}; use super::*; -use crate::wire::EthernetFrame; +use crate::{phy::Medium, wire::EthernetFrame}; #[derive(Debug)] -pub struct TapInterfaceDesc { +pub struct TunTapInterfaceDesc { lower: libc::c_int, - ifreq: ifreq + ifreq: ifreq, + medium: Medium, } -impl AsRawFd for TapInterfaceDesc { +impl AsRawFd for TunTapInterfaceDesc { fn as_raw_fd(&self) -> RawFd { self.lower } } -impl TapInterfaceDesc { - pub fn new(name: &str) -> io::Result { +impl TunTapInterfaceDesc { + pub fn new(name: &str, medium: Medium) -> io::Result { let lower = unsafe { let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char, libc::O_RDWR | libc::O_NONBLOCK); @@ -24,14 +25,21 @@ impl TapInterfaceDesc { lower }; - Ok(TapInterfaceDesc { - lower: lower, - ifreq: ifreq_for(name) + Ok(TunTapInterfaceDesc { + lower, + ifreq: ifreq_for(name), + medium, }) } 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(|_| ()) } @@ -46,9 +54,19 @@ impl TapInterfaceDesc { 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.) // 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 { @@ -70,7 +88,7 @@ impl TapInterfaceDesc { } } -impl Drop for TapInterfaceDesc { +impl Drop for TunTapInterfaceDesc { fn drop(&mut self) { unsafe { libc::close(self.lower); } } diff --git a/src/phy/tap_interface.rs b/src/phy/tuntap_interface.rs similarity index 78% rename from src/phy/tap_interface.rs rename to src/phy/tuntap_interface.rs index 018c2c0..cfd4f2d 100644 --- a/src/phy/tap_interface.rs +++ b/src/phy/tuntap_interface.rs @@ -8,44 +8,46 @@ use crate::Result; use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; use crate::time::Instant; -/// A virtual Ethernet interface. +/// A virtual TUN (IP) or TAP (Ethernet) interface. #[derive(Debug)] -pub struct TapInterface { - lower: Rc>, - mtu: usize +pub struct TunTapInterface { + lower: Rc>, + mtu: usize, + medium: Medium, } -impl AsRawFd for TapInterface { +impl AsRawFd for TunTapInterface { fn as_raw_fd(&self) -> RawFd { self.lower.borrow().as_raw_fd() } } -impl TapInterface { - /// Attaches to a TAP interface called `name`, or creates it if it does not exist. +impl TunTapInterface { + /// 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, /// no special privileges are needed. Otherwise, this requires superuser privileges /// or a corresponding capability set on the executable. - pub fn new(name: &str) -> io::Result { - let mut lower = sys::TapInterfaceDesc::new(name)?; + pub fn new(name: &str, medium: Medium) -> io::Result { + let mut lower = sys::TunTapInterfaceDesc::new(name, medium)?; lower.attach_interface()?; let mtu = lower.interface_mtu()?; - Ok(TapInterface { + Ok(TunTapInterface { 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 TxToken = TxToken; fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: self.mtu, - medium: Medium::Ethernet, + medium: self.medium, ..DeviceCapabilities::default() } } @@ -89,7 +91,7 @@ impl phy::RxToken for RxToken { #[doc(hidden)] pub struct TxToken { - lower: Rc>, + lower: Rc>, } impl phy::TxToken for TxToken {