dect
/
libpcap
Archived
13
0
Fork 0

In memory-mapped mode, don't release the packet as soon as the callback

finishes processing the packet; in some cases, such as pcap_next() and
pcap_next_ex(), the packet data is expected to be available after the
callback returns, and only discarded when the next packet is read.
This commit is contained in:
Guy Harris 2009-03-23 23:18:25 -07:00
parent f5af8a7ce4
commit 54ef309e92
2 changed files with 61 additions and 11 deletions

View File

@ -134,6 +134,7 @@ struct pcap_md {
bpf_u_int32 oldmode; /* mode to restore when turning monitor mode off */
u_int tp_version; /* version of tpacket_hdr for mmaped ring */
u_int tp_hdrlen; /* hdrlen of tpacket_hdr for mmaped ring */
union thdr prev_pkt; /* previous packet handed to the callback */
#endif /* linux */
#ifdef HAVE_DAG_API

View File

@ -2216,6 +2216,24 @@ pcap_get_ring_frame(pcap_t *handle, int status)
return h.raw;
}
static inline void
pcap_release_previous_ring_frame(pcap_t *handle)
{
if (handle->prev_pkt.raw != NULL) {
switch (handle->md.tp_version) {
case TPACKET_V1:
handle->prev_pkt.h1->tp_status = TP_STATUS_KERNEL;
break;
#ifdef HAVE_TPACKET2
case TPACKET_V2:
handle->prev_pkt.h2->tp_status = TP_STATUS_KERNEL;
break;
#endif
}
handle->prev_pkt.raw = NULL;
}
}
static int
pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback,
u_char *user)
@ -2263,10 +2281,44 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback,
unsigned int tp_sec;
unsigned int tp_usec;
/*
* Check for break loop condition; a callback might have
* set it.
*/
if (handle->break_loop) {
handle->break_loop = 0;
return -2;
}
h.raw = pcap_get_ring_frame(handle, TP_STATUS_USER);
if (!h.raw)
break;
/*
* We have a packet; release the previous packet,
* if any.
*
* Libpcap has never guaranteed that, if we get a
* packet from the underlying packet capture
* mechanism, the data passed to callbacks for
* any previous packets is still valid. It did
* implicitly guarantee that the data will still
* be available after the callback returns, by
* virtue of implementing pcap_next() by calling
* pcap_dispatch() with a count of 1 and a callback
* that fills in a structure with a pointer to
* the packet data, meaning that pointer is
* expected to point to valid data after the
* callback returns and pcap_next() returns,
* so we can't release the packet when the
* callback returns.
*
* Therefore, we remember the packet that
* needs to be released after handing it
* to the callback, and release it up here.
*/
pcap_release_previous_ring_frame(handle);
switch (handle->md.tp_version) {
case TPACKET_V1:
tp_len = h.h1->tp_len;
@ -2417,17 +2469,14 @@ pcap_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback,
handle->md.packets_read++;
skip:
/* next packet */
switch (handle->md.tp_version) {
case TPACKET_V1:
h.h1->tp_status = TP_STATUS_KERNEL;
break;
#ifdef HAVE_TPACKET2
case TPACKET_V2:
h.h2->tp_status = TP_STATUS_KERNEL;
break;
#endif
}
/*
* As per the comment above, we can't yet release this
* packet, even though the callback has returned, as
* some users of pcap_loop() and pcap_dispatch() - such
* as pcap_next() and pcap_next_ex() - expect the packet
* to be available until the next pcap_dispatch() call.
*/
handle->prev_pkt = h;
if (++handle->offset >= handle->cc)
handle->offset = 0;