Просмотр исходного кода

Add support for TUN interfaces.

Dario Nieuwenhuis 4 лет назад
Родитель
Сommit
b869449b31

+ 1 - 1
.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

+ 12 - 12
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

+ 2 - 2
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.
 

+ 11 - 7
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);

+ 14 - 8
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);

+ 13 - 8
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(

+ 13 - 8
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);

+ 2 - 2
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,

+ 13 - 8
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);

+ 12 - 7
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);

+ 15 - 9
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 {

+ 8 - 7
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.

+ 3 - 7
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;

+ 7 - 7
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<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)]
 #[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<libc::c_int> {
     unsafe {

+ 30 - 12
src/phy/sys/tap_interface.rs → 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<TapInterfaceDesc> {
+impl TunTapInterfaceDesc {
+    pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
         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<usize> {
@@ -70,7 +88,7 @@ impl TapInterfaceDesc {
     }
 }
 
-impl Drop for TapInterfaceDesc {
+impl Drop for TunTapInterfaceDesc {
     fn drop(&mut self) {
         unsafe { libc::close(self.lower); }
     }

+ 16 - 14
src/phy/tap_interface.rs → 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<RefCell<sys::TapInterfaceDesc>>,
-    mtu:    usize
+pub struct TunTapInterface {
+    lower:  Rc<RefCell<sys::TunTapInterfaceDesc>>,
+    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<TapInterface> {
-        let mut lower = sys::TapInterfaceDesc::new(name)?;
+    pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterface> {
+        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<RefCell<sys::TapInterfaceDesc>>,
+    lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
 }
 
 impl phy::TxToken for TxToken {