]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
af-packet: Ignore outgoing packets on loopback interfaces
authorArne Welzel <arne.welzel@corelight.com>
Wed, 29 Mar 2023 15:29:31 +0000 (17:29 +0200)
committerVictor Julien <vjulien@oisf.net>
Fri, 5 May 2023 08:21:07 +0000 (10:21 +0200)
When reading a loopback interface, packets are received twice: Once as
outgoing packets and once as incoming packets.

Libpcap ignores outgoing packets. With current versions of Suricata, sniffing
a single http://localhost:80 request over lo using the af-packet source
minimally shows two syn packets, two synacks and twice as many packets in
the stats entries than you'd expect when running tcpdump or Wireshark.

src/source-af-packet.c

index 01efea348f0a0df0b0c5cdaf380eb68bca402f5b..f442e079bf73ae15b1d817c74763a3ed30cce2b0 100644 (file)
@@ -327,6 +327,9 @@ typedef struct AFPThreadVars_
 
     int promisc;
 
+    /* bitmask of ignored ssl_pkttypes */
+    uint32_t pkttype_filter_mask;
+
     int down_count;
 
     uint16_t cluster_id;
@@ -853,6 +856,25 @@ static inline int AFPReadFromRingWaitForPacket(AFPThreadVars *ptv)
     return AFP_READ_OK;
 }
 
+/**
+ * \brief AF packet frame ignore logic
+ *
+ * Given a sockaddr_ll of a frame, use the pkttype_filter_mask to decide if the
+ * frame should be ignored. Protect from undefined behavior if there's ever
+ * a sll_pkttype that would shift by too much. At this point, only outgoing
+ * packets (4) are ignored. The highest value in if_linux.h is PACKET_KERNEL (7),
+ * this extra check is being overly cautious.
+ *
+ * \retval true if the frame should be ignored
+ */
+static inline bool AFPShouldIgnoreFrame(AFPThreadVars *ptv, const struct sockaddr_ll *sll)
+{
+    if (unlikely(sll->sll_pkttype > 31))
+        return false;
+
+    return (ptv->pkttype_filter_mask & BIT_U32(sll->sll_pkttype)) != 0;
+}
+
 /**
  * \brief AF packet read function for ring
  *
@@ -898,6 +920,12 @@ static int AFPReadFromRing(AFPThreadVars *ptv)
             goto next_frame;
         }
 
+        const struct sockaddr_ll *sll =
+                (const struct sockaddr_ll *)((uint8_t *)h.h2 +
+                                             TPACKET_ALIGN(sizeof(struct tpacket2_hdr)));
+        if (unlikely(AFPShouldIgnoreFrame(ptv, sll)))
+            goto next_frame;
+
         Packet *p = PacketGetFromQueueOrAlloc();
         if (p == NULL) {
             return AFPSuriFailure(ptv, h);
@@ -990,6 +1018,12 @@ static inline int AFPWalkBlock(AFPThreadVars *ptv, struct tpacket_block_desc *pb
     uint8_t *ppd = (uint8_t *)pbd + pbd->hdr.bh1.offset_to_first_pkt;
 
     for (int i = 0; i < num_pkts; ++i) {
+        const struct sockaddr_ll *sll =
+                (const struct sockaddr_ll *)(ppd + TPACKET_ALIGN(sizeof(struct tpacket3_hdr)));
+        if (unlikely(AFPShouldIgnoreFrame(ptv, sll))) {
+            ppd = ppd + ((struct tpacket3_hdr *)ppd)->tp_next_offset;
+            continue;
+        }
         int ret = AFPParsePacketV3(ptv, pbd, (struct tpacket3_hdr *)ppd);
         switch (ret) {
             case AFP_READ_OK:
@@ -1877,6 +1911,10 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose)
         goto socket_err;
     }
 
+    /* ignore outgoing packets on loopback interfaces */
+    if (if_flags & IFF_LOOPBACK)
+        ptv->pkttype_filter_mask |= BIT_U32(PACKET_OUTGOING);
+
     if (ptv->promisc != 0) {
         /* Force promiscuous mode */
         memset(&sock_params, 0, sizeof(sock_params));