rust-pcap / pcap

Rust language pcap library
Apache License 2.0
595 stars 138 forks source link

Timeout not working #289

Closed nimcomputing closed 1 year ago

nimcomputing commented 1 year ago

Hi,

I'm having an issue trying to capture packets because it appears that next_packet() isn't honoring the given timeout (or lack thereof).

Here's the relevant section of my code

    let capture_device_result = Capture::from_device(device.unwrap().to_owned()).unwrap().promisc(true).timeout(0).open();
    if capture_device_result.is_err() {
        error!("Unable to open a handle to the network interface with GUID {}", network_interface.guid);
        return Err("Unable to open a handle to the network interface detailed in config");
    }

    let mut capture_device = capture_device_result.unwrap();
    let filter_result = capture_device.filter("ip and udp and udp port 67", true);
    if filter_result.is_err() {
        warn!("Unable to compile capture filter: {}", filter_result.unwrap_err());
        return Err("Failed to set the capture filter");
    }

    while let Ok(packet) = capture_device.next_packet() {
        println!("received packet! {:?}", packet);
    }

It doesn't seem to make any difference whether or not I set .timeout() on the inactive capture. I've also tried setting a timeout to a high value (like 50000), but next_packet() always returns immediately.

This is being ran on Windows and I've tried using both WinPCAP and Win10PCAP, and both result in the same issue. The timeout works when using these libraries directly in a C++ app.

Can you help please?

Wojtek242 commented 1 year ago

Hi @nimcomputing. Apologies for the late response.

The timeout works when using these libraries directly in a C++ app.

Could you provide the code snippet for this? That way I can compare the underlying C++ calls to the ones this crate is making.

robs-zeynet commented 1 year ago

FYI: OS by OS, the timeout value is treated differently or even ignored.

See the libpcap man page, specifically:

https://www.tcpdump.org/manpages/pcap.3pcap.html  under
"packet buffer timeout"

"Not all platforms support a packet buffer timeout; on platforms that don't, the packet buffer timeout is ignored. A zero value for the timeout, on platforms that support a packet buffer timeout, will cause a read to wait forever to allow enough packets to arrive, with no timeout. A negative value is invalid; the result of setting the timeout to a negative value is unpredictable."

It works out that the OS's where the timeout doesn't (Linux, MacOS) do what you want are also the OS's where you can use poll() or similar to implement the timeout yourself.

Fwiw, I've been working with pcap for 25+ years and I forget this and rerun into this problem every 10 years :-)

mmarkmos commented 1 year ago

I don't know if this falls under the same category. But I am getting a pcap::Error::TimeoutExpired at seemingly random times. I opened my capture with

    let cap = match Capture::from_device(device) {
        Ok(cap) => cap,
        Err(e) => panic!("Failed to create capture with Error {:?}", e),
    };

    let cap = cap.promisc(true).immediate_mode(true).timeout(0);

From my understanding and by searching the repository for TimeoutExpired, the only place where it is thrown is PacketStream::poll_next . As I never call stream on the Capture, I am confused as to how it gets thrown.

Interestingly, on

Does anyone know why ?

robs-zeynet commented 1 year ago

My guess is that because Linux already ignores the packet timeout, that the .timeout(0) does nothing and you're getting the timeout not from the rust pcap but actually from the underlying C call to pcap_next_ex(). It's not much consolation, but rust-pcap (this library) is really only a light wrapper over the venerable libpcap C library which tries really hard to be OS agnostic, but basically cannot due to all of these quirks. I would handle this like errno::EINT - something that you have to accept will trigger somewhat randomly and when it does, just retry. Not sure how helpful that is but best of luck :-)