flow-bypass: add abstraction layer

The flow bypass thread can now be used by any capture method that
register it timeout check function.
pull/3221/head
Eric Leblond 8 years ago
parent 43ecf0d78d
commit 08eec0833e

@ -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)

@ -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

@ -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;

@ -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

@ -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

Loading…
Cancel
Save