]> The Tcpdump Group git mirrors - libpcap/commitdiff
In memory-mapped mode, don't release the packet as soon as the callback
authorGuy Harris <[email protected]>
Tue, 24 Mar 2009 06:18:25 +0000 (23:18 -0700)
committerGuy Harris <[email protected]>
Tue, 24 Mar 2009 06:18:25 +0000 (23:18 -0700)
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.

pcap-int.h
pcap-linux.c

index a54f382bd090b0125fcb4a399387552fa6b515b4..d5d310b016834260bb079ab13640de6fb4daabb5 100644 (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
index 9314eec13544193ebc14fa391690abf771e6b1ad..f35d459656154955376270826d7f8aa58cdf05b9 100644 (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;