utuntap 0.3.1

A low level Rust library for Tun/Tap devices.
Documentation
use etherparse::{IpHeader, PacketBuilder, PacketHeaders, TransportHeader};
use serial_test::serial;
#[cfg(target_family = "unix")]
use std::io::ErrorKind;
use std::io::{IoSlice, Read, Write};
use std::net::{IpAddr, Ipv4Addr, UdpSocket};
#[cfg(not(target_os = "macos"))]
use utuntap::tap;
use utuntap::tun;

#[cfg(target_os = "linux")]
#[test]
#[serial]
fn tun_sents_packets() {
    let mut file = tun::OpenOptions::new()
        .packet_info(false)
        .open(10)
        .expect("failed to open device");
    let data = [1; 10];
    let socket = UdpSocket::bind("10.10.10.1:2424").expect("failed to bind to address");
    socket
        .send_to(&data, "10.10.10.2:4242")
        .expect("failed to send data");
    let mut buffer = [0; 50];
    let number = file.read(&mut buffer).expect("failed to receive data");
    assert_eq!(number, 38);
    let packet = &buffer[..number];
    if let PacketHeaders {
        ip: Some(IpHeader::Version4(ip_header, _)),
        transport: Some(TransportHeader::Udp(udp_header)),
        payload,
        ..
    } = PacketHeaders::from_ip_slice(&packet).expect("failed to parse packet")
    {
        assert_eq!(ip_header.source, [10, 10, 10, 1]);
        assert_eq!(ip_header.destination, [10, 10, 10, 2]);
        assert_eq!(udp_header.source_port, 2424);
        assert_eq!(udp_header.destination_port, 4242);
        assert_eq!(payload, data);
    } else {
        assert!(false, "incorrect packet");
    }
}

#[cfg(target_os = "linux")]
#[test]
#[serial]
fn tun_sents_packets_with_packet_info() {
    let mut file = tun::OpenOptions::new()
        .packet_info(true)
        .open(10)
        .expect("failed to open device");
    let data = [1; 10];
    let socket = UdpSocket::bind("10.10.10.1:2424").expect("failed to bind to address");
    socket
        .send_to(&data, "10.10.10.2:4242")
        .expect("failed to send data");
    let mut buffer = [0; 50];
    let number = file.read(&mut buffer).expect("failed to receive data");
    assert_eq!(number, 42);
    assert_eq!(&buffer[..4], [0, 0, 8, 0]);
    let packet = &buffer[4..number];
    if let PacketHeaders {
        ip: Some(IpHeader::Version4(ip_header, _)),
        transport: Some(TransportHeader::Udp(udp_header)),
        payload,
        ..
    } = PacketHeaders::from_ip_slice(&packet).expect("failed to parse packet")
    {
        assert_eq!(ip_header.source, [10, 10, 10, 1]);
        assert_eq!(ip_header.destination, [10, 10, 10, 2]);
        assert_eq!(udp_header.source_port, 2424);
        assert_eq!(udp_header.destination_port, 4242);
        assert_eq!(payload, data);
    } else {
        assert!(false, "incorrect packet");
    }
}

#[cfg(target_os = "linux")]
#[test]
#[serial]
fn tun_receives_packets() {
    let mut file = tun::OpenOptions::new()
        .packet_info(false)
        .open(10)
        .expect("failed to open device");
    let data = [1; 10];
    let socket = UdpSocket::bind("10.10.10.1:2424").expect("failed to bind to address");
    let builder = PacketBuilder::ipv4([10, 10, 10, 2], [10, 10, 10, 1], 20).udp(4242, 2424);
    let packet = {
        let mut packet = Vec::<u8>::with_capacity(builder.size(data.len()));
        builder
            .write(&mut packet, &data)
            .expect("failed to build packet");
        packet
    };
    file.write(&packet).expect("failed to send packet");
    let mut buffer = [0; 50];
    let (number, source) = socket
        .recv_from(&mut buffer)
        .expect("failed to receive packet");
    assert_eq!(number, 10);
    assert_eq!(source.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 10, 2)));
    assert_eq!(source.port(), 4242);
    assert_eq!(data, &buffer[..number]);
}

#[cfg(target_os = "linux")]
#[test]
#[serial]
fn tun_receives_packets_with_packet_info() {
    let mut file = tun::OpenOptions::new()
        .packet_info(true)
        .open(10)
        .expect("failed to open device");
    let data = [1; 10];
    let socket = UdpSocket::bind("10.10.10.1:2424").expect("failed to bind to address");
    let builder = PacketBuilder::ipv4([10, 10, 10, 2], [10, 10, 10, 1], 20).udp(4242, 2424);
    let packet_info = [0u8, 0, 8, 0];
    let packet = {
        let mut packet = Vec::<u8>::with_capacity(builder.size(data.len()));
        builder
            .write(&mut packet, &data)
            .expect("failed to build packet");
        packet
    };
    let iovec = [IoSlice::new(&packet_info), IoSlice::new(&packet)];
    file.write_vectored(&iovec).expect("failed to send packet");
    let mut buffer = [0; 50];
    let (number, source) = socket
        .recv_from(&mut buffer)
        .expect("failed to receive packet");
    assert_eq!(number, 10);
    assert_eq!(source.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 10, 2)));
    assert_eq!(source.port(), 4242);
    assert_eq!(data, &buffer[..number]);
}

#[cfg(target_os = "openbsd")]
#[test]
#[serial]
fn tun_sents_packets() {
    let mut file = tun::OpenOptions::new()
        .open(10)
        .expect("failed to open device");
    let data = [1; 10];
    let socket = UdpSocket::bind("10.10.10.1:2424").expect("failed to bind to address");
    socket
        .send_to(&data, "10.10.10.2:4242")
        .expect("failed to send data");
    let mut buffer = [0; 50];
    let number = file.read(&mut buffer).expect("failed to receive data");
    assert_eq!(number, 42);
    assert_eq!(&buffer[..4], [0u8, 0, 0, 2]);
    let packet = &buffer[4..number];
    if let PacketHeaders {
        ip: Some(IpHeader::Version4(ip_header, _)),
        transport: Some(TransportHeader::Udp(udp_header)),
        payload,
        ..
    } = PacketHeaders::from_ip_slice(&packet).expect("failed to parse packet")
    {
        assert_eq!(ip_header.source, [10, 10, 10, 1]);
        assert_eq!(ip_header.destination, [10, 10, 10, 2]);
        assert_eq!(udp_header.source_port, 2424);
        assert_eq!(udp_header.destination_port, 4242);
        assert_eq!(payload, data);
    } else {
        assert!(false, "incorrect packet");
    }
}

#[cfg(target_os = "openbsd")]
#[test]
#[serial]
fn tun_receives_packets() {
    let mut file = tun::OpenOptions::new()
        .open(10)
        .expect("failed to open device");
    let data = [1; 10];
    let socket = UdpSocket::bind("10.10.10.1:2424").expect("failed to bind to address");
    let builder = PacketBuilder::ipv4([10, 10, 10, 2], [10, 10, 10, 1], 20).udp(4242, 2424);
    let family = [0u8, 0, 0, 2];
    let packet = {
        let mut packet = Vec::<u8>::with_capacity(builder.size(data.len()));
        builder
            .write(&mut packet, &data)
            .expect("failed to build packet");
        packet
    };
    let iovec = [IoSlice::new(&family), IoSlice::new(&packet)];
    file.write_vectored(&iovec).expect("failed to send packet");
    let mut buffer = [0; 50];
    let (number, source) = socket
        .recv_from(&mut buffer)
        .expect("failed to receive packet");
    assert_eq!(number, 10);
    assert_eq!(source.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 10, 2)));
    assert_eq!(source.port(), 4242);
    assert_eq!(data, &buffer[..number]);
}

#[cfg(target_os = "macos")]
#[test]
#[serial]
fn tun_sents_packets() {
    let mut file = tun::OpenOptions::new()
        .open(10)
        .expect("failed to open device");

    std::process::Command::new("ifconfig")
        .arg("utun10")
        .arg("10.10.10.1")
        .arg("10.10.10.2")
        .arg("netmask")
        .arg("255.255.255.255")
        .status()
        .unwrap();

    let data = [1; 10];
    let socket = UdpSocket::bind("10.10.10.1:2424").expect("failed to bind to address");
    socket
        .send_to(&data, "10.10.10.2:4242")
        .expect("failed to send data");
    let mut buffer = [0; 50];
    let number = file.read(&mut buffer).expect("failed to receive data");
    assert_eq!(number, 42);
    assert_eq!(&buffer[..4], [0u8, 0, 0, 2]);
    let packet = &buffer[4..number];
    if let PacketHeaders {
        ip: Some(IpHeader::Version4(ip_header, _)),
        transport: Some(TransportHeader::Udp(udp_header)),
        payload,
        ..
    } = PacketHeaders::from_ip_slice(&packet).expect("failed to parse packet")
    {
        assert_eq!(ip_header.source, [10, 10, 10, 1]);
        assert_eq!(ip_header.destination, [10, 10, 10, 2]);
        assert_eq!(udp_header.source_port, 2424);
        assert_eq!(udp_header.destination_port, 4242);
        assert_eq!(payload, data);
    } else {
        assert!(false, "incorrect packet");
    }
}

#[cfg(target_os = "macos")]
#[test]
#[serial]
fn tun_receives_packets() {
    let mut file = tun::OpenOptions::new()
        .open(10)
        .expect("failed to open device");

    std::process::Command::new("ifconfig")
        .arg("utun10")
        .arg("10.10.10.1")
        .arg("10.10.10.2")
        .arg("netmask")
        .arg("255.255.255.255")
        .status()
        .unwrap();

    let data = [1; 10];
    let socket = UdpSocket::bind("10.10.10.1:2424").expect("failed to bind to address");
    let builder = PacketBuilder::ipv4([10, 10, 10, 2], [10, 10, 10, 1], 20).udp(4242, 2424);
    let family = [0u8, 0, 0, 2];
    let packet = {
        let mut packet = Vec::<u8>::with_capacity(builder.size(data.len()));
        builder
            .write(&mut packet, &data)
            .expect("failed to build packet");
        packet
    };
    let iovec = [IoSlice::new(&family), IoSlice::new(&packet)];
    file.write_vectored(&iovec).expect("failed to send packet");
    let mut buffer = [0; 50];
    let (number, source) = socket
        .recv_from(&mut buffer)
        .expect("failed to receive packet");
    assert_eq!(number, 10);
    assert_eq!(source.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 10, 2)));
    assert_eq!(source.port(), 4242);
    assert_eq!(data, &buffer[..number]);
}

#[cfg(target_family = "unix")]
#[test]
#[serial]
fn tun_non_blocking_io() {
    let mut file = tun::OpenOptions::new()
        .nonblock(true)
        .open(11)
        .expect("failed to open device");
    let mut buffer = [0; 10];
    while file.read(&mut buffer).is_ok() {}
    let error = file.read(&mut buffer).err().unwrap();
    assert_eq!(error.kind(), ErrorKind::WouldBlock);
}

#[cfg(all(target_family = "unix", not(target_os = "macos")))]
#[test]
#[serial]
fn tap_non_blocking_io() {
    let mut file = tap::OpenOptions::new()
        .nonblock(true)
        .open(11)
        .expect("failed to open device");
    let mut buffer = [0; 10];
    while file.read(&mut buffer).is_ok() {}
    let error = file.read(&mut buffer).err().unwrap();
    assert_eq!(error.kind(), ErrorKind::WouldBlock);
}