]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
flow-bypass: add abstraction layer
authorEric Leblond <eric@regit.org>
Sat, 16 Dec 2017 23:01:42 +0000 (00:01 +0100)
committerEric Leblond <eric@regit.org>
Tue, 6 Feb 2018 15:58:18 +0000 (16:58 +0100)
The flow bypass thread can now be used by any capture method that
register it timeout check function.

src/flow-bypass.c
src/flow-bypass.h
src/runmode-af-packet.c
src/util-ebpf.c
src/util-ebpf.h

index 66b67ed80bcfecc0168ab065b43c407c4a31d979..790e00408ac7e5e146908c09f8c26d37c08a942a 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2016 Open Information Security Foundation
+/* Copyright (C) 2016-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
@@ -28,7 +28,6 @@
 #include "flow-private.h"
 #include "util-ebpf.h"
 
-#define BYPASSED_FLOW_TIMEOUT   60
 #define FLOW_BYPASS_DELAY       10
 
 typedef struct BypassedFlowManagerThreadData_ {
@@ -37,42 +36,9 @@ typedef struct BypassedFlowManagerThreadData_ {
     uint16_t flow_bypassed_bytes;
 } BypassedFlowManagerThreadData;
 
-#ifdef HAVE_PACKET_EBPF
-
-static int BypassedFlowV4Timeout(int fd, struct flowv4_keys *key, struct pair *value, void *data)
-{
-    struct timespec *curtime = (struct timespec *)data;
-    SCLogDebug("Got curtime %" PRIu64 " and value %" PRIu64 " (sp:%d, dp:%d) %u",
-               curtime->tv_sec, value->time / 1000000000,
-               key->port16[0], key->port16[1], key->ip_proto
-              );
-
-    if (curtime->tv_sec - value->time / 1000000000 > BYPASSED_FLOW_TIMEOUT) {
-        SCLogDebug("Got no packet for %d -> %d at %" PRIu64,
-                   key->port16[0], key->port16[1], value->time);
-        return 1;
-    }
-    return 0;
-}
-
-static int BypassedFlowV6Timeout(int fd, struct flowv6_keys *key, struct pair *value, void *data)
-{
-    struct timespec *curtime = (struct timespec *)data;
-    SCLogDebug("Got curtime %" PRIu64 " and value %" PRIu64 " (sp:%d, dp:%d)",
-               curtime->tv_sec, value->time / 1000000000,
-               key->port16[0], key->port16[1]
-              );
-
-    if (curtime->tv_sec - value->time / 1000000000 > BYPASSED_FLOW_TIMEOUT) {
-        SCLogDebug("Got no packet for %d -> %d at %" PRIu64,
-                   key->port16[0], key->port16[1], value->time);
-        EBPFDeleteKey(fd, key);
-        return 1;
-    }
-    return 0;
-}
-
-#endif
+#define BYPASSFUNCMAX   4
+int g_bypassed_func_max_index = 0;
+BypassedCheckFunc BypassedFuncList[BYPASSFUNCMAX];
 
 static TmEcode BypassedFlowManager(ThreadVars *th_v, void *thread_data)
 {
@@ -81,29 +47,23 @@ static TmEcode BypassedFlowManager(ThreadVars *th_v, void *thread_data)
     BypassedFlowManagerThreadData *ftd = thread_data;
 
     while (1) {
+        int i;
         SCLogDebug("Dumping the table");
         struct timespec curtime;
-        struct flows_stats bypassstats = { 0, 0, 0};
         if (clock_gettime(CLOCK_MONOTONIC, &curtime) != 0) {
             SCLogWarning(SC_ERR_INVALID_VALUE, "Can't get time: %s (%d)",
                          strerror(errno), errno);
             sleep(1);
             continue;
         }
-        /* TODO indirection here: AF_PACKET and NFQ should be able to give their iterate function */
-        tcount = EBPFForEachFlowV4Table("flow_table_v4", BypassedFlowV4Timeout, &bypassstats, &curtime);
-        if (tcount) {
-            StatsAddUI64(th_v, ftd->flow_bypassed_cnt_clo, (uint64_t)bypassstats.count);
-            StatsAddUI64(th_v, ftd->flow_bypassed_pkts, (uint64_t)bypassstats.packets);
-            StatsAddUI64(th_v, ftd->flow_bypassed_bytes, (uint64_t)bypassstats.bytes);
-        }
-        memset(&bypassstats, 0, sizeof(bypassstats));
-        /* TODO indirection here: AF_PACKET and NFQ should be able to give their iterate function */
-        tcount = EBPFForEachFlowV6Table("flow_table_v6", BypassedFlowV6Timeout, &bypassstats, &curtime);
-        if (tcount) {
-            StatsAddUI64(th_v, ftd->flow_bypassed_cnt_clo, (uint64_t)bypassstats.count);
-            StatsAddUI64(th_v, ftd->flow_bypassed_pkts, (uint64_t)bypassstats.packets);
-            StatsAddUI64(th_v, ftd->flow_bypassed_bytes, (uint64_t)bypassstats.bytes);
+        for (i = 0; i < g_bypassed_func_max_index; i++) {
+            struct flows_stats bypassstats = { 0, 0, 0};
+            tcount = BypassedFuncList[i](&bypassstats, &curtime);
+            if (tcount) {
+                StatsAddUI64(th_v, ftd->flow_bypassed_cnt_clo, (uint64_t)bypassstats.count);
+                StatsAddUI64(th_v, ftd->flow_bypassed_pkts, (uint64_t)bypassstats.packets);
+                StatsAddUI64(th_v, ftd->flow_bypassed_bytes, (uint64_t)bypassstats.bytes);
+            }
         }
 
         if (TmThreadsCheckFlag(th_v, THV_KILL)) {
@@ -147,7 +107,6 @@ void BypassedFlowManagerThreadSpawn()
     return;
 #endif
 
-#ifdef HAVE_PACKET_EBPF
     ThreadVars *tv_flowmgr = NULL;
     tv_flowmgr = TmThreadCreateMgmtThreadByName("BypassedFlowManager",
             "BypassedFlowManager", 0);
@@ -161,7 +120,20 @@ void BypassedFlowManagerThreadSpawn()
         printf("ERROR: TmThreadSpawn failed\n");
         exit(1);
     }
-#endif
+}
+
+int BypassedFlowManagerRegisterCheckFunc(BypassedCheckFunc CheckFunc)
+{
+    if (!CheckFunc) {
+        return -1;
+    }
+    if (g_bypassed_func_max_index < BYPASSFUNCMAX) {
+        BypassedFuncList[g_bypassed_func_max_index] = CheckFunc;
+        g_bypassed_func_max_index++;
+    } else {
+        return -1;
+    }
+    return 0;
 }
 
 void TmModuleBypassedFlowManagerRegister (void)
index 812cccb12a040411ef52f8d5551908978b81c670..ffa37b6971fb58be3559982ed8f68b27a53f9df6 100644 (file)
 #ifndef __FLOW_BYPASS_H__
 #define __FLOW_BYPASS_H__
 
+struct flows_stats {
+    uint64_t count;
+    uint64_t packets;
+    uint64_t bytes;
+};
+
+typedef int (*BypassedCheckFunc)(struct flows_stats *bypassstats,
+                                 struct timespec *curtime);
+
 void FlowAddToBypassed(Flow *f);
 
 void BypassedFlowManagerThreadSpawn(void);
 void TmModuleBypassedFlowManagerRegister(void);
 
+int BypassedFlowManagerRegisterCheckFunc(BypassedCheckFunc CheckFunc);
+
 #endif
 
 
index f213aa884f7ca25f6aa33c8bd8593ebfbea9c8f4..1ad138d2a12621edaca92a2358aa1d5f2e36e3fe 100644 (file)
@@ -45,6 +45,8 @@
 #include "alert-unified2-alert.h"
 #include "alert-debuglog.h"
 
+#include "flow-bypass.h"
+
 #include "util-debug.h"
 #include "util-time.h"
 #include "util-cpu.h"
@@ -401,10 +403,15 @@ static void *ParseAFPConfig(const char *iface)
         aconf->ebpf_filter_file = ebpf_file;
         ConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &conf_val);
         if (conf_val) {
+#ifdef HAVE_PACKET_EBPF
             SCLogConfig("Using bypass kernel functionality for AF_PACKET (iface %s)",
                     aconf->iface);
             aconf->flags |= AFP_BYPASS;
             RunModeEnablesBypassManager();
+            BypassedFlowManagerRegisterCheckFunc(EBPFCheckBypassedFlowTimeout);
+#else
+            SCLogError(SC_ERR_UNIMPLEMENTED, "Bypass set but eBPF support is not built-in");
+#endif
         }
     }
 
@@ -430,10 +437,15 @@ static void *ParseAFPConfig(const char *iface)
         aconf->xdp_filter_file = ebpf_file;
         ConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &conf_val);
         if (conf_val) {
+#ifdef HAVE_PACKET_XDP
             SCLogConfig("Using bypass kernel functionality for AF_PACKET (iface %s)",
                     aconf->iface);
             aconf->flags |= AFP_XDPBYPASS;
             RunModeEnablesBypassManager();
+            BypassedFlowManagerRegisterCheckFunc(EBPFCheckBypassedFlowTimeout);
+#else
+            SCLogError(SC_ERR_UNIMPLEMENTED, "Bypass set but XDP support is not built-in");
+#endif
         }
 #ifdef HAVE_PACKET_XDP
         const char *xdp_mode;
index 20922977bac593f7fa5f5c1cb97c110e60ec67dc..9e2a14add8a48754f636f2bc802fc47deda13093 100644 (file)
@@ -34,6 +34,7 @@
 #define SC_PCAP_DONT_INCLUDE_PCAP_H 1
 
 #include "suricata-common.h"
+#include "flow-bypass.h"
 
 #ifdef HAVE_PACKET_EBPF
 
@@ -50,6 +51,8 @@
 
 #define BPF_MAP_MAX_COUNT 16
 
+#define BYPASSED_FLOW_TIMEOUT   60
+
 struct bpf_map_item {
     const char * name;
     int fd;
@@ -58,6 +61,11 @@ struct bpf_map_item {
 static struct bpf_map_item bpf_map_array[BPF_MAP_MAX_COUNT];
 static int bpf_map_last = 0;
 
+static void EBPFDeleteKey(int fd, void *key)
+{
+    bpf_map_delete_elem(fd, key);
+}
+
 int EBPFGetMapFDByName(const char *name)
 {
     int i;
@@ -214,7 +222,7 @@ int EBPFSetupXDP(const char *iface, int fd, uint8_t flags)
 }
 
 
-int EBPFForEachFlowV4Table(const char *name,
+static int EBPFForEachFlowV4Table(const char *name,
                               int (*FlowCallback)(int fd, struct flowv4_keys *key, struct pair *value, void *data),
                               struct flows_stats *flowstats,
                               void *data)
@@ -258,7 +266,7 @@ int EBPFForEachFlowV4Table(const char *name,
     return found;
 }
 
-int EBPFForEachFlowV6Table(const char *name,
+static int EBPFForEachFlowV6Table(const char *name,
                               int (*FlowCallback)(int fd, struct flowv6_keys *key, struct pair *value, void *data),
                               struct flows_stats *flowstats,
                               void *data)
@@ -303,9 +311,63 @@ int EBPFForEachFlowV6Table(const char *name,
     return found;
 }
 
-void EBPFDeleteKey(int fd, void *key)
+static int EBPFBypassedFlowV4Timeout(int fd, struct flowv4_keys *key, struct pair *value, void *data)
 {
-    bpf_map_delete_elem(fd, key);
+    struct timespec *curtime = (struct timespec *)data;
+    SCLogDebug("Got curtime %" PRIu64 " and value %" PRIu64 " (sp:%d, dp:%d) %u",
+               curtime->tv_sec, value->time / 1000000000,
+               key->port16[0], key->port16[1], key->ip_proto
+              );
+
+    if (curtime->tv_sec - value->time / 1000000000 > BYPASSED_FLOW_TIMEOUT) {
+        SCLogDebug("Got no packet for %d -> %d at %" PRIu64,
+                   key->port16[0], key->port16[1], value->time);
+        return 1;
+    }
+    return 0;
+}
+
+static int EBPFBypassedFlowV6Timeout(int fd, struct flowv6_keys *key, struct pair *value, void *data)
+{
+    struct timespec *curtime = (struct timespec *)data;
+    SCLogDebug("Got curtime %" PRIu64 " and value %" PRIu64 " (sp:%d, dp:%d)",
+               curtime->tv_sec, value->time / 1000000000,
+               key->port16[0], key->port16[1]
+              );
+
+    if (curtime->tv_sec - value->time / 1000000000 > BYPASSED_FLOW_TIMEOUT) {
+        SCLogDebug("Got no packet for %d -> %d at %" PRIu64,
+                   key->port16[0], key->port16[1], value->time);
+        EBPFDeleteKey(fd, key);
+        return 1;
+    }
+    return 0;
+}
+
+int EBPFCheckBypassedFlowTimeout(struct flows_stats *bypassstats,
+                                        struct timespec *curtime)
+{
+    struct flows_stats l_bypassstats = { 0, 0, 0};
+    int ret = 0;
+    int tcount = 0;
+    tcount = EBPFForEachFlowV4Table("flow_table_v4", EBPFBypassedFlowV4Timeout,
+                                    &l_bypassstats, curtime);
+    if (tcount) {
+        bypassstats->count = l_bypassstats.count;
+        bypassstats->packets = l_bypassstats.packets ;
+        bypassstats->bytes = l_bypassstats.bytes;
+        ret = 1;
+    }
+    memset(&l_bypassstats, 0, sizeof(l_bypassstats));
+    tcount = EBPFForEachFlowV6Table("flow_table_v6", EBPFBypassedFlowV6Timeout,
+                                    &l_bypassstats, curtime);
+    if (tcount) {
+        bypassstats->count += l_bypassstats.count;
+        bypassstats->packets += l_bypassstats.packets ;
+        bypassstats->bytes += l_bypassstats.bytes;
+        ret = 1;
+    }
+    return ret;
 }
 
 #endif
index ac9e0da56536846c3780399d3fe9ce7dadd096ef..21d7e55ae94599c2c1596c3797c494bb406b9b55 100644 (file)
@@ -31,7 +31,7 @@
 #define XDP_FLAGS_DRV_MODE             (1U << 2)
 #define XDP_FLAGS_HW_MODE              (1U << 3)
 
-
+#include "flow-bypass.h"
 
 struct flowv4_keys {
        __be32 src;
@@ -59,12 +59,6 @@ struct pair {
     uint64_t bytes;
 } __attribute__((__aligned__(8)));
 
-struct flows_stats {
-    uint64_t count;
-    uint64_t packets;
-    uint64_t bytes;
-};
-
 #define EBPF_SOCKET_FILTER  (1<<0)
 #define EBPF_XDP_CODE       (1<<1)
 
@@ -72,15 +66,8 @@ int EBPFGetMapFDByName(const char *name);
 int EBPFLoadFile(const char *path, const char * section, int *val, uint8_t flags);
 int EBPFSetupXDP(const char *iface, int fd, uint8_t flags);
 
-int EBPFForEachFlowV4Table(const char *name,
-                              int (*FlowCallback)(int fd, struct flowv4_keys *key, struct pair *value, void *data),
-                              struct flows_stats *flowstats,
-                              void *data);
-int EBPFForEachFlowV6Table(const char *name,
-                              int (*FlowCallback)(int fd, struct flowv6_keys *key, struct pair *value, void *data),
-                              struct flows_stats *flowstats,
-                              void *data);
-void EBPFDeleteKey(int fd, void *key);
+int EBPFCheckBypassedFlowTimeout(struct flows_stats *bypassstats,
+                                        struct timespec *curtime);
 
 #endif