Giter Site home page Giter Site logo

libpnet / libpnet Goto Github PK

View Code? Open in Web Editor NEW
2.2K 32.0 294.0 1.48 MB

Cross-platform, low level networking using the Rust programming language.

License: Apache License 2.0

Makefile 0.12% C 1.02% Python 0.34% Rust 97.54% Shell 0.98%
rust packets transport-protocols libpnet winpcap cross-platform networking datalink

libpnet's Introduction

libpnet Crates.io License Documentation

Build Status: Build Status

Discussion and support:

libpnet provides a cross-platform API for low level networking using Rust.

There are four key components:

  • The packet module, allowing safe construction and manipulation of packets;
  • The pnet_macros crate, providing infrastructure for the packet module;
  • The transport module, which allows implementation of transport protocols;
  • The datalink module, which allows sending and receiving data link packets directly.

Why?

There are lots of reasons to use low level networking, and many more to do it using Rust. A few are outlined here:

Developing Transport Protocols

There are usually two ways to go about developing a new transport layer protocol:

  • Write it in a scripting language such as Python;
  • Write it using C.

The former is great for trying out new ideas and rapid prototyping, however not so great as a real-world implementation. While you can usually get reasonable performance out of these implementations, they're generally significantly slower than an implementation in C, and not suitable for any "heavy lifting".

The next option is to write it in C - this will give you great performance, but comes with a number of other issues:

  • Lack of memory safety - this is a huge source of security vulnerabilities and other bugs in C-based network stacks. It is far too easy to forget a bounds check or use a pointer after it is freed.
  • Lack of thread safety - you have to be very careful to make sure the correct locks are used, and used correctly.
  • Lack of high level abstractions - part of the appeal of scripting languages such as Python is the higher level of abstraction which enables simpler APIs and ease of programming.

Using libpnet and Rust, you get the best of both worlds. The higher level abstractions, memory and thread safety, alongside the performance of C.

Network Utilities

Many networking utilities such as ping and traceroute rely on being able to manipulate network and transport headers, which isn't possible with standard networking stacks such as those provided by std::io::net.

Data Link Layer

It can be useful to work directly at the data link layer, to see packets as they are "on the wire". There are lots of uses for this, including network diagnostics, packet capture and traffic shaping.

Documentation

API documentation for the latest build can be found here: https://docs.rs/pnet/

Usage

To use libpnet in your project, add the following to your Cargo.toml:

[dependencies.pnet]
version = "0.34.0"

libpnet should work with the latest stable version of Rust.

When running the test suite, there are a number of networking tests which will likely fail - the easiest way to workaround this is to run cargo test as a root or administrative user. This can often be avoided, however it is more involved.

Windows

There are three requirements for building on Windows:

  • You must use a version of Rust which uses the MSVC toolchain
  • You must have WinPcap or npcap installed (tested with version WinPcap 4.1.3) (If using npcap, make sure to install with the "Install Npcap in WinPcap API-compatible Mode")
  • You must place Packet.lib from the WinPcap Developers pack in a directory named lib, in the root of this repository. Alternatively, you can use any of the locations listed in the %LIB%/$Env:LIB environment variables. For the 64 bit toolchain it is in WpdPack/Lib/x64/Packet.lib, for the 32 bit toolchain, it is in WpdPack/Lib/Packet.lib.

libpnet's People

Contributors

berkus avatar bkchr avatar chifflier avatar dependabot-preview[bot] avatar dependabot-support avatar dlrobertson avatar dsvensson avatar ebfe avatar faern avatar homu avatar hughesac avatar james-jra avatar juxhindb avatar kishiguro avatar landesfeind avatar landhb avatar little-dude avatar lukeasrodgers avatar martichou avatar moonpolysoft avatar moosingin3space avatar mrmonday avatar nbaraz avatar petehayes102 avatar reticulis avatar termquick avatar teutat3s avatar vmx avatar vvv avatar yu-re-ka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libpnet's Issues

Support 802.1Q tags

VLAN tags handling is usually done on switches level and not needed on end-points. This is not true for virtual networking hypervisors which could be connected via trunk in order to divide multiple VM networks.

EthernetPacket should have one more method fn get_tag(&self) -> Option(VlanTag)

What has to be done to implement this? I guess it is possible even now, match 0x8100 EtherType and then handle tag and real EtherType in payload. But native support would be great.

Example 'packetdump.rs' panicked at 'called `Option::unwrap()` on a `None` value', ../src/libcore/option.rs

Using rust-1.6.0 on OS X 10.10.5, I tried the example packetdump.rs, which panicked with:

pastoraleman@macbook:~/dev/packetdumper$ cargo run --verbose
       Fresh regex-syntax v0.2.2
       Fresh winapi-build v0.1.1
       Fresh pnet_macros_support v0.1.1
       Fresh rustc-serialize v0.3.18
       Fresh libc v0.1.12
       Fresh unicode-xid v0.0.3
       Fresh libc v0.2.7
       Fresh bitflags v0.3.3
       Fresh winapi v0.2.5
       Fresh log v0.3.5
       Fresh memchr v0.1.7
       Fresh aho-corasick v0.4.1
       Fresh kernel32-sys v0.2.1
       Fresh regex v0.1.48
       Fresh term v0.2.14
       Fresh syntex_syntax v0.26.0
       Fresh syntex v0.26.0
       Fresh pnet_macros v0.7.4
       Fresh pnet v0.7.4
       Fresh packetdumper v0.1.0 (file:///Users/pastoraleman/dev/packetdumper)
     Running `target/debug/packetdumper`
thread '<main>' panicked at 'called `Option::unwrap()` on a `None` value', ../src/libcore/option.rs:367
stack backtrace:
   1:        0x10542df58 - sys::backtrace::tracing::imp::write::h71b7c99a21cff148fMt
   2:        0x10542f46f - panicking::log_panic::_<closure>::closure.40876
   3:        0x10542ef12 - panicking::log_panic::h2d79dd1ccae87ed8BFx
   4:        0x105427726 - sys_common::unwind::begin_unwind_inner::hb8ac08b8a72b9d1bhPs
   5:        0x10542789e - sys_common::unwind::begin_unwind_fmt::h076ba7ed08690e9dnOs
   6:        0x10542d577 - rust_begin_unwind
   7:        0x10544f0c0 - panicking::panic_fmt::h3967afc085fe8067LFK
   8:        0x10544f3bc - panicking::panic::h77d028a733b1a80eiEK
   9:        0x1054161a8 - option::_<impl>::unwrap::unwrap::h5829740329116412953
  10:        0x1054158f7 - main::h83448c216bee47e3Zla
  11:        0x10542ec92 - sys_common::unwind::try::try_fn::h4103587514840227558
  12:        0x10542d398 - __rust_try
  13:        0x10542eb39 - rt::lang_start::h216753457f385fdaJCx
  14:        0x10541d589 - main
Process didn't exit successfully: `target/debug/packetdumper` (exit code: 101)
pastoraleman@macbook:~/dev/packetdumper$ 

error: unresolved import `packet::ethernet::EthernetPacket`. There is no `EthernetPacket` in `packet::ethernet`

Trying to compile with rust nightly, I get the following errors. (I don't need nightly, but with stable, I get different errors...)

   Compiling pnet_macros v0.1.0 (https://github.com/libpnet/libpnet.git#31fefb3a)
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/mod.rs:15:35: 15:49 error: unresolved import `packet::ethernet::EthernetPacket`. There is no `EthernetPacket` in `packet::ethernet` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/mod.rs:15 use packet::ethernet::{EtherType, EthernetPacket, MutableEthernetPacket};
                                                                                                                          ^~~~~~~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/mod.rs:15:35: 15:49 help: run `rustc --explain E0432` to see a detailed explanation
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/mod.rs:15:51: 15:72 error: unresolved import `packet::ethernet::MutableEthernetPacket`. There is no `MutableEthernetPacket` in `packet::ethernet` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/mod.rs:15 use packet::ethernet::{EtherType, EthernetPacket, MutableEthernetPacket};
                                                                                                                                          ^~~~~~~~~~~~~~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/mod.rs:15:51: 15:72 help: run `rustc --explain E0432` to see a detailed explanation
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/bpf.rs:21:24: 21:38 error: unresolved import `packet::ethernet::EthernetPacket`. There is no `EthernetPacket` in `packet::ethernet` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/bpf.rs:21 use packet::ethernet::{EthernetPacket, MutableEthernetPacket};
                                                                                                               ^~~~~~~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/bpf.rs:21:24: 21:38 help: run `rustc --explain E0432` to see a detailed explanation
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/bpf.rs:21:40: 21:61 error: unresolved import `packet::ethernet::MutableEthernetPacket`. There is no `MutableEthernetPacket` in `packet::ethernet` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/bpf.rs:21 use packet::ethernet::{EthernetPacket, MutableEthernetPacket};
                                                                                                                               ^~~~~~~~~~~~~~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/datalink/bpf.rs:21:40: 21:61 help: run `rustc --explain E0432` to see a detailed explanation
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:35:20: 35:30 error: unresolved import `packet::ipv4::Ipv4Packet`. There is no `Ipv4Packet` in `packet::ipv4` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:35 use packet::ipv4::{Ipv4Packet};
                                                                                                        ^~~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:35:20: 35:30 help: run `rustc --explain E0432` to see a detailed explanation
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:36:19: 36:28 error: unresolved import `packet::udp::UdpPacket`. There is no `UdpPacket` in `packet::udp` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:36 use packet::udp::{UdpPacket};
                                                                                                       ^~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:36:19: 36:28 help: run `rustc --explain E0432` to see a detailed explanation
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:147:13: 147:44 error: unresolved import `packet::ipv4::MutableIpv4Packet`. There is no `MutableIpv4Packet` in `packet::ipv4` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:147         use packet::ipv4::MutableIpv4Packet;
                                                                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:147:13: 147:44 help: run `rustc --explain E0432` to see a detailed explanation
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:222:25: 222:56 error: unresolved import `packet::ipv4::MutableIpv4Packet`. There is no `MutableIpv4Packet` in `packet::ipv4` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:222                     use packet::ipv4::MutableIpv4Packet;
                                                                                                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:177:1: 247:2 note: in expansion of transport_channel_iterator!
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:249:1: 251:47 note: expansion site
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:222:25: 222:56 help: run `rustc --explain E0432` to see a detailed explanation
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:222:25: 222:56 error: unresolved import `packet::ipv4::MutableIpv4Packet`. There is no `MutableIpv4Packet` in `packet::ipv4` [E0432]
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:222                     use packet::ipv4::MutableIpv4Packet;
                                                                                                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:177:1: 247:2 note: in expansion of transport_channel_iterator!
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:253:1: 255:46 note: expansion site
/Users/lars/.cargo/git/checkouts/libpnet-82bd55c36237b802/master/src/transport.rs:222:25: 222:56 help: run `rustc --explain E0432` to see a detailed explanation
error: aborting due to 9 previous errors
Could not compile `pnet`.

NetworkInterface addresses and masks

NetworkInterface would be much more useful if the interface addresses included the netmask. This might be done with a MaskedIP type that consists conceptually of a (ip, mask) tuple with some conveniences to check if a given address falls within the ip/mask combo. If you'd be willing to merge something like this, I can implement it for OSX and Linux.

Field level documentation inside #[packet] errors

#[packet]
struct Foo {
/// Bar is used for quuxing.
bar: u1
}
   Compiling pnet v0.10.0 (file:///home/robertc/personal/rust/libpnet)
error: failed to run custom build command for `pnet v0.10.0 (file:///home/robertc/personal/rust/libpnet)`
Process didn't exit successfully: `/home/robertc/personal/rust/libpnet/target/debug/build/pnet-91d70124e1d583bc/build-script-build` (exit code: 101)
--- stderr
src/packet/gre.rs.in:42:5: 42:25 error: unknown attribute: doc
src/packet/gre.rs.in:42     checksum_present: u1,
                            ^~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
thread '<main>' panicked at 'Box<Any>', /home/robertc/.cargo/registry/src/github.com-1ecc6299db9ec823/syntex_syntax-0.31.0/src/errors/mod.rs:613
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Simple packet inspection

Currently, examples/packet_dump.rs is 143 lines and growing. A simple packet logger should be writeable in less than 10 lines.

Audit unsafe code

The unsafe code in libpnet should be checked over. In particular:

  • Any assumptions made should be codified (to the extent possible)
    • This likely involves adding lots of assert/debug_assert statements to check for null and such
  • All FFI code should be tested using ctest or something similar
  • The latest advise from the Rustonomicon should be checked/followed

Issue getting IPs for interfaces on OSX

I'm having an issue getting IP addresses for interfaces on OSX using get_network_interfaces.

The problem appears to be this code: https://github.com/libpnet/libpnet/blob/ea45c1d8b7d91b98ebe4b873d2f77fcd71c632dd/src/util.rs#L226-236.

If old.ips is None, (i.e. the first interface we found for a given name has no IPs), then the new ones will never be merged in.

I know you're working on a rewrite, so I didn't bother opening a PR, but would be happy to do so (with an attempt at a test demoing the issue) if you'd like.

bpf.datalink_channel gives misleading error

On OSX, when datalink_channel fails to open a BPF device due to incorrect permissions, a confusing error is returned. This is because the code iterates over an arbitrary number of possible BPF devices and just returns the last error rather than the most pertinent one. Since the last tested BPF device almost certainly doesn't exist, almost any failure to open a BPF device results in a "no such file or directory" error.

Two options to fix this: a) test for the existence of device files before attempting to open them, so the last open error is likely to be informative or b) just make the returned error generic "Could not open BPF device" should be enough.

a few questions and issues about using libpnet

Checksum

When trying to implement icmp packets, I've added one #[packet] per icmp
type. Checksum calculation is the same for all those packets, but the function
I wrote for it only accepts one type of packets:

/// Calculates the checksum of an ICMP packet
pub fn checksum(packet: &IcmpPacket) -> u16be {
    use packet::Packet;

    let mut sum = 0u32;
    let mut i = 0;
    while i < packet.packet().len() {
        let word = (packet.packet()[i] as u32) << 8 | packet.packet()[i + 1] as u32;
        sum = sum + word;
        i = i + 2;
    }
    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }

    !sum as u16
}

I didn't try too much but I guess I could solve this by implementing a trait
IcmpChecksum and use #[derive(IcmpChecksum)] on all the icmp packets.

Struggling with ownership concepts

Coming from python, I still struggling with some basic things. Here is what I would have been able to do but could get working.

nested new

When I know a buffer represents an icmp packet, I'd like to be able to do
this:

let capture = &testutils::read_capture("echo_request");
// let say `capture[0]` is a Vec<u8> representing an icmp echo request packet
let icmp_packet =
    EchoRequest::new(
        Ipv4Packet::new(
            EthernetPacket::new($capture[0][..])
            .unwrap()
            .payload())
        .unwrap()
        .payload())
    .unwrap();

But this does not work because the ethernet packet does not live long enough.
So I have to go with this version:

let capture = &testutils::read_capture("echo_request");
// let say `capture[0]` is a Vec<u8> representing an icmp echo request packet
let ethernet = EthernetPacket::new($capture[0][..]).unwrap();
let ip = Ipv4Packet::new(ethernet.payload()).unwrap();
let echo_request =  EchoRequest::new(ip.payload()).unwrap();

returning a packet

when writing tests for icmp packets I wanted to be able to write something like
this (I know, it saves only two lines, but these are two lines I would have to
write all the time):

fn get_icmp_buf(packet_buf: &[u8]) -> &[u8] {
    let eth = EthernetPacket::new(packet_buf).unwrap();
    let ip = Ipv4Packet::new(eth.payload()).unwrap():
    ip.payload();
}

// then later:

let capture = testutils::read_capture("time_exceeded");
let time_exceeded_packet = TimeExceeded::new(get_icmp_buf(&capture[0][..])).unwrap();

But the compiler complains so I ended up copying the data:

fn get_ip_payload(packet_bytes: &[u8]) -> Vec<u8> {
    let eth_packet = EthernetPacket::new(packet_bytes).unwrap();
    let ip_packet = Ipv4Packet::new(eth_packet.payload()).unwrap();
    let mut bytes = vec![];
    for byte in ip_packet.payload() {
        bytes.push(*byte);
    }
    bytes
}

For the same reason, I cannot make a function that returns an ip packet

fn get_ip_pakcet(packet_bytes: &[u8]) -> Ipv4Packet {
    let eth_packet = EthernetPacket::new(packet_bytes).unwrap();
    Ipv4Packet::new(eth_packet.payload()).unwrap();
}

panic!

when an ip packet does not have any option in the header the following seems to panic!:

ip_packet.get_options_raw()
 thread 'packet::icmp::tests::destination_unreachable' panicked at 'slice index starts at 20 but ends at 0', src/libcore/slice.rs:578

bump version

I don't really know how versioning works with rust, but I had to change pnet version to 0.5.2 to make it compile. Should I open a PR for this?
little-dude@c9bbacd#diff-80398c5faae3c069e4e6aa2ed11b28c0L3

implementing fmt::Debug for packets

It would be really nice to debug tests if we could print the packets like this:

println!("{:?}", my_ip_packet);
// output would look like:
// 
// source: (0x0A, 0x00, 0x00, 0x01)
// destination: (0x08, 0x08, 0x08, 0x08)
// header_length: 0x05
// etc.

Not sure how hard it would be, but I could look into it.

Automated build infrastructure

Currently we're using Travis CI for testing/building, unfortunately it can only build/test on Linux, and there are obviously multiple platforms that need testing. I can set up VMs/Vagrant/whatever for OS X/Windows/FreeBSD, but need to invest some time into automating builds etc.

Add TCP packet encoding/decoding

Greetings. If it's OK with you I was thinking that this ticket could serve as an async comm channel alternative to #libpneton IRC to discuss this feature addition.

please add direct pnet_macro support for optional fields in protocols

For instance, in GRE headers, having either of the checksum or source routing bits set will cause both the checksum and offset fields to be present (both of which are fixed size when present). Right now they get implemented using a Vec of narrower packet definitions, but this is a little awkward: there's no payload to even consider within that, and a dedicated type has to be added just to enable it.

Ideally one could write something like:

#[packet]
struct Foo {
checksum_present: u1,
#[option_fn = checksum_present]
checksum: u16be,
}

fn checksum_present(foo: &FooPacket) -> bool {
 foo.checksum_present
}

And for more complex types, replace u16be with a struct type.

add rfc2460 ipv6 checksum function

let it be known: i am volunteering to write this code.

The TCP checksum in a TCP/IPv6 packet uses the same rfc1071 checksum function but sets up the initial data differently. It's described in the rfc:
https://www.ietf.org/rfc/rfc2460.txt

This essentially means that the IPv6 pseudoheader for calculating the TCP checksum is necessarily different than the IPv4 pseudoheader... since the source and dest addresses in IPv6 are 128bit.

The rfc1071 checksum is based on addition and thus we should first calculate the pseudo-header checksum and then ADD to that with the rest of the TCP header checksum calculation. In this way we create good abstraction so that we reuse the same checksum function for all protocols... but have a differing preparatory step depending on the wrapping protocol (ipv4 versus ipv6).

gopacket's implementation is a good code reference:
https://github.com/google/gopacket/blob/master/layers/tcpip.go

This ticket is related to issue #90 tcp/ipv6.

Support alternative DLTs

Currently DataLinkStream is hard coded to use Ethernet - it should support arbitrary data link types.

libpnet is letting me iterate over ICMP packets as if they were IPv4 packets

Not the greatest bug title of all time, as I'm not really sure what the issue is, but I was encouraged on IRC to post it.

I wrote some code that looked a bit like this:

extern crate pnet;

use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::transport::ipv4_packet_iter;
use pnet::transport::transport_channel;
use pnet::transport::TransportChannelType::Layer4;
use pnet::transport::TransportProtocol::Ipv4;

fn main() {
  let protocol = Layer4(Ipv4(IpNextHeaderProtocols::Icmp));

  let (_, mut rx) = transport_channel(4096, protocol).unwrap();

  let mut iter = ipv4_packet_iter(&mut rx);
  loop {
    let (packet, _) = iter.next().unwrap();
    println!("Received {:?} from {} to {}", packet.get_next_level_protocol(), packet.get_source(), packet.get_destination());
  }
}

To run, note that you need to run this as root, and generate incoming ICMP packets somehow. Here's the output when I run ping localhost at the same time:

Received IpNextHeaderProtocol(188) from 0.10.25.160 to 8.9.10.11
Received IpNextHeaderProtocol(188) from 0.10.43.93 to 8.9.10.11
Received IpNextHeaderProtocol(188) from 0.10.60.30 to 8.9.10.11

Obviously this is not correct! On IRC my error was pointed out to me - I am being given the payload of the packet by the iterator, not the IP header as well - but it was suggested that it would be nice to give a compile time error for this, and I'm inclined to agree!

Additionally, I managed to cause a panic when pushing further through with my bad code. This seems so highly related I'm filing one issue for the both, but if it's two separate issues just let me know and I will create another!

This code crashes (same instructions for running as the first example):

extern crate pnet;

use pnet::packet::Packet;
use pnet::packet::icmp::IcmpPacket;
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::transport::transport_channel;
use pnet::transport::TransportProtocol::Ipv4;
use pnet::transport::TransportChannelType::Layer4;
use pnet::transport::ipv4_packet_iter;

fn main() {
    let protocol = Layer4(Ipv4(IpNextHeaderProtocols::Icmp));

    let (_, mut rx) = transport_channel(4096, protocol).unwrap();

    let mut iter = ipv4_packet_iter(&mut rx);
    let (packet, _) = iter.next().unwrap();
    IcmpPacket::new(packet.payload());
}

Output:

thread 'main' panicked at 'attempt to subtract with overflow', /Users/zofrex/Code/rust/ping/target/debug/build/pnet-81bd5cb98b679239/out/ipv4.rs:1141

I don't have a useful backtrace to hand (I'm on OS X), I can try to figure out getting one if that would be useful. I guess the crashing part is perhaps related to #141?

panic on UDP frames where payload is less than 8B.

[1 ytti@lintukoto ~]% sudo hping -d 7 -0H 253 194.100.7.227

Causes this backtrace:

thread '<main>' panicked at 'called `Option::unwrap()` on a `None` value', ../src/libcore/option.rs:363
stack backtrace:
   1:     0x7fbc471ba999 - sys::backtrace::write::h58560f48d5b719d9lvs
   2:     0x7fbc471c2c69 - panicking::on_panic::h7c79997343596b35Pxx
   3:     0x7fbc4718802e - rt::unwind::begin_unwind_inner::h56159f0d5aa797b8h0w
   4:     0x7fbc47188bd1 - rt::unwind::begin_unwind_fmt::h21db40757fae9a0dnZw
   5:     0x7fbc471c25c1 - rust_begin_unwind
   6:     0x7fbc47210aef - panicking::panic_fmt::h45b84e160969d0c8qgC
   7:     0x7fbc4720b268 - panicking::panic::h62fafe958ac8e785XeC
   8:     0x7fbc47914cc8 - option::Option<T>::unwrap::h2911093014629543058
                        at ../src/libcore/macros.rs:20
   9:     0x7fbc4791607d - transport::UdpTransportChannelIterator<'a>::next::h8479062227c3c6c9Ure
                        at /home/ytti/.cargo/git/checkouts/libpnet-fbe89e77b3a2f206/master/src/transport.rs:210
  10:     0x7fbc4790f35f - main::he6f75027b1d55d39taa
                        at src/annubar.rs:21
  11:     0x7fbc471c4b14 - rt::unwind::try::try_fn::h8862207228583653733
  12:     0x7fbc471c2568 - __rust_try
  13:     0x7fbc471c47cb - rt::lang_start::h74469c6250afc3c4Ttx
  14:     0x7fbc47912166 - main
  15:     0x7fbc46d58b44 - __libc_start_main
  16:     0x7fbc4790ed48 - <unknown>
  17:                0x0 - <unknown>

Changing -d to 8 and problem is gone.

Code is essentially:

fn main() {
  let protocol = Layer4(Ipv4(IpNextHeaderProtocols::Test1));
  let (_tx, mut rx) = match transport_channel(4096, protocol) {
    Ok((tx, rx)) => (tx, rx),
    Err(e) => panic!("An error occurred when creating the transport channel: {}", e)
  };

  let mut iter = udp_packet_iter(&mut rx);
  loop {
    println!("here0");
    match iter.next() {
      Ok((packet, _addr)) => {
        println!("here1");

here1 is never reached

Can not compile plugin example

I try to compile plugin example
and get this error:

pnet_macros_plugin-0.1.0\src\lib.rs:15:27: 15:35 error: mismatched types [E0308]
pnet_macros_plugin-0.1.0\src\lib.rs:15     pnet_macros::register(registry);
pnet_macros_plugin-0.1.0\src\lib.rs:15:27: 15:35 help: run `rustc --explain E0308` to see a detailed explanation
pnet_macros_plugin-0.1.0\src\lib.rs:15:27: 15:35 note: expected type `&mut syntex::Registry`
pnet_macros_plugin-0.1.0\src\lib.rs:15:27: 15:35 note:    found type `&mut rustc_plugin::Registry<'_>`
   Compiling syntex_syntax v0.31.0
   Compiling syntex v0.31.0
   Compiling pnet_macros v0.9.0
   Compiling pnet v0.10.0
   Compiling pnet_macros_support v0.1.1
   Compiling pnet_macros_plugin v0.1.0

Document overheads and guarantees?

I was looking to do packet manipulation on top of netmap_sys (specifically looking for 10+Gbps line-rate capabilities, so DPDK and netmap and PF_RING_ZC are the only options AIUI), and pnet looks like an interesting possiblity, but it seems - at brief inspection - to introduce a number of inefficiencies on top of the basic batch operation. I'm sure I'm not getting all the subtleties of rust though, so would be great would be something in the docs / README that talks about overheads.

E.g. "libpnet supports zero-copy receive and send of packets with DPDK/netmap - e.g. a router receiving packets, updating the destination MAC and sending on the same interface can do so with no copies involved." [or whatever the actual capabilities of pnet are].

Frame check sequence incorrect

While running a slightly modified version of the rs_sender.rs file under rust nightly (version: nightly.20150828164732) the frame check sequence is incorrect according to wireshark and I don't receive the udp packet on the targeted server. I could not find in the source code where the FCS was computed.

Here is the script I use:

// Copyright (c) 2014, 2015 Robert Clipsham <[email protected]>
//
// 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.

// FIXME Remove before 1.0
#![feature(str_char)]

extern crate pnet;

use pnet::datalink::datalink_channel;
use pnet::datalink::DataLinkChannelType::Layer2;
use pnet::packet::{MutablePacket, Packet};
use pnet::packet::ethernet::{EtherTypes, MutableEthernetPacket, EthernetPacket};
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::ipv4::MutableIpv4Packet;
use pnet::packet::ipv4;
use pnet::packet::udp::MutableUdpPacket;
use pnet::packet::udp;
use pnet::util::get_network_interfaces;

use std::env;
use std::net::Ipv4Addr;

static IPV4_HEADER_LEN: usize = 20;
static UDP_HEADER_LEN: usize = 8;
static TEST_DATA_LEN: usize = 5;

pub fn build_ipv4_header(packet: &mut [u8]) -> MutableIpv4Packet {
    let mut ip_header = MutableIpv4Packet::new(packet).unwrap();

    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(IpNextHeaderProtocols::Udp);
    ip_header.set_source(Ipv4Addr::new(127, 0, 0, 1));
    ip_header.set_destination(Ipv4Addr::new(127, 0, 0, 1));

    let checksum = ipv4::checksum(&ip_header.to_immutable());
    ip_header.set_checksum(checksum);

    ip_header
}

pub fn build_udp_header(packet: &mut [u8]) -> MutableUdpPacket {
    let mut udp_header = MutableUdpPacket::new(packet).unwrap();

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

    udp_header
}

pub fn build_udp4_packet(packet: &mut [u8], msg: &str) {
    let mut ip_header = build_ipv4_header(packet);
    let source = ip_header.get_source();
    let destination = ip_header.get_destination();
    let mut udp_header = build_udp_header(ip_header.payload_mut());

    {
        let data = udp_header.payload_mut();
        data[0] = msg.char_at(0) as u8;
        data[1] = msg.char_at(1) as u8;
        data[2] = msg.char_at(2) as u8;
        data[3] = msg.char_at(3) as u8;
        data[4] = msg.char_at(4) as u8;
    }

    let checksum = udp::ipv4_checksum(&udp_header.to_immutable(),
                                      source, destination, IpNextHeaderProtocols::Udp);
    udp_header.set_checksum(checksum);
}

fn main() {
    let interface_name = env::args().nth(1).unwrap();
    let destination = (&env::args().nth(2).unwrap()[..]).parse().unwrap();
    // Find the network interface with the provided name
    let interfaces = get_network_interfaces();
    let interface = interfaces.iter()
                              .filter(|iface| iface.name == interface_name)
                              .next()
                              .unwrap();

    // Create a channel to send on
    let (mut tx, _) = match datalink_channel(interface, 64, 0, Layer2) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("rs_sender: unable to create channel: {}", e)
    };

    let mut buffer = [0u8; 64];
    let mut mut_ethernet_header = MutableEthernetPacket::new(&mut buffer[..]).unwrap();
    {
        mut_ethernet_header.set_destination(destination);
        mut_ethernet_header.set_source(interface.mac_address());
        mut_ethernet_header.set_ethertype(EtherTypes::Ipv4);
        build_udp4_packet(mut_ethernet_header.payload_mut(), "rmesg");
    }

    let ethernet_header = EthernetPacket::new(mut_ethernet_header.packet()).unwrap();

    loop {
        match tx.send_to(&ethernet_header, None) {
            Some(Err(e)) => println!("{:?}", e),
            Some(Ok(())) => println!("Ok"),
            None => println!("Something happened")
        };
    }
}

And here the Ethernet description given by wireshark in the frame sent:

Ethernet II, Src: Dell_71:c8:9d (34:17:eb:71:c8:9d), Dst: Dell_71:c8:9d (34:17:eb:71:c8:9d)
Destination: Dell_71:c8:9d (34:17:eb:71:c8:9d)
Source: Dell_71:c8:9d (34:17:eb:71:c8:9d)
Type: IP (0x0800)
00:00:00:00:00:00:00:00:00:00:00:00:00
Frame check sequence: 0x00000000 [incorrect, should be 0xc8e7dc81]
FCS Good: False
FCS Bad: True
Expert Info (Error/Checksum): Bad checksum

The frame:

3417eb71c89d3417eb71c89d080045000021000000000411b8ca7f0000017f00000104d20dcf000d494f726d6573670000000000000000000000000000000000

Thank you for your help.

Get libpnet building on stable Rust

All the remaining features we rely on from the standard library have been either stabilised or deprecated for the 1.6 release cycle, with the exception of compiler plugins - we can use syntex to get around this issue.

Ipv4 packet size issue

Maybe I'm doing something wrong (always a distinct possibility) but i belive the following code should pass it's assertion because the IHL value is 0b1111 which implies 15 * 32bit fields, (15 * 32) / 8 == 60. This is documented in RFC 791 as well as on Wikipedia.

extern crate pnet;

use pnet::packet::ipv4::Ipv4Packet;
use pnet::packet::PacketSize;

fn main() {
  let raw_packet: [u8; 28] = [
    0b01001111, 0b00000000, 0b00000000, 0b00100000,
    0b00000000, 0b00000000, 0b01000000, 0b00000000,
    0b01000000, 0b00000001, 0b00000000, 0b00000000,
    0b00000000, 0b00000000, 0b00000000, 0b00000000,
    0b00000000, 0b00000000, 0b00000000, 0b00000000,
    // ICMP
    0b00001000, 0b00000011, 0b00000000, 0b10000000,  // TODO: Fix checksum.
    0b10000000, 0b11111111, 0b10000000, 0b10000000,
  ];
  let packet = Ipv4Packet::new(&raw_packet).unwrap();

  assert_eq!(packet.packet_size(), 60)
}

I found this debugging an even more strange issue where the first bytes of the IP header were being used for the first bytes of my ICMP header, despite having the correct values in the payload of the IP packet.

Windows support

Currently all the code is written for Windows support, but it does not work:

  • On Win32 receives time out
  • On Win64 the tests segfault

These issues need investigating and fixing.

length_fn compile error

I have the following test struct

[packet]

pub struct MsgTest{

source: u16be,

destination: u16be,

#[length_fn="msg_options_length"]

options:Vec<u8>,

#[payload]
payload: Vec<u8>

}
this works well.But when I add " t:u8," after options:Vec just as following codes:
pub struct MsgTest{

source: u16be,

destination: u16be,

#[length_fn="msg_options_length"]

options:  Vec <u8>,

t:u8, //this cause compile error

#[payload]
payload: Vec<u8>

}

the error message is

:141:47: 141:58 error: use of moved value: self.packet [E0382]
:141 self_.packet[4 + msg_options_length(&self.to_immutable()) + 0] = (val) as u8;

Support #![no_std]

A lot of people are writing operating systems kernels in Rust, and they'll be looking for a network stack. pnet could be a choice option.

Some thoughts:

  • nom implements this as a feature on their package. Compiling with the core feature means that #![no_std] compilation is supported.
  • It's probably reasonable to assume support for core, alloc, and collections.
  • On the other hand, assuming support for libc is not going to go well. AFAIK It's pretty much an interface to glibc, which I wouldn't expect anyone to port to their OS.

can't run packetdump example with rust version 1.9 Linux

cargo run --example packetdump
Running target/debug/examples/packetdump
thread '

' panicked at 'called Option::unwrap() on a None value', ../src/libcore/option.rs:326
note: Run with RUST_BACKTRACE=1 for a backtrace.
error: Process didn't exit successfully: target/debug/examples/packetdump (exit code: 101)

Stop sending entire backing buffer as ethernet frame

The library sends too many bytes in the ethernet frames.

When using EthernetDataLinkSender::build_and_send with packet_size set to 100 and you construct a 20 byte packet in the callback (14 bytes header + 6 bytes in the payload) then libpnet actually transmit all 100 bytes to the network when you would expect it to send only 20.

If the library would reuse the same buffer in a consecutive call to the callback func and the user would construct an even smaller packet this time then the remaining bytes would not just be zeroes, it would be data from the old packet. I didn't fully study the implementation, but this might be a possible scenario.

Possible payload positioning issue.

The below code should construct an ICMP echo request packet. When capturing the packet with tcpdump it was showing up as an echo reply due to an extra byte between the destination address and the payload data. It's possible I'm just using the API incorrectly in some way, if so please let me know.

Captured packet including ethernet frame:

0000  00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00   ..............E.
0010  00 40 00 00 40 00 40 01 3c bb 7f 00 00 01 7f 00   .@..@.@.<.......
0020  00 01 00 08 00 00 00 00 00 00 01 00 00 00 00 00   ................
0030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0040  00 00 00 00 00 00 00 00 00 00 00 00 00 00         ..............

The issue is in the third line. The first two bytes are the end of the 127.0.0.1 address, then there is a zero byte before the start of the payload.

Example code:

 #![feature(ip_addr)]
extern crate pnet;

use pnet::transport::TransportChannelType::Layer3;
use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};

fn main() {
    let protocol = Layer3(IpNextHeaderProtocols::Icmp);
    let (mut tx, mut rx) = match pnet::transport::transport_channel(1024, protocol) {
        Ok((tx, rx)) => (tx, rx),
        Err(e) => panic!("An error occurred when creating the transport channel: {}", e),
    };

    let addr = std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1));
    let addr_v4 = std::net::Ipv4Addr::new(127, 0, 0, 1);
    let mut vec: Vec<u8> = vec![0; 64];
    let mut packet = MutableIpv4Packet::new(&mut vec);

    let payload: Vec<u8> = vec![8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0];
    let options: Vec<pnet::packet::ipv4::Ipv4Option> = vec![];

    packet.set_version(4);
    packet.set_header_length(5);
    packet.set_ttl(64);
    packet.set_flags(2);
    packet.set_next_level_protocol(IpNextHeaderProtocols::Icmp);
    packet.set_source(addr_v4);
    packet.set_destination(addr_v4);
    packet.set_options(options);
    packet.set_payload(payload);

    packet.set_total_length(64);
    let l = packet.get_total_length();

    match tx.send_to(packet, addr) {
        Ok(n) => assert_eq!(n, l as usize),
        Err(e) => panic!("failed to send packet: {}", e)
    };
}

pselect EINVAL when compiling in release mode

I have an application that sets up a channel with a read_timeout:

datalink::Config { read_timeout: Some(Duration::new(10, 0)), ..Default::default() };

When compiling my application in debug mode everything works and strace shows pselect calls with the expected timeout:

pselect6(5, [4], NULL, NULL, {10, 0}, {NULL, 8}) = 1 (in [4], left {9, 999998995})

But when compiling the same application in release mode, some receive calls fail with an EINVAL error and from the strace output it looks like the timeout argument is filled with garbage data:

pselect6(5, [4], NULL, NULL, {908191936, 2097152}, {NULL, 8}) = 1 (in [4], left {908191936, 2095630})
pselect6(5, [4], NULL, NULL, {0, 78264}, {NULL, 8}) = 1 (in [4], left {0, 77124})
pselect6(5, [4], NULL, NULL, {0, 2097865011817319249}, {NULL, 8}) = -1 EINVAL (Invalid argument)
pselect6(5, [4], NULL, NULL, {30, 2024323}, {NULL, 8}) = 1 (in [4], left {30, 2021266})
pselect6(5, [4], NULL, NULL, {0, 2097865011889982044}, {NULL, 8}) = -1 EINVAL (Invalid argument)

No panicking

There are currently multiple locations where libpnet/the code generated by libpnet_macros could panic. The code should be audited to make sure everywhere returns a Result rather than panicking.

Header/Packet Equality

We need to decide what it means for an XHeader or XPacket to be equal. There are three possibilities:

  • The entire packet must be equal, including payload
  • Only the header must be equal
  • Only selected parts of the header must be equal (ie. Ignore volatile fields such as TTL).

'arithmetic operation overflowed' when not calling set_header_length

The following code sample will panic with an 'arithmetic operation overflowed' error if set_header_length is not called or if the value is set to 16 or greater

let mut bytes = [0u8; 100];
let mut ethernet : MutableEthernetPacket = ethernet::MutableEthernetPacket::new(&mut bytes).unwrap();
ethernet.set_source(mac);
ethernet.set_destination(mac);

{
    let mut ipv4 = pnet::packet::ipv4::MutableIpv4Packet::new(ethernet.payload_mut()).unwrap();

    ipv4.set_source(source_ip);
    ipv4.set_destination(destination_ip);
    //ipv4.set_header_length(8);
    ipv4.set_dscp(0);

    {
        let mut echo = echo_request::MutableEchoRequestPacket::new(ipv4.payload_mut()).unwrap();

        echo.set_icmp_type(icmp::icmp_types::EchoRequest);
        echo.set_icmp_code(echo_request::icmp_codes::NoCode);
        echo.set_identifier(1);
        echo.set_sequence_number(1);

    }


}

Simple packet composition

Currently, benches/rs_sender.rs is 112 lines long, compared to just 22 of benches/py_sender.py. It is possible to massively reduce the amount of code needed to compose packets in Rust, and this should be done.

Interface to setsockopt

Do you have any plans to expose an interface for setsockopt in TransportSender/TransportReceiver. There is currently no way to join multicast groups or set any other socket parameters, unless I'm missing something.
I'd gladly contribute!

Setting IPv4 flags affect fragment offset on OSX

I have noticed that calling pnet::packet::ipv4::MutableIpv4Packet::set_flags does not affect the flag in the header, but rather the fragment offset on OSX 10.11. These headers are adjacent and I suspect that some bit manipulation is slightly off here.
I have not inspected this in more detail. Any more info I can provide?

No way to find size of ethernet packet that includes the payload

I can only extract the header size or the size of the backing buffer from an ethernet packet, not the size of the header + payload, nor the payload alone. Here is an example:

use pnet::packet::ethernet::MutableEthernetPacket;
use pnet::packet::{PacketSize, FromPacket, Packet};

let mut buffer = (0..100).map(|_| 0).collect::<Vec<u8>>();
{
    let mut eth_packet = MutableEthernetPacket::new(&mut buffer[..]).unwrap();
    let payload = vec![15, 16, 17, 18, 19, 20];
    println!("This prints 20, as expected: {}",
             MutableEthernetPacket::minimum_packet_size() + payload.len());
    eth_packet.set_payload(payload);

    println!("Expected 20, prints 14: {}", eth_packet.packet_size());
    println!("Expected 20, prints 100: {}",
             MutableEthernetPacket::packet_size(&eth_packet.from_packet()));

    println!("Expected 20, prints 100: {}", eth_packet.packet().len());
    println!("Expected 6, prints 86: {}", eth_packet.payload().len());

    println!("I have no idea how to get 20 now when `payload` has been moved");
}
  • MutableEthernetPacket::packet_size(_packet: &Ethernet) -> usize states in the doc that it should give the number of bytes this packet will occupy when converted to a byte array, but it returns the size of the backing buffer.

  • pnet::packet::PacketSize::packet_size(&self) -> usize states in the doc:

    Used to find the calculated size of the packet. This is used for occasions where the underlying buffer is not the same length as the packet itself.

    Then I would expect it to give me the size of the packet, being the header and the payload, but it gives the header length only.

  • The packet(&self) and payload(&self) methods of pnet::packet::Packet gives me the entire backing buffer when I would expect it to limit the slice to the actual data of the packet.

Now to the questions:

  1. Are any or all of these results bugs or did I greatly misunderstand their usage?
  2. Is there any way to get the actual size of the packet, meaning the header + payload (20 in the example code)?
  3. Should maybe one of the packet_size functions be removed or at least renamed?

Overflow when generating packet

use pnet_macros_support::types::*;

mod test {
    #[packet]
    pub struct TestBeOne {
        val3: u3be,
        val6: u6be,
        val7: u7be,
        #[payload]
        payload: Vec<u8>,
    }
}

or run cargo test on this repo: https://github.com/polachok/pnet-test

Process didn't exit successfully: `/tmp/pnet-test/target/debug/build/pnet-test-845c7e9adc594db5/build-script-build` (exit code: 101)
--- stderr
thread '<main>' panicked at 'arithmetic operation overflowed', /home/plhk/.multirust/toolchains/stable/cargo/git/checkouts/libpnet-82bd55c36237b802/master/pnet_macros/src/util.rs:249
note: Run with `RUST_BACKTRACE=1` for a backtrace.

EthernetDataLinkChannelIterator method next() panic: 'called `Option::unwrap()`

I do not know how to reproduce the error exactly, but from time to time, this error occurs:

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', ../src/libcore\option.rs:325
stack backtrace:
   0:     0x7ff745476b7c - std::rt::lang_start::hfe4efe1fc39e4a30
   1:     0x7ff745476189 - std::rt::lang_start::hfe4efe1fc39e4a30
   2:     0x7ff74546897d - std::panicking::rust_panic_with_hook::h983af77c1a2e581b
   3:     0x7ff7454791bb - rust_begin_unwind
   4:     0x7ff74546997f - std::panicking::begin_panic_fmt::hdddb415186c241e7
   5:     0x7ff745478dfc - rust_begin_unwind
   6:     0x7ff74547bcf5 - core::panicking::panic_fmt::hf4e16cb7f0d41a25
   7:     0x7ff74547bff0 - core::panicking::panic::h907815f47e914305
   8:     0x7ff7453a108c - unwrap<(usize, usize)>
                        at C:\bot\slave\nightly-dist-rustc-win-msvc-64\build\src\libcore\macros.rs:21
   9:     0x7ff7453a079e - next
                        at C:\Users\.cargo\registry\src\github.com-1ecc6299db9ec823\pnet-0.10.0\src\datalink\winpcap.rs:279
  10:     0x7ff745375600 - handle
                        at C:\Users\projects\rust\packet_helper\src\main.rs:112
  11:     0x7ff7453712bc - main
                        at C:\Users\projects\rust\packet_helper\src\main.rs:62
  12:     0x7ff745475b7c - std::rt::lang_start::hfe4efe1fc39e4a30
  13:     0x7ff745479251 - _rust_maybe_catch_panic
  14:     0x7ff7454758b4 - std::rt::lang_start::hfe4efe1fc39e4a30
  15:     0x7ff745375c59 - main
  16:     0x7ff745480a6f - __scrt_common_main_seh
                        at f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:255
  17:     0x7ff817128101 - BaseThreadInitThunk
error: Process didn't exit successfully: `target\debug\packet_helper.exe` (exit code: 101)

This is packet_helper\src\main.rs:112 line:

let packet = iter.next().chain_err(|| "Unable to receive packet")?;

This is full code:

fn main() {
    // Find the network interface with the provided name
    let interfaces = get_network_interfaces();
    let interface = interfaces.into_iter()
        .next()
        .ok_or("interface not found".into())
        .unwrap_or_else(|e| exit_with_error(e));

    handle(&interface)
        .chain_err(|| format!("Can not handle packet from interface; interface = {}", &interface.name[..]))
        .unwrap_or_else(|e| exit_with_error(e));
}

fn handle(interface: &NetworkInterface) -> Result<()> {
    // Create a new channel, dealing with layer 2 packets
    let channel = datalink::channel(interface, &Default::default()).chain_err(|| "An error occurred when creating the datalink channel")?;
    let (mut _tx, mut rx) = match channel {
        Ethernet(tx, rx) => (tx, rx),
        _ => return Err("Unhandled channel type".into()),
    };

    let mut iter = rx.iter();
    loop {
        let packet = iter.next().chain_err(|| "Unable to receive packet")?;
        handle_packet(&interface.name[..], &packet)?;
    }
}

fn handle_packet(interface_name: &str, ethernet: &EthernetPacket) -> Result<()> {
    match ethernet.get_ethertype() {
        EtherTypes::Ipv4 => handle_ipv4_packet(interface_name, ethernet),
        _ => {
            trace!("[{}]: Not supported Ethernet packet: {} > {}; ethertype: {:?} length: {}",
                   interface_name,
                   ethernet.get_source(),
                   ethernet.get_destination(),
                   ethernet.get_ethertype(),
                   ethernet.packet().len());
            Ok(())
        }
    }
}

fn handle_ipv4_packet(interface_name: &str, ethernet: &EthernetPacket) -> Result<()> {
    let header = Ipv4Packet::new(ethernet.payload()).ok_or("Malformed IPv4 Packet")?;
    handle_transport_protocol(interface_name,
                              IpAddr::V4(header.get_source()),
                              IpAddr::V4(header.get_destination()),
                              header.get_next_level_protocol(),
                              header.payload())
}

fn handle_transport_protocol(interface_name: &str, source: IpAddr, destination: IpAddr, protocol: IpNextHeaderProtocol, packet: &[u8]) -> Result<()> {
    match protocol {
        IpNextHeaderProtocols::Tcp => handle_tcp_packet(interface_name, source, destination, packet),
        _ => {
            trace!("[{}]: Not supported transport packet: {} > {}; protocol: {:?} length: {}",
                   interface_name,
                   source,
                   destination,
                   protocol,
                   packet.len());
            Ok(())
        }
    }
}

fn handle_tcp_packet(interface_name: &str, source: IpAddr, destination: IpAddr, packet: &[u8]) -> Result<()> {
    let tcp = TcpPacket::new(packet).ok_or("Malformed TCP Packet")?;

...

    Ok(())
}

OS: Windows 10
rustc 1.11.0-nightly (1ab87b65a 2016-07-02)

remove code duplication for rfc1071 checksum

I noticed there is a checksum function for IPv4 and for UDP... and they are nearly identical... I am looking into writing a checksum helper function for TCP... and but instead all three of these protocols should share the same checksum function.

Here's an example of good abstraction for providing checksums for all these protocols:
https://github.com/google/gopacket/blob/master/layers/tcpip.go

That is... the checksum helper function for each protocol becomes a thin shim who is only responsible for gathering the header fields into a buffer. Then the checksum function operates on this buffer and returns the resulting checksum value.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.