pnet 0.3.1

Cross-platform, low level networking using the Rust programming language.
// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate libc;

use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::sync::mpsc::channel;
use std::thread;
use std::iter::Iterator;

use packet::Packet;
use packet::ip::{IpNextHeaderProtocols, IpNextHeaderProtocol};
use packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
use packet::ipv4;
use packet::ipv6::{MutableIpv6Packet};
use packet::udp::{UdpPacket, MutableUdpPacket};
use packet::udp;
use transport::{udp_packet_iter, ipv4_packet_iter, transport_channel, TransportProtocol,
                TransportChannelType};
use transport::TransportProtocol::{Ipv4, Ipv6};
use util;

const IPV4_HEADER_LEN: usize = 20;
const IPV6_HEADER_LEN: usize = 40;
const UDP_HEADER_LEN: usize = 8;
const TEST_DATA_LEN: usize = 4;

fn ipv4_source() -> Ipv4Addr {
    Ipv4Addr::new(127, 0, 0, 1)
}

fn ipv4_destination() -> Ipv4Addr {
    Ipv4Addr::new(127, 0, 0, 1)
}

fn ipv6_source() -> Ipv6Addr {
    Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)
}

fn ipv6_destination() -> Ipv6Addr {
    Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)
}

// Use a protocol which is unlikely to have other packets on
const TEST_PROTO: IpNextHeaderProtocol = IpNextHeaderProtocols::Test1;

fn build_ipv4_header(packet: &mut [u8], offset: usize) {
    let mut ip_header = MutableIpv4Packet::new(&mut packet[offset..]);

    let total_len = (IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN) as u16;

    ip_header.set_version(4);
    ip_header.set_header_length(5);
    ip_header.set_total_length(total_len);
    ip_header.set_ttl(4);
    ip_header.set_next_level_protocol(TEST_PROTO);
    ip_header.set_source(ipv4_source());
    ip_header.set_destination(ipv4_destination());
    let checksum = ipv4::checksum(&ip_header.to_immutable());
    ip_header.set_checksum(checksum);
}

fn build_ipv6_header(packet: &mut [u8], offset: usize) {
    let mut ip_header = MutableIpv6Packet::new(&mut packet[offset..]);

    ip_header.set_version(6);
    ip_header.set_payload_length((UDP_HEADER_LEN + TEST_DATA_LEN) as u16);
    ip_header.set_next_header(TEST_PROTO);
    ip_header.set_hop_limit(4);
    ip_header.set_source(ipv6_source());
    ip_header.set_destination(ipv6_destination());
}

fn build_udp_header(packet: &mut [u8], offset: usize) {
    let mut udp_header = MutableUdpPacket::new(&mut packet[offset..]);

    udp_header.set_source(1234); // Arbitary port number
    udp_header.set_destination(1234);
    udp_header.set_length((UDP_HEADER_LEN + TEST_DATA_LEN) as u16);
}

fn build_udp4_packet(packet: &mut [u8], start: usize, msg: &str) {
    build_ipv4_header(packet, start);
    build_udp_header(packet, start + IPV4_HEADER_LEN as usize);

    let data_start = start + IPV4_HEADER_LEN + UDP_HEADER_LEN;
    packet[data_start + 0] = msg.char_at(0) as u8;
    packet[data_start + 1] = msg.char_at(1) as u8;
    packet[data_start + 2] = msg.char_at(2) as u8;
    packet[data_start + 3] = msg.char_at(3) as u8;

    let slice = &mut packet[(start + IPV4_HEADER_LEN as usize)..];
    let checksum = udp::ipv4_checksum(&UdpPacket::new(slice),
                                      ipv4_source(),
                                      ipv4_destination(),
                                      TEST_PROTO);
    MutableUdpPacket::new(slice).set_checksum(checksum);
}

fn build_udp6_packet(packet: &mut [u8], start: usize, msg: &str) {
    build_ipv6_header(packet, start);
    build_udp_header(packet, start + IPV6_HEADER_LEN as usize);

    let data_start = start + IPV6_HEADER_LEN + UDP_HEADER_LEN;
    packet[data_start + 0] = msg.char_at(0) as u8;
    packet[data_start + 1] = msg.char_at(1) as u8;
    packet[data_start + 2] = msg.char_at(2) as u8;
    packet[data_start + 3] = msg.char_at(3) as u8;

    let slice = &mut packet[(start + IPV6_HEADER_LEN as usize)..];
    let checksum = udp::ipv6_checksum(&UdpPacket::new(slice),
                                      ipv6_source(),
                                      ipv6_destination(),
                                      TEST_PROTO);
    MutableUdpPacket::new(slice).set_checksum(checksum);
}

// OSes have a nasty habit of tweaking IP fields, so we only check
// the less volatile fields (identification, checksum)
fn check_ipv4_header(packet: &[u8], header: &Ipv4Packet) {
    let ipv4_header = Ipv4Packet::new(packet);

    assert_eq!(header.get_version(), ipv4_header.get_version());
    assert_eq!(header.get_header_length(), ipv4_header.get_header_length());
    assert_eq!(header.get_dscp(), ipv4_header.get_dscp());
    assert_eq!(header.get_ecn(), ipv4_header.get_ecn());
    assert_eq!(header.get_total_length(), ipv4_header.get_total_length());
    assert_eq!(header.get_flags(), ipv4_header.get_flags());
    assert_eq!(header.get_fragment_offset(), ipv4_header.get_fragment_offset());
    assert_eq!(header.get_ttl(), ipv4_header.get_ttl());
    assert_eq!(header.get_next_level_protocol(), ipv4_header.get_next_level_protocol());
    assert_eq!(header.get_source(), ipv4_header.get_source());
    assert_eq!(header.get_destination(), ipv4_header.get_destination());
}

fn layer4(ip: IpAddr, header_len: usize) {
    let mut packet = [0u8; IPV6_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN];
    let packet_len = header_len + UDP_HEADER_LEN + TEST_DATA_LEN;

    match ip {
        IpAddr::V4(..) => {
            build_udp4_packet(&mut packet[..], 0, "l4i4")
        },
        IpAddr::V6(..) => {
            build_udp6_packet(&mut packet[..], 0, "l4i6")
        }
    };

    let udp = UdpPacket::new(&packet[header_len .. packet_len]);

    let (tx, rx) = channel();

    let tc = transport_channel(128, TransportChannelType::Layer4(get_proto(ip)));
    let (mut ttx, mut trx) = match tc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer4: unable to create channel: {}", e),
    };

    let res = thread::scoped( move || {
        tx.send(()).unwrap();
        let mut iter = udp_packet_iter(&mut trx);
        loop {
            let next = iter.next();
            match next {
                Ok((header, addr)) => {
                    assert_eq!(addr, ip);
                    assert_eq!(header, UdpPacket::new(&packet[header_len .. packet_len]));
                    break;
                },
                Err(e) => {
                    panic!("Receive failed for layer4_test(): {}", e);
                }
            }

        }
    });

    rx.recv().unwrap();
    match ttx.send_to(udp, ip) {
        Ok(res) => assert_eq!(res as usize, UDP_HEADER_LEN + TEST_DATA_LEN),
        Err(e) => panic!("layer4_test failed: {}", e)
    }

    fn get_proto(ip: IpAddr) -> TransportProtocol {
        match ip {
            IpAddr::V4(..) => Ipv4(TEST_PROTO),
            IpAddr::V6(..) => Ipv6(TEST_PROTO)
        }
    }

    res.join();
}

#[test]
fn layer4_ipv4() {
    layer4(IpAddr::V4(ipv4_source()), IPV4_HEADER_LEN as usize);
}

#[test]
fn layer4_ipv6() {
    layer4(IpAddr::V6(ipv6_source()), IPV6_HEADER_LEN);
}

#[test]
fn layer3_ipv4() {
    let send_addr = IpAddr::V4(ipv4_source());
    let mut packet = [0u8; IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN];

    build_udp4_packet(&mut packet[..], 0, "l3i4");

    let (tx, rx) = channel();

    let tc =  transport_channel(IPV4_HEADER_LEN + UDP_HEADER_LEN + TEST_DATA_LEN,
                                    TransportChannelType::Layer3(TEST_PROTO));
    let (mut ttx, mut trx) = match tc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer3: unable to create channel: {}", e),
    };

    let res = thread::scoped( move || {
        tx.send(()).unwrap();
        let mut iter = ipv4_packet_iter(&mut trx);
        loop {
            let next = iter.next();
            match next {
                Ok((header, addr)) => {
                    assert_eq!(addr, send_addr);
                    check_ipv4_header(&packet[..], &header);
                    let udp_header = UdpPacket::new(&header.packet()[
                                                   (header.get_header_length() as usize * 4usize) ..]);
                    assert_eq!(udp_header, UdpPacket::new(&packet[IPV4_HEADER_LEN..]));

                    assert_eq!(&udp_header.packet()[UDP_HEADER_LEN..],
                               &packet[IPV4_HEADER_LEN + UDP_HEADER_LEN..]);
                    break;
                },
                Err(e) => {
                    panic!("receive failed for layer3_ipv4_test(): {}", e);
                }
            }
        }
    });


    rx.recv().unwrap();
    match ttx.send_to(Ipv4Packet::new(&packet[..]), send_addr) {
        Ok(res) => assert_eq!(res as usize, packet.len()),
        Err(e) => panic!("layer3_ipv4_test failed: {}", e)
    }

    res.join();
}

fn get_test_interface() -> util::NetworkInterface {
    use std::clone::Clone;
    use std::env;

    (*util::get_network_interfaces()
        .as_slice().iter()
        .filter(|x| {
            match env::var("PNET_TEST_IFACE") {
                Ok(name) => x.name == name,
                Err(_) => x.is_loopback()
            }
        })
        .next()
        .unwrap())
        .clone()
}


// FIXME Find a way to test this with netmap
#[cfg(not(feature = "netmap"))]
#[test]
fn layer2() {
    use datalink::{datalink_channel, DataLinkChannelType};
<<<<<<< HEAD
    use packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
    use util;
=======
    use old_packet::ethernet::{EtherTypes, EthernetHeader, MutableEthernetHeader};
>>>>>>> Fixing bug in `get_network_interfaces_impl` interface merging.

    const MIN_PACKET_SIZE: usize = 64;
    const ETHERNET_HEADER_LEN: usize = 14;

<<<<<<< HEAD
    fn get_test_interface() -> util::NetworkInterface {
        use std::clone::Clone;
        use std::env;

        (*(&util::get_network_interfaces()[..]).iter()
            .filter(|x| {
                match env::var("PNET_TEST_IFACE") {
                    Ok(name) => x.name == name,
                    Err(_) => x.is_loopback()
                }
            })
            .next()
            .unwrap())
            .clone()
    }

=======
>>>>>>> Fixing bug in `get_network_interfaces_impl` interface merging.
    let interface = get_test_interface();

    let mut packet = [0u8; ETHERNET_HEADER_LEN +
                           IPV4_HEADER_LEN +
                           UDP_HEADER_LEN +
                           TEST_DATA_LEN];

    {
        let mut ethernet_header = MutableEthernetPacket::new(&mut packet[..]);
        ethernet_header.set_source(interface.mac_address());
        ethernet_header.set_destination(interface.mac_address());
        ethernet_header.set_ethertype(EtherTypes::Ipv4);
    }

    build_udp4_packet(&mut packet[..], ETHERNET_HEADER_LEN as usize, "l2tt");

    let (tx, rx) = channel();

    let dlc = datalink_channel(&interface,
                               MIN_PACKET_SIZE*2,
                               MIN_PACKET_SIZE*2,
                               DataLinkChannelType::Layer2);
    let (mut dltx, mut dlrx) = match dlc {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("layer2: unable to create channel: {}", e)
    };

    let res = thread::scoped( move || {
        tx.send(()).unwrap();
        let mut i = 0usize;
        let mut iter = dlrx.iter();
        loop {
            let next = iter.next();
            match next {
                Ok(eh) => {
                    if i == 10_000 {
                        panic!("layer2: did not find matching packet after 10_000 iterations");
                    }
                    if EthernetPacket::new(&packet[..]) == eh {
                        return;
                    }
                    i += 1;
                },
                Err(e) => {
                    panic!("layer2 failed: {}", e);
                }
            }
        }
    });

    rx.recv().unwrap();
    match dltx.send_to(&EthernetPacket::new(&packet[..]), None) {
        Some(Ok(())) => (),
        Some(Err(e)) => panic!("layer2_test failed: {}", e),
        None => panic!("Provided buffer too small")
    }

    res.join();
}

#[test]
fn check_test_environment() {
    use std::env;
    let tasks = env::var("RUST_TEST_THREADS");
    if !tasks.is_ok() || &tasks.unwrap()[..] != "1" {
        panic!("Tests must be run with environment variable RUST_TEST_THREADS=1");
    }

    test_iface();

    #[cfg(not(target_os = "linux"))]
    fn test_iface() {
        let iface = env::var("PNET_TEST_IFACE");
        if !iface.is_ok() {
            panic!("The environment variable PNET_TEST_IFACE must be set.");
        }
    }

    #[cfg(target_os = "linux")]
    fn test_iface() {}
}

#[test]
fn test_get_network_interfaces_ips() {
    test_iface_ips();

    #[cfg(not(target_os = "linux"))]
    fn test_iface_ips() {
        let interface = get_test_interface();
        match interface.ips {
            Some(ips) => assert!(ips.len() > 0),
            None => panic!("Could not get any IPs from test interface")
        }
    }

    #[cfg(target_os = "linux")]
    fn test_iface_ips() {}
}