From: Eric Leblond Date: Sat, 16 Dec 2017 23:01:42 +0000 (+0100) Subject: flow-bypass: add abstraction layer X-Git-Tag: suricata-4.1.0-beta1~216 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=08eec0833e4990ade0a196a0c8b56398ec7eb4f0;p=thirdparty%2Fsuricata.git flow-bypass: add abstraction layer The flow bypass thread can now be used by any capture method that register it timeout check function. --- diff --git a/src/flow-bypass.c b/src/flow-bypass.c index 66b67ed80b..790e00408a 100644 --- a/src/flow-bypass.c +++ b/src/flow-bypass.c @@ -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) diff --git a/src/flow-bypass.h b/src/flow-bypass.h index 812cccb12a..ffa37b6971 100644 --- a/src/flow-bypass.h +++ b/src/flow-bypass.h @@ -24,11 +24,22 @@ #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 diff --git a/src/runmode-af-packet.c b/src/runmode-af-packet.c index f213aa884f..1ad138d2a1 100644 --- a/src/runmode-af-packet.c +++ b/src/runmode-af-packet.c @@ -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; diff --git a/src/util-ebpf.c b/src/util-ebpf.c index 20922977ba..9e2a14add8 100644 --- a/src/util-ebpf.c +++ b/src/util-ebpf.c @@ -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 diff --git a/src/util-ebpf.h b/src/util-ebpf.h index ac9e0da565..21d7e55ae9 100644 --- a/src/util-ebpf.h +++ b/src/util-ebpf.h @@ -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