]> git.ipfire.org Git - people/ms/suricata.git/commitdiff
af-packet: refactor VLAN hdr handling
authorVictor Julien <victor@inliniac.net>
Thu, 4 Nov 2021 09:00:17 +0000 (10:00 +0100)
committerVictor Julien <vjulien@oisf.net>
Mon, 22 Nov 2021 12:56:07 +0000 (13:56 +0100)
Update the packet payload after decode, instead of during IPS send.

This means the updates happen in the capture thread, and the VLAN header
is available to logging as well.

Ticket: #4805.

src/source-af-packet.c
src/source-af-packet.h

index 3012f618633d276d4d7da20f81a9c8b3a43ba683..aeb9e4bae0fa39fb9ea2996094b45fdf6f5075f3 100644 (file)
@@ -632,10 +632,6 @@ static TmEcode AFPWritePacket(Packet *p, int version)
 {
     struct sockaddr_ll socket_address;
     int socket;
-    uint8_t *pstart;
-    size_t plen;
-    union thdr h;
-    uint16_t vlan_tci = 0;
 
     if (p->afp_v.copy_mode == AFP_COPY_MODE_IPS) {
         if (PacketTestAction(p, ACTION_DROP)) {
@@ -662,42 +658,8 @@ static TmEcode AFPWritePacket(Packet *p, int version)
         SCMutexLock(&p->afp_v.peer->sock_protect);
     socket = SC_ATOMIC_GET(p->afp_v.peer->socket);
 
-    h.raw = p->afp_v.relptr;
-
-    if (version == TPACKET_V2) {
-        /* Copy VLAN header from ring memory. For post june 2011 kernel we test
-         * the flag. It is not defined for older kernel so we go best effort
-         * and test for non zero value of the TCI header. */
-        if (h.h2->tp_status & TP_STATUS_VLAN_VALID || h.h2->tp_vlan_tci) {
-            vlan_tci = h.h2->tp_vlan_tci;
-        }
-    } else {
-#ifdef HAVE_TPACKET_V3
-        if (h.h3->tp_status & TP_STATUS_VLAN_VALID || h.h3->hv1.tp_vlan_tci) {
-            vlan_tci = h.h3->hv1.tp_vlan_tci;
-        }
-#else
-        /* Should not get here */
-        BUG_ON(1);
-#endif
-    }
-
-    if (vlan_tci != 0) {
-        pstart = GET_PKT_DATA(p) - VLAN_HEADER_LEN;
-        plen = GET_PKT_LEN(p) + VLAN_HEADER_LEN;
-        /* move ethernet addresses */
-        memmove(pstart, GET_PKT_DATA(p), 2 * ETH_ALEN);
-        /* write vlan info */
-        *(uint16_t *)(pstart + 2 * ETH_ALEN) = htons(0x8100);
-        *(uint16_t *)(pstart + 2 * ETH_ALEN + 2) = htons(vlan_tci);
-    } else {
-        pstart = GET_PKT_DATA(p);
-        plen = GET_PKT_LEN(p);
-    }
-
-    if (sendto(socket, pstart, plen, 0,
-               (struct sockaddr*) &socket_address,
-               sizeof(struct sockaddr_ll)) < 0) {
+    if (sendto(socket, GET_PKT_DATA(p), GET_PKT_LEN(p), 0, (struct sockaddr *)&socket_address,
+                sizeof(struct sockaddr_ll)) < 0) {
         SCLogWarning(SC_ERR_SOCKET, "Sending packet failed on socket %d: %s",
                   socket,
                   strerror(errno));
@@ -810,6 +772,7 @@ static void AFPReadFromRingSetupPacket(
             (tp_status & TP_STATUS_VLAN_VALID || h.h2->tp_vlan_tci)) {
         p->vlan_id[0] = h.h2->tp_vlan_tci & 0x0fff;
         p->vlan_idx = 1;
+        p->afp_v.vlan_tci = h.h2->tp_vlan_tci;
     }
 
     (void)PacketSetData(p, (unsigned char *)h.raw + h.h2->tp_mac, h.h2->tp_snaplen);
@@ -977,6 +940,7 @@ static inline int AFPParsePacketV3(AFPThreadVars *ptv, struct tpacket_block_desc
             (ppd->tp_status & TP_STATUS_VLAN_VALID || ppd->hv1.tp_vlan_tci)) {
         p->vlan_id[0] = ppd->hv1.tp_vlan_tci & 0x0fff;
         p->vlan_idx = 1;
+        p->afp_v.vlan_tci = ppd->hv1.tp_vlan_tci;
     }
 
     (void)PacketSetData(p, (unsigned char *)ppd + ppd->tp_mac, ppd->tp_snaplen);
@@ -1691,19 +1655,14 @@ static int AFPSetupRing(AFPThreadVars *ptv, char *devname)
     }
 #endif
 
-    /* Let's reserve head room so we can add the VLAN header in IPS
-     * or TAP mode before write the packet */
-    if (ptv->copy_mode != AFP_COPY_MODE_NONE) {
-        /* Only one vlan is extracted from AFP header so
-         * one VLAN header length is enough. */
-        int reserve = VLAN_HEADER_LEN;
-        if (setsockopt(ptv->socket, SOL_PACKET, PACKET_RESERVE, (void *) &reserve,
-                    sizeof(reserve)) < 0) {
-            SCLogError(SC_ERR_AFP_CREATE,
-                    "Can't activate reserve on packet socket: %s",
-                    strerror(errno));
-            return AFP_FATAL_ERROR;
-        }
+    /* Reserve head room for a VLAN header. One vlan is extracted from AFP header
+     * so one VLAN header length is enough. */
+    int reserve = VLAN_HEADER_LEN;
+    if (setsockopt(ptv->socket, SOL_PACKET, PACKET_RESERVE, (void *)&reserve, sizeof(reserve)) <
+            0) {
+        SCLogError(
+                SC_ERR_AFP_CREATE, "Can't activate reserve on packet socket: %s", strerror(errno));
+        return AFP_FATAL_ERROR;
     }
 
     /* Allocate RX ring */
@@ -2681,6 +2640,34 @@ TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data)
     SCReturnInt(TM_ECODE_OK);
 }
 
+/** \internal
+ *  \brief add a VLAN header into the raw data for inspection, logging
+ *         and sending out in IPS mode
+ *
+ *  The kernel doesn't provide the first VLAN header the raw packet data,
+ *  but instead feeds it to us through meta data. For logging and IPS
+ *  we need to put it back into the raw data. Luckily there is some head
+ *  room in the original data so its enough to move the ethernet header
+ *  a bit to make space for the VLAN header.
+ */
+static void UpdateRawDataForVLANHdr(Packet *p)
+{
+    if (p->afp_v.vlan_tci != 0) {
+        uint8_t *pstart = GET_PKT_DATA(p) - VLAN_HEADER_LEN;
+        size_t plen = GET_PKT_LEN(p) + VLAN_HEADER_LEN;
+        /* move ethernet addresses */
+        memmove(pstart, GET_PKT_DATA(p), 2 * ETH_ALEN);
+        /* write vlan info */
+        *(uint16_t *)(pstart + 2 * ETH_ALEN) = htons(0x8100);
+        *(uint16_t *)(pstart + 2 * ETH_ALEN + 2) = htons(p->afp_v.vlan_tci);
+
+        /* update the packet raw data pointer to start at the new offset */
+        (void)PacketSetData(p, pstart, plen);
+        /* update ethernet header pointer to point to the new start of the data */
+        p->ethh = (void *)pstart;
+    }
+}
+
 /**
  * \brief This function passes off to link type decoders.
  *
@@ -2694,6 +2681,8 @@ TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data)
 TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data)
 {
     SCEnter();
+
+    const bool afp_vlan_hdr = p->vlan_idx != 0;
     DecodeThreadVars *dtv = (DecodeThreadVars *)data;
 
     DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p));
@@ -2701,13 +2690,13 @@ TmEcode DecodeAFP(ThreadVars *tv, Packet *p, void *data)
     /* update counters */
     DecodeUpdatePacketCounters(tv, dtv, p);
 
-    /* If suri has set vlan during reading, we increase vlan counter */
-    if (p->vlan_idx) {
-        StatsIncr(tv, dtv->counter_vlan);
-    }
-
     /* call the decoder */
     DecodeLinkLayer(tv, dtv, p->datalink, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
+    /* post-decoding put vlan hdr back into the raw data) */
+    if (afp_vlan_hdr) {
+        StatsIncr(tv, dtv->counter_vlan);
+        UpdateRawDataForVLANHdr(p);
+    }
 
     PacketDecodeFinalize(tv, dtv, p);
 
index 4065699130a8090baa98aeea4199588edd678549..e8929f40cc36a0e33ed025c49a371db053ad20c5 100644 (file)
@@ -148,6 +148,7 @@ typedef struct AFPPacketVars_
      */
     AFPPeer *mpeer;
     uint8_t copy_mode;
+    uint16_t vlan_tci;
 #ifdef HAVE_PACKET_EBPF
     int v4_map_fd;
     int v6_map_fd;
@@ -156,21 +157,25 @@ typedef struct AFPPacketVars_
 } AFPPacketVars;
 
 #ifdef HAVE_PACKET_EBPF
-#define AFPV_CLEANUP(afpv) do {           \
-    (afpv)->relptr = NULL;                \
-    (afpv)->copy_mode = 0;                \
-    (afpv)->peer = NULL;                  \
-    (afpv)->mpeer = NULL;                 \
-    (afpv)->v4_map_fd = -1;               \
-    (afpv)->v6_map_fd = -1;               \
-} while(0)
+#define AFPV_CLEANUP(afpv)                                                                         \
+    do {                                                                                           \
+        (afpv)->relptr = NULL;                                                                     \
+        (afpv)->copy_mode = 0;                                                                     \
+        (afpv)->vlan_tci = 0;                                                                      \
+        (afpv)->peer = NULL;                                                                       \
+        (afpv)->mpeer = NULL;                                                                      \
+        (afpv)->v4_map_fd = -1;                                                                    \
+        (afpv)->v6_map_fd = -1;                                                                    \
+    } while (0)
 #else
-#define AFPV_CLEANUP(afpv) do {           \
-    (afpv)->relptr = NULL;                \
-    (afpv)->copy_mode = 0;                \
-    (afpv)->peer = NULL;                  \
-    (afpv)->mpeer = NULL;                 \
-} while(0)
+#define AFPV_CLEANUP(afpv)                                                                         \
+    do {                                                                                           \
+        (afpv)->relptr = NULL;                                                                     \
+        (afpv)->copy_mode = 0;                                                                     \
+        (afpv)->vlan_tci = 0;                                                                      \
+        (afpv)->peer = NULL;                                                                       \
+        (afpv)->mpeer = NULL;                                                                      \
+    } while (0)
 #endif
 
 /**