]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
af-packet: add VLAN header when needed in IPS mode
authorEric Leblond <eric@regit.org>
Tue, 1 Nov 2016 21:09:31 +0000 (22:09 +0100)
committerVictor Julien <victor@inliniac.net>
Wed, 8 Feb 2017 10:21:29 +0000 (11:21 +0100)
When packet is coming from a real ethernet card, the kernel is
stripping the vlan header and delivering a modified packet so
we need to insert the VLAN header back before sending the packet
on the wire.

To do so, we pass an option to the raw socket to add a reserve
before the packet data. It will get Suricata some head room to
to move the ethernet addresses before there actual place and
and insert the VLAN header in the correct place.

We get VLAN info from the ring buffer as the call of AFPWrite is
always done in the release function so we still have access to the
memory.

src/source-af-packet.c

index da80ea324ca67c5aa23613d55bd1ecdc3585825d..289b92f58d62e9a7a190f955b28a1de543f87270 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2011-2016 Open Information Security Foundation
+/* Copyright (C) 2011-2017 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
@@ -662,10 +662,23 @@ int AFPRead(AFPThreadVars *ptv)
     SCReturnInt(AFP_READ_OK);
 }
 
-TmEcode AFPWritePacket(Packet *p)
+/**
+ * \brief AF packet write function.
+ *
+ * This function has to be called before the memory
+ * related to Packet in ring buffer is released.
+ *
+ * \param pointer to Packet
+ * \param version of capture: TPACKET_V2 or TPACKET_V3
+ * \retval TM_ECODE_FAILED on failure and TM_ECODE_OK on success
+ *
+ */
+static TmEcode AFPWritePacket(Packet *p, int version)
 {
     struct sockaddr_ll socket_address;
     int socket;
+    uint8_t *pstart;
+    size_t plen;
 
     if (p->afp_v.copy_mode == AFP_COPY_MODE_IPS) {
         if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
@@ -691,7 +704,31 @@ TmEcode AFPWritePacket(Packet *p)
     if (p->afp_v.peer->flags & AFP_SOCK_PROTECT)
         SCMutexLock(&p->afp_v.peer->sock_protect);
     socket = SC_ATOMIC_GET(p->afp_v.peer->socket);
-    if (sendto(socket, GET_PKT_DATA(p), GET_PKT_LEN(p), 0,
+
+    if (version == TPACKET_V2) {
+        union thdr h;
+        h.raw = p->afp_v.relptr;
+        /* 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) {
+            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(h.h2->tp_vlan_tci);
+        } else {
+            pstart = GET_PKT_DATA(p);
+            plen = GET_PKT_LEN(p);
+        }
+    } 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) {
         SCLogWarning(SC_ERR_SOCKET, "Sending packet failed on socket %d: %s",
@@ -712,7 +749,7 @@ void AFPReleaseDataFromRing(Packet *p)
     /* Need to be in copy mode and need to detect early release
        where Ethernet header could not be set (and pseudo packet) */
     if ((p->afp_v.copy_mode != AFP_COPY_MODE_NONE) && !PKT_IS_PSEUDOPKT(p)) {
-        AFPWritePacket(p);
+        AFPWritePacket(p, TPACKET_V2);
     }
 
     if (AFPDerefSocket(p->afp_v.mpeer) == 0)
@@ -728,15 +765,17 @@ cleanup:
     AFPV_CLEANUP(&p->afp_v);
 }
 
+#ifdef HAVE_TPACKET_V3
 void AFPReleasePacketV3(Packet *p)
 {
     /* Need to be in copy mode and need to detect early release
        where Ethernet header could not be set (and pseudo packet) */
     if ((p->afp_v.copy_mode != AFP_COPY_MODE_NONE) && !PKT_IS_PSEUDOPKT(p)) {
-        AFPWritePacket(p);
+        AFPWritePacket(p, TPACKET_V3);
     }
     PacketFreeOrRelease(p);
 }
+#endif
 
 void AFPReleasePacket(Packet *p)
 {
@@ -1686,6 +1725,21 @@ 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;
+        }
+    }
+
     /* Allocate RX ring */
 #ifdef HAVE_TPACKET_V3
     if (ptv->flags & AFP_TPACKET_V3) {