From 51aef3c2303eec7a93755a5512f0dc715ae91780 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Wed, 29 Mar 2023 17:29:31 +0200 Subject: [PATCH] af-packet: Ignore outgoing packets on loopback interfaces 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 | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/source-af-packet.c b/src/source-af-packet.c index 01efea348f..f442e079bf 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -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));