From: Guy Harris Date: Tue, 24 Mar 2009 06:18:25 +0000 (-0700) Subject: In memory-mapped mode, don't release the packet as soon as the callback X-Git-Tag: libpcap-1.1.0~190 X-Git-Url: https://git.tcpdump.org/libpcap/commitdiff_plain/54ef309e921c11a4e80cd7a26d9e25d30c833e14 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. --- diff --git a/pcap-int.h b/pcap-int.h index a54f382b..d5d310b0 100644 --- a/pcap-int.h +++ b/pcap-int.h @@ -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 diff --git a/pcap-linux.c b/pcap-linux.c index 9314eec1..f35d4596 100644 --- a/pcap-linux.c +++ b/pcap-linux.c @@ -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;