Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is pcap_next ignoring timeout? #572

Closed
ardikars opened this issue Mar 23, 2017 · 14 comments
Closed

Is pcap_next ignoring timeout? #572

ardikars opened this issue Mar 23, 2017 · 14 comments

Comments

@ardikars
Copy link

pcap_t *p = pcap_open_live("eth0", 65535, 1, 500. errbuf);

u_char *buf = pcap_next(p, pkt_hdr);

in libpcap 1.7.4, if after 500 ms no packet is captured it's return NULL, in libpcap 1.8.1 pcap_next still waiting next packet.

@infrastation
Copy link
Member

If this is a modern Linux, then the usual cause of this difference between old and new libpcap is that new libpcap uses TPACKET_V3 if it is available. This results in less overhead work but involves deeper buffers. A side effect of this is that the timeout may mean the amount of time after the first packet, not before. Please see "packet buffer timeout" in this man page for explanation. If you need to receive packets without the delay, pcap_set_immediate_mode() is the function, you can see how it works in the tcpdump source code.

Let us know if this resolves your issue.

@beinhaerter
Copy link

I ran into the same problem. Replacing pcap_open_live with a combination of pcap_create, pcap_set_snaplen, pcap_set_promisc, pcap_set_timeout, pcap_set_immediate_mode and pcap_activate works.
Nevertheless either pcap_open_live should be fixed to work as it did before or it should be removed or at least in the h file be marked as deprecated (using compiler directives).

@guyharris
Copy link
Member

I ran into the same problem. Replacing pcap_open_live with a combination of pcap_create, pcap_set_snaplen, pcap_set_promisc, pcap_set_timeout, pcap_set_immediate_mode and pcap_activate works.

It's the pcap_set_immediate_mode() that's making a difference there.

Nevertheless either pcap_open_live should be fixed to work as it did before or it should be removed or at least in the h file be marked as deprecated (using compiler directives).

To quote the pcap(3) man page:

   packet buffer timeout
          If,  when  capturing,  packets  are  delivered  as  soon as they
          arrive, the application capturing the packets will be  woken  up
          for  each  packet  as  it arrives, and might have to make one or
          more calls to the operating system to fetch each packet.

          If, instead, packets are not delivered as soon as  they  arrive,
          but  are  delivered after a short delay (called a "packet buffer
          timeout"), more than one packet can be  accumulated  before  the
          packets are delivered, so that a single wakeup would be done for
          multiple packets, and each set of calls made  to  the  operating
          system  would  supply  multiple  packets,  rather  than a single
          packet.  This reduces the per-packet CPU overhead if packets are
          arriving  at  a  high rate, increasing the number of packets per
          second that can be captured.

          The packet buffer timeout is required  so  that  an  application
          won't  wait for the operating system's capture buffer to fill up
          before packets are delivered; if packets  are  arriving  slowly,
          that wait could take an arbitrarily long period of time.

          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 time-
          out, 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  unpre-
          dictable.

          NOTE:  the  packet  buffer timeout cannot be used to cause calls
          that read packets to return within a  limited  period  of  time,
          because, on some platforms, the packet buffer timeout isn't sup-
          ported, and, on other platforms, the timer doesn't  start  until
          at  least one packet arrives.  This means that the packet buffer
          timeout should NOT be  used,  for  example,  in  an  interactive
          application  to  allow  the  packet capture loop to ``poll'' for
          user input periodically, as there's no  guarantee  that  a  call
          reading packets will return after the timeout expires even if no
          packets have arrived.

          The packet buffer timeout is set with pcap_set_timeout().

There has never been a guarantee that pcap_dispatch(), pcap_loop(), pcap_next(), and pcap_next_ex() wouldn't block indefinitely until at least one packet arrives. Whether that happens is platform-dependent, and, for better or worse, Linux, with TPACKET_V3, is currently a platform where it does.

@manufohrer
Copy link

manufohrer commented Jan 17, 2020

Hi, i ran into the same problem. Replacing pcap_open_live with a combination of pcap_create, pcap_set_snaplen, pcap_set_promisc, pcap_set_timeout, pcap_set_immediate_mode and pcap_activate works.

Nevertheless either pcap_open_live should be fixed to work as it did before or it should be removed or at least in the h file be marked as deprecated (using compiler directives).

Hi, i got the same problem and also tried your combination, but that didn't solve it. May you or someone else post the code how you create that pointer? Maybe some of my overloads are not correct.

Cheers Manu

@guyharris
Copy link
Member

Hi, i ran into the same problem. Replacing pcap_open_live with a combination of ... pcap_set_immediate_mode ... works.

If you set immediate mode, packets are delivered, well, immediately - no timeout, no delay, nothing.

This also means that you be more likely to drop packets.

@guyharris
Copy link
Member

As I noted, there is no documented guarantee that the timer will go off if no packets have arrived, so not a bug.

@beinhaerter
Copy link

@guyharris So what's the use of pcap_open_live if timeouts are not respected and you run into trouble if no packet arrives? pcap_set_immediate_mode cannot be used after pcap_open_live, so I fail to see how pcap_open_live can be used in a safe manner. Can you please elaborate?

@manufohrer
Copy link

manufohrer commented Jan 19, 2020

@beinhaerter i got that timeout problem solved now with using other functions than open_live.

In my case it worked with pcap_create, pcap_set_snaplen, pcap_set_promisc, pcap_set_timeout, pcap_set_immediate_mode, pcap_setnonblocking and pcap_activate.

@guyharris i'm not your opinion. If open_live provides a timeoutfunction, then it should work. If it leads into killing the whole program, because there is no return, then it is a bug.

@guyharris
Copy link
Member

@guyharris So what's the use of pcap_open_live if timeouts are not respected

They are respected.

They just don't do what you are expecting them to do.

You're expecting the timeout to occur whether or not there are packets available.

That's not how the timeout in Linux TPACKET_V3 works.

The main purpose of timeouts in packet capture mechanisms is to allow the capture mechanism to buffer up multiple packets, and deliver multiple packets in a single wakeup, rather than one wakeup per packet, reducing the number of wakeups (which aren't free), without causing indefinitely-long waits for a packet to be delivered.

If packets are delivered only when the packet buffer is full, then, if the buffer is large enough to hold more than one packet, the amount of time between the arrival of a packet and its delivery to the capturing program is unbounded above - the first packet arrives, but it doesn't completely fill the buffer, and it won't be delivered until the buffer is full, so it won't be delivered until another packet arrives, and there's no guarantee, in all cases, that this will happen within any given period of time.

So a timeout was added - if the buffer isn't full, it'll be delivered when the timeout expires.

Whether the buffer is delivered if it's empty is not guaranteed. Some packet capture mechanisms, such as the BPF packet capture mechanism used in *BSD, macOS, AIX, and Solaris 11, and the mechanisms in the WinPcap and Npcap drivers on Windows, will deliver an empty buffer. Other packet capture mechanisms, such as (as I remember from when I last checked) DLPI+bufmod mechanism in Solaris 10 and earlier, the non-memory-mapped mechanism in Linux (which was originally the only mechanism in Linux), and the TPACKET_V3 capture mechanism in Linux, don't deliver empty buffers.

So, given that libpcap runs atop many packet capture mechanisms, it makes no guarantee that the timeout is a timeout that puts an upper bound on how long pcap_dispatch(), pcap_next(), or pcap_next_ex() will block. It only makes a guarantee that the time between the arrival of a packet and the deliver of the packet via one of those routines has the timeout as the upper bound.

and you run into trouble if no packet arrives?

Why is your program running into trouble if no packet arrives? Presumably that's because it's doing something other than processing captured packets, and needs to do that every so often.

If so, that's no different from, for example, a program that's reading a stream of data from a TCP socket and also doing something other than processing that data; those programs would typically either use a mechanism such as select() to multiplex network and other input, or do the network input in another thread.

pcap_set_immediate_mode cannot be used after pcap_open_live, so I fail to see how pcap_open_live can be used in a safe manner.

It can be used in a safe manner by assuming that pcap_dispatch() or pcap_next() or pcap_next_ex() may block for an indefinite amount of time and writing code so that the program won't be in trouble if they do.

@guyharris
Copy link
Member

@beinhaerter i got that timeout problem solved now with using other functions than open_live.

In my case it worked with pcap_create, pcap_set_snaplen, pcap_set_promisc, pcap_set_timeout, pcap_set_immediate_mode, pcap_setnonblocking and pcap_activate.

In non-blocking mode, pcap_dispatch(), pcap_next(), and pcap_next_ex() will always return immediately (if not, that's a bug), regardless of whether the pcap_t is in immediate mode or not - that's what non-blocking mode means. However, if you're using non-blocking mode, either you're spinning the CPU or you're using some other call to wait for packets to arrive - see my previous comment about using select() or some other mechanism to multiplex network and other input.

@guyharris i'm not your opinion. If open_live provides a timeoutfunction, then it should work.

It does work, as per the libpcap man page I quoted above. It just doesn't work the way you expect it to work; see the "NOTE" section in the quoted man page.

If it leads into killing the whole program, because there is no return, then it is a bug.

It's a bug in the program, because it's assuming behavior not guaranteed by the library.

@kayoub5
Copy link

kayoub5 commented Jun 7, 2020

@guyharris Your latest comment contradict with the man pages,

In non-blocking mode, pcap_dispatch(), pcap_next(), and pcap_next_ex() will always return immediately

This means that pcap_next could be used in non-blocking mode, however the docs indicates in pcap_setnonblock

pcap_loop(3PCAP) and pcap_next(3PCAP) will not work in ``non-blocking'' mode.

Could you clarify the discrepancy here?

@guyharris
Copy link
Member

@guyharris Your latest comment contradict with the man pages,

In non-blocking mode, pcap_dispatch(), pcap_next(), and pcap_next_ex() will always return immediately

This means that pcap_next could be used in non-blocking mode, however the docs indicates in pcap_setnonblock

pcap_loop(3PCAP) and pcap_next(3PCAP) will not work in ``non-blocking'' mode.

Could you clarify the discrepancy here?

I should clarify what "will not work" means in the pcap_setnonblock() man page.

To quote the pcap_next()/pcap_next_ex() man page:

   pcap_next() returns a pointer  to  the  packet  data  on  success,  and
   returns  NULL  if  an error occurred, or if no packets were read from a
   live capture (if, for example, they were discarded because they  didn't
   pass  the  packet  filter,  or  if,  on platforms that support a packet
   buffer timeout that starts  before  any  packets  arrive,  the  timeout
   expires  before  any  packets arrive, or if the file descriptor for the
   capture device is in non-blocking mode and no packets were available to
   be read), or if no more packets are available in a ``savefile.'' Unfor-
   tunately, there is no way to determine whether  an  error  occurred  or
   not.

If you use pcap_next() in non-blocking mode, it will return NULL if there isn't a packet immediately available.

It will also return NULL if an error occurred.

And there's no way to determine which was the case.

I might be tempted simply to say "pcap_next() will not work" in its man page and leave it at that. :-) It's not a very good API. You really want to use pcap_next_ex(), because it distinguishes between "no packet" and "error" - 0 is returned for "no packet", and PCAP_ERROR (-1) is returned for "error".

(And for pcap_loop(), it'll just spin, eating up CPU time, in non-blocking mode; the right way to do the equivalent of pcap_loop() would be to call pcap_dispatch() when you know that there will be packets to process.)

@guyharris
Copy link
Member

I should clarify what "will not work" means in the pcap_setnonblock() man page.

Done in 7b8c17a.

@infrastation
Copy link
Member

For posterity, there is now a FAQ entry about this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

6 participants