Add support for TUN interfaces.

master
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.
- 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

View File

@ -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

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.
### 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.

View File

@ -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);

View File

@ -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);

View File

@ -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(

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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.

View File

@ -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;

View File

@ -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 {

View File

@ -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); }
}

View File

@ -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 {