]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
pfring: hw bypass support
authorAlfredo Cardigliano <cardigliano@ntop.org>
Tue, 7 Nov 2017 10:49:47 +0000 (11:49 +0100)
committerVictor Julien <victor@inliniac.net>
Thu, 23 Nov 2017 06:14:16 +0000 (07:14 +0100)
This patch adds support for hw bypass by enabling flow offload in the network
card (when supported) and implementing the BypassPacketsFlow callback.
Hw bypass support is disabled by default, and can be enabled by setting
"bypass: yes" in the pfring interface configuration section in suricata.yaml.

configure.ac
src/decode.h
src/runmode-pfring.c
src/source-pfring.c
src/source-pfring.h
src/util-device.h
src/util-error.c
src/util-error.h
suricata.yaml.in

index c0c76463a36874d6dcdb40af06c0453f7d115c8c..54ee1f6d7b495394753ed617969752fceb480df0 100644 (file)
                 exit 1
                 fi
             fi
+            AC_COMPILE_IFELSE(
+                [AC_LANG_SOURCE([[
+                    #include <pfring.h>
+                    #ifndef PF_RING_FLOW_OFFLOAD
+                    # error PF_RING_FLOW_OFFLOAD not defined
+                    #endif
+                ]])],
+                [
+                    AC_DEFINE([HAVE_PF_RING_FLOW_OFFLOAD], [1], [PF_RING bypass support enabled])
+                ],
+                [
+                    echo
+                    echo "   Warning! Pfring hw bypass not supported by this library version < 7,"
+                    echo "   please upgrade to a newer version to use this feature."
+                    echo
+                    echo "   Continuing for now with hw bypass support disabled..."
+                    echo
+                ])
         else
             if test "x$enable_pfring" = "xyes"; then
             echo
index 16c54a28b017f493d6167e2aa79a8a32b13d5d6d..d3d5b21a9296829d1f67511b4c548571f72b35b6 100644 (file)
@@ -69,6 +69,7 @@ enum PktSrcEnum {
 #include "source-af-packet.h"
 #include "source-mpipe.h"
 #include "source-netmap.h"
+#include "source-pfring.h"
 
 #include "action-globals.h"
 
@@ -460,6 +461,9 @@ typedef struct Packet_
 #ifdef HAVE_NETMAP
         NetmapPacketVars netmap_v;
 #endif
+#ifdef HAVE_PFRING
+        PfringPacketVars pfring_v;
+#endif
 
         /** libpcap vars: shared by Pcap Live mode and Pcap File mode */
         PcapPacketVars pcap_v;
index 3e0fb3f25ed49a344b324e26256a4bb44c964887..1a1683dc562ad98c8ce992ebea01b3d172e6fed7 100644 (file)
@@ -195,6 +195,7 @@ static void *ParsePfringConfig(const char *iface)
     cluster_type default_ctype = CLUSTER_ROUND_ROBIN;
     int getctype = 0;
     const char *bpf_filter = NULL;
+    int bool_val;
 
     if (unlikely(pfconf == NULL)) {
         return NULL;
@@ -354,6 +355,19 @@ static void *ParsePfringConfig(const char *iface)
         }
     }
 
+    if (ConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &bool_val) == 1) {
+        if (bool_val) {
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+            SCLogConfig("Enabling bypass support in PF_RING for iface %s (if supported by underlying hw)", pfconf->iface);
+            pfconf->flags |= PFRING_CONF_FLAGS_BYPASS;
+#else
+            SCLogError(SC_ERR_BYPASS_NOT_SUPPORTED, "Bypass is not supported by this Pfring version, please upgrade");
+            SCFree(pfconf);
+            return NULL;
+#endif
+        }
+    }
+
     return pfconf;
 }
 
index ed387583c297780646409d18d18f426844404d71..b423778c73516d219e96e25059a2fec0892a15b2 100644 (file)
@@ -127,9 +127,9 @@ static SCMutex pfring_bpf_set_filter_lock = SCMUTEX_INITIALIZER;
 #define LIBPFRING_REENTRANT   0
 #define LIBPFRING_WAIT_FOR_INCOMING 1
 
-typedef enum {
-    PFRING_FLAGS_ZERO_COPY = 0x1
-} PfringThreadVarsFlags;
+/* PfringThreadVars flags */
+#define PFRING_FLAGS_ZERO_COPY (1 << 0)
+#define PFRING_FLAGS_BYPASS    (1 << 1)
 
 /**
  * \brief Structure to hold thread specific variables.
@@ -145,6 +145,7 @@ typedef struct PfringThreadVars_
 
     uint16_t capture_kernel_packets;
     uint16_t capture_kernel_drops;
+    uint16_t capture_bypassed;
 
     uint32_t flags;
 
@@ -209,10 +210,19 @@ static inline void PfringDumpCounters(PfringThreadVars *ptv)
          * to the interface counter */
         uint64_t th_pkts = StatsGetLocalCounterValue(ptv->tv, ptv->capture_kernel_packets);
         uint64_t th_drops = StatsGetLocalCounterValue(ptv->tv, ptv->capture_kernel_drops);
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+        uint64_t th_bypassed = StatsGetLocalCounterValue(ptv->tv, ptv->capture_bypassed);
+#endif
         SC_ATOMIC_ADD(ptv->livedev->pkts, pfring_s.recv - th_pkts);
         SC_ATOMIC_ADD(ptv->livedev->drop, pfring_s.drop - th_drops);
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+        SC_ATOMIC_ADD(ptv->livedev->bypassed, pfring_s.shunt - th_bypassed);
+#endif
         StatsSetUI64(ptv->tv, ptv->capture_kernel_packets, pfring_s.recv);
         StatsSetUI64(ptv->tv, ptv->capture_kernel_drops, pfring_s.drop);
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+        StatsSetUI64(ptv->tv, ptv->capture_bypassed, pfring_s.shunt);
+#endif
     }
 }
 
@@ -283,6 +293,42 @@ static inline void PfringProcessPacket(void *user, struct pfring_pkthdr *h, Pack
     SET_PKT_LEN(p, h->caplen);
 }
 
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+/**
+ * \brief Pfring bypass callback function
+ *
+ * \param p a Packet to use information from to trigger bypass
+ * \return 1 if bypass is successful, 0 if not
+ */
+static int PfringBypassCallback(Packet *p)
+{
+    hw_filtering_rule r;
+
+    /* Only bypass TCP and UDP */
+    if (!(PKT_IS_TCP(p) || PKT_IS_UDP(p))) {
+        return 0;
+    }
+
+    /* Bypassing tunneled packets is currently not supported */
+    if (IS_TUNNEL_PKT(p)) {
+        return 0;
+    }
+
+    r.rule_family_type = generic_flow_id_rule;
+    r.rule_family.flow_id_rule.action = flow_drop_rule;
+    r.rule_family.flow_id_rule.thread = 0;
+    r.rule_family.flow_id_rule.flow_id = p->pfring_v.flow_id;
+
+    SCLogDebug("Bypass set for flow ID = %u", p->pfring_v.flow_id);
+
+    if (pfring_add_hw_rule(p->pfring_v.ptv->pd, &r) < 0) {
+        return 0;
+    }
+
+    return 1;
+}
+#endif
+
 /**
  * \brief Recieves packets from an interface via libpfring.
  *
@@ -353,6 +399,14 @@ TmEcode ReceivePfringLoop(ThreadVars *tv, void *data, void *slot)
              * reset it here */
             PACKET_PROFILING_RESTART(p);
 
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+            if (ptv->flags & PFRING_FLAGS_BYPASS) {
+                p->pfring_v.flow_id = hdr.extended_hdr.pkt_hash; /* pkt hash contains the flow id in this configuration */
+                p->pfring_v.ptv = ptv;
+                p->BypassPacketsFlow = PfringBypassCallback;
+            }
+#endif
+
             /* Check for Zero-copy mode */
             if (ptv->flags & PFRING_FLAGS_ZERO_COPY) {
                 PacketSetData(p, pkt_buffer, hdr.caplen);
@@ -493,6 +547,13 @@ TmEcode ReceivePfringThreadInit(ThreadVars *tv, const void *initdata, void **dat
         }
     }
 
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+    if (pfconf->flags & PFRING_CONF_FLAGS_BYPASS) {
+        opflag |= PF_RING_FLOW_OFFLOAD | PF_RING_FLOW_OFFLOAD_NOUPDATES;
+        ptv->flags |= PFRING_FLAGS_BYPASS;
+    }
+#endif
+
     ptv->pd = pfring_open(ptv->interface, (uint32_t)default_packet_size, opflag);
     if (ptv->pd == NULL) {
         SCLogError(SC_ERR_PF_RING_OPEN,"Failed to open %s: pfring_open error."
@@ -561,6 +622,10 @@ TmEcode ReceivePfringThreadInit(ThreadVars *tv, const void *initdata, void **dat
             ptv->tv);
     ptv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
             ptv->tv);
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+    ptv->capture_bypassed = StatsRegisterCounter("capture.bypassed",
+            ptv->tv);
+#endif
 
     /* A bit strange to have this here but we only have vlan information
      * during reading so we need to know if we want to keep vlan during
@@ -613,6 +678,13 @@ void ReceivePfringThreadExitStats(ThreadVars *tv, void *data)
             StatsGetLocalCounterValue(tv, ptv->capture_kernel_packets),
             StatsGetLocalCounterValue(tv, ptv->capture_kernel_drops));
     SCLogPerf("(%s) Packets %" PRIu64 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes);
+#ifdef HAVE_PF_RING_FLOW_OFFLOAD
+    if (ptv->flags & PFRING_FLAGS_BYPASS) {
+        SCLogPerf("(%s) Bypass: Packets %" PRIu64 "",
+                tv->name,
+                StatsGetLocalCounterValue(tv, ptv->capture_bypassed));
+    }
+#endif
 }
 
 /**
index 9871f458f6ee0998c4734555d95ff840503d539a..dc4535556d56ec0429086c92c2fac2651cba7ea0 100644 (file)
 #include <pfring.h>
 #endif
 
-typedef enum {
-    PFRING_CONF_FLAGS_CLUSTER = 0x1
-} PfringIfaceConfigFlags;
+typedef struct PfringThreadVars_ PfringThreadVars;
+
+/* PfringIfaceConfig flags */
+#define PFRING_CONF_FLAGS_CLUSTER (1 << 0)
+#define PFRING_CONF_FLAGS_BYPASS  (1 << 1)
 
 typedef struct PfringIfaceConfig_
 {
@@ -55,6 +57,16 @@ typedef struct PfringIfaceConfig_
     void (*DerefFunc)(void *);
 } PfringIfaceConfig;
 
+/**
+ * \brief per packet Pfring vars
+ *
+ * This structure is used to pass packet metadata in callbacks.
+ */
+typedef struct PfringPacketVars_
+{
+    PfringThreadVars *ptv;
+    uint32_t flow_id;
+} PfringPacketVars;
 
 
 void TmModuleReceivePfringRegister (void);
index 5c4e76c09f3733461fbd34a0c716da1c70edeb19..3dd634567102bf94d56227903459f4407e4b9584 100644 (file)
@@ -43,6 +43,7 @@ typedef struct LiveDevice_ {
     int ignore_checksum;
     SC_ATOMIC_DECLARE(uint64_t, pkts);
     SC_ATOMIC_DECLARE(uint64_t, drop);
+    SC_ATOMIC_DECLARE(uint64_t, bypassed);
     SC_ATOMIC_DECLARE(uint64_t, invalid_checksums);
     TAILQ_ENTRY(LiveDevice_) next;
 
index c8ad0cf42a8d475bba0f106192ac418c9e98e6be..1e4075cd83e08d04a042eb65eb65f23d09f6c69c 100644 (file)
@@ -342,6 +342,7 @@ const char * SCErrorToString(SCError err)
         CASE_CODE (SC_WARN_EVENT_DROPPED);
         CASE_CODE (SC_ERR_NO_REDIS_ASYNC);
         CASE_CODE (SC_ERR_REDIS_CONFIG);
+        CASE_CODE (SC_ERR_BYPASS_NOT_SUPPORTED);
     }
 
     return "UNKNOWN_ERROR";
index 94f67d14b253290cfabdfa03c905ada2f258599c..cc73272a1fae19291edc52df305af4066d2bc044 100644 (file)
@@ -331,7 +331,8 @@ typedef enum {
     SC_WARN_LOG_CF_TOO_MANY_NODES,
     SC_WARN_EVENT_DROPPED,
     SC_ERR_NO_REDIS_ASYNC,
-    SC_ERR_REDIS_CONFIG
+    SC_ERR_REDIS_CONFIG,
+    SC_ERR_BYPASS_NOT_SUPPORTED
 } SCError;
 
 const char *SCErrorToString(SCError);
index c7fc6bd16e534dd119b42275f9bfcc62bc72c0a3..04055abf0521f5a2dc78de0eae2cd751b9f6e360 100644 (file)
@@ -1620,6 +1620,12 @@ pfring:
     cluster-type: cluster_flow
     # bpf filter for this interface
     #bpf-filter: tcp
+
+    # If bypass is set then the PF_RING hw bypass is activated, when supported
+    # by the interface in use. Suricata will instruct the interface to bypass
+    # all future packets for a flow that need to be bypassed.
+    #bypass: yes
+
     # Choose checksum verification mode for the interface. At the moment
     # of the capture, some packets may be with an invalid checksum due to
     # offloading to the network card of the checksum computation.