mirror of https://github.com/OISF/suricata
parent
223a4194ea
commit
c3092b6e5a
@ -1,533 +0,0 @@
|
||||
/* Copyright (C) 2007-2018 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
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "runmode-pfring.h"
|
||||
#include "tm-threads.h"
|
||||
#include "conf.h"
|
||||
#include "runmodes.h"
|
||||
#include "source-pfring.h"
|
||||
#include "suricata.h"
|
||||
#include "util-bpf.h"
|
||||
#include "util-debug.h"
|
||||
#include "util-time.h"
|
||||
#include "util-cpu.h"
|
||||
#include "util-runmodes.h"
|
||||
#include "util-device.h"
|
||||
#include "util-ioctl.h"
|
||||
#include "util-byte.h"
|
||||
#include "util-conf.h"
|
||||
|
||||
#ifdef HAVE_PFRING
|
||||
#include <pfring.h>
|
||||
|
||||
#define PFRING_CONF_V1 1
|
||||
#define PFRING_CONF_V2 2
|
||||
#endif
|
||||
|
||||
const char *RunModeIdsPfringGetDefaultMode(void)
|
||||
{
|
||||
#ifdef HAVE_PFRING
|
||||
return "workers";
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void RunModeIdsPfringRegister(void)
|
||||
{
|
||||
RunModeRegisterNewRunMode(RUNMODE_PFRING, "autofp",
|
||||
"Multi threaded pfring mode. Packets from "
|
||||
"each flow are assigned to a single detect "
|
||||
"thread, unlike \"pfring_auto\" where packets "
|
||||
"from the same flow can be processed by any "
|
||||
"detect thread",
|
||||
RunModeIdsPfringAutoFp, NULL);
|
||||
RunModeRegisterNewRunMode(
|
||||
RUNMODE_PFRING, "single", "Single threaded pfring mode", RunModeIdsPfringSingle, NULL);
|
||||
RunModeRegisterNewRunMode(RUNMODE_PFRING, "workers",
|
||||
"Workers pfring mode, each thread does all"
|
||||
" tasks from acquisition to logging",
|
||||
RunModeIdsPfringWorkers, NULL);
|
||||
}
|
||||
|
||||
#ifdef HAVE_PFRING
|
||||
static void PfringDerefConfig(void *conf)
|
||||
{
|
||||
PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
|
||||
if (SC_ATOMIC_SUB(pfp->ref, 1) == 1) {
|
||||
SCFree(pfp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief extract information from config file
|
||||
*
|
||||
* The returned structure will be freed by the thread init function.
|
||||
* This is thus necessary to or copy the structure before giving it
|
||||
* to thread or to reparse the file for each thread (and thus have
|
||||
* new structure.
|
||||
*
|
||||
* If old config system is used, then return the same parameters
|
||||
* value for each interface.
|
||||
*
|
||||
* \return a PfringIfaceConfig corresponding to the interface name
|
||||
*/
|
||||
static void *OldParsePfringConfig(const char *iface)
|
||||
{
|
||||
const char *threadsstr = NULL;
|
||||
PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
|
||||
const char *tmpclusterid;
|
||||
const char *tmpctype = NULL;
|
||||
cluster_type default_ctype = CLUSTER_FLOW;
|
||||
|
||||
if (unlikely(pfconf == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (iface == NULL) {
|
||||
SCFree(pfconf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
|
||||
pfconf->flags = 0;
|
||||
pfconf->threads = 1;
|
||||
pfconf->cluster_id = 1;
|
||||
pfconf->ctype = default_ctype;
|
||||
pfconf->DerefFunc = PfringDerefConfig;
|
||||
pfconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
|
||||
SC_ATOMIC_INIT(pfconf->ref);
|
||||
(void) SC_ATOMIC_ADD(pfconf->ref, 1);
|
||||
|
||||
/* Find initial node */
|
||||
if (ConfGet("pfring.threads", &threadsstr) != 1) {
|
||||
pfconf->threads = 1;
|
||||
} else {
|
||||
if (threadsstr != NULL) {
|
||||
if (StringParseInt32(&pfconf->threads, 10, 0, threadsstr) < 0) {
|
||||
SCLogWarning("Invalid value for "
|
||||
"pfring.threads: '%s'. Resetting to 1.",
|
||||
threadsstr);
|
||||
pfconf->threads = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pfconf->threads == 0) {
|
||||
pfconf->threads = 1;
|
||||
}
|
||||
|
||||
SC_ATOMIC_RESET(pfconf->ref);
|
||||
(void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
|
||||
|
||||
if (strncmp(pfconf->iface, "zc", 2) == 0) {
|
||||
SCLogInfo("%s: ZC interface detected, not setting cluster-id", pfconf->iface);
|
||||
}
|
||||
else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
|
||||
SCLogInfo("DNA interface detected, not setting cluster-id");
|
||||
} else if (ConfGet("pfring.cluster-id", &tmpclusterid) != 1) {
|
||||
SCLogError("Could not get cluster-id from config");
|
||||
} else {
|
||||
if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
|
||||
SCLogWarning("Invalid value for "
|
||||
"pfring.cluster_id: '%s'. Resetting to 1.",
|
||||
tmpclusterid);
|
||||
pfconf->cluster_id = 1;
|
||||
}
|
||||
pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
|
||||
SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
|
||||
}
|
||||
|
||||
if (strncmp(pfconf->iface, "zc", 2) == 0) {
|
||||
SCLogInfo("%s: ZC interface detected, not setting cluster type for PF_RING", pfconf->iface);
|
||||
} else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
|
||||
SCLogInfo(
|
||||
"%s: DNA interface detected, not setting cluster type for PF_RING", pfconf->iface);
|
||||
} else if (ConfGet("pfring.cluster-type", &tmpctype) != 1) {
|
||||
SCLogError("Could not get cluster-type from config");
|
||||
} else if (strcmp(tmpctype, "cluster_round_robin") == 0) {
|
||||
SCLogInfo("%s: Using round-robin cluster mode for PF_RING", pfconf->iface);
|
||||
pfconf->ctype = (cluster_type)tmpctype;
|
||||
} else if (strcmp(tmpctype, "cluster_flow") == 0) {
|
||||
SCLogInfo("%s: Using flow cluster mode for PF_RING", pfconf->iface);
|
||||
pfconf->ctype = (cluster_type)tmpctype;
|
||||
} else {
|
||||
SCLogError("invalid cluster-type %s", tmpctype);
|
||||
SCFree(pfconf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pfconf;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief extract information from config file
|
||||
*
|
||||
* The returned structure will be freed by the thread init function.
|
||||
* This is thus necessary to or copy the structure before giving it
|
||||
* to thread or to reparse the file for each thread (and thus have
|
||||
* new structure.
|
||||
*
|
||||
* If old config system is used, then return the same parameters
|
||||
* value for each interface.
|
||||
*
|
||||
* \return a PfringIfaceConfig corresponding to the interface name
|
||||
*/
|
||||
static void *ParsePfringConfig(const char *iface)
|
||||
{
|
||||
const char *threadsstr = NULL;
|
||||
ConfNode *if_root;
|
||||
ConfNode *if_default = NULL;
|
||||
ConfNode *pf_ring_node;
|
||||
PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
|
||||
const char *tmpclusterid;
|
||||
const char *tmpctype = NULL;
|
||||
cluster_type default_ctype = CLUSTER_FLOW;
|
||||
int getctype = 0;
|
||||
int bool_val;
|
||||
const char *active_runmode = RunmodeGetActive();
|
||||
|
||||
if (unlikely(pfconf == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (iface == NULL) {
|
||||
SCFree(pfconf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(pfconf, 0, sizeof(PfringIfaceConfig));
|
||||
strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
|
||||
pfconf->threads = 1;
|
||||
pfconf->cluster_id = 1;
|
||||
pfconf->ctype = (cluster_type)default_ctype;
|
||||
pfconf->DerefFunc = PfringDerefConfig;
|
||||
SC_ATOMIC_INIT(pfconf->ref);
|
||||
(void) SC_ATOMIC_ADD(pfconf->ref, 1);
|
||||
|
||||
/* Find initial node */
|
||||
pf_ring_node = ConfGetNode("pfring");
|
||||
if (pf_ring_node == NULL) {
|
||||
SCLogInfo("Unable to find pfring config using default value");
|
||||
return pfconf;
|
||||
}
|
||||
|
||||
if_root = ConfFindDeviceConfig(pf_ring_node, iface);
|
||||
|
||||
if_default = ConfFindDeviceConfig(pf_ring_node, "default");
|
||||
|
||||
if (if_root == NULL && if_default == NULL) {
|
||||
SCLogInfo("Unable to find pfring config for "
|
||||
"interface %s, using default value or 1.0 "
|
||||
"configuration system. ",
|
||||
iface);
|
||||
return pfconf;
|
||||
}
|
||||
|
||||
/* If there is no setting for current interface use default one as main iface */
|
||||
if (if_root == NULL) {
|
||||
if_root = if_default;
|
||||
if_default = NULL;
|
||||
}
|
||||
|
||||
if (active_runmode && !strcmp("single", active_runmode)) {
|
||||
pfconf->threads = 1;
|
||||
} else if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
|
||||
pfconf->threads = 1;
|
||||
} else if (threadsstr != NULL) {
|
||||
if (strcmp(threadsstr, "auto") == 0) {
|
||||
pfconf->threads = (int)UtilCpuGetNumProcessorsOnline();
|
||||
if (pfconf->threads > 0) {
|
||||
SCLogPerf("%u cores, so using %u threads", pfconf->threads, pfconf->threads);
|
||||
} else {
|
||||
pfconf->threads = GetIfaceRSSQueuesNum(iface);
|
||||
if (pfconf->threads > 0) {
|
||||
SCLogPerf("%d RSS queues, so using %u threads", pfconf->threads, pfconf->threads);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint16_t threads = 0;
|
||||
if (StringParseUint16(&threads, 10, 0, (const char *)threadsstr) < 0) {
|
||||
SCLogWarning("Invalid value for "
|
||||
"pfring.threads: '%s'. Resetting to 1.",
|
||||
threadsstr);
|
||||
pfconf->threads = 1;
|
||||
} else {
|
||||
pfconf->threads = threads;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pfconf->threads <= 0) {
|
||||
pfconf->threads = 1;
|
||||
}
|
||||
|
||||
SC_ATOMIC_RESET(pfconf->ref);
|
||||
(void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
|
||||
|
||||
/* command line value has precedence */
|
||||
if (ConfGet("pfring.cluster-id", &tmpclusterid) == 1) {
|
||||
if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
|
||||
SCLogWarning("Invalid value for "
|
||||
"pfring.cluster-id: '%s'. Resetting to 1.",
|
||||
tmpclusterid);
|
||||
pfconf->cluster_id = 1;
|
||||
}
|
||||
pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
|
||||
SCLogDebug("Going to use command-line provided cluster-id %" PRId32,
|
||||
pfconf->cluster_id);
|
||||
} else {
|
||||
|
||||
if (strncmp(pfconf->iface, "zc", 2) == 0) {
|
||||
SCLogInfo(
|
||||
"%s: ZC interface detected, not setting cluster-id for PF_RING", pfconf->iface);
|
||||
} else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
|
||||
SCLogInfo("%s: DNA interface detected, not setting cluster-id for PF_RING",
|
||||
pfconf->iface);
|
||||
} else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-id", &tmpclusterid) != 1) {
|
||||
SCLogError("Could not get cluster-id from config");
|
||||
} else {
|
||||
if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
|
||||
SCLogWarning("Invalid value for "
|
||||
"pfring.cluster-id: '%s'. Resetting to 1.",
|
||||
tmpclusterid);
|
||||
pfconf->cluster_id = 1;
|
||||
}
|
||||
pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
|
||||
SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
|
||||
}
|
||||
}
|
||||
|
||||
ConfSetBPFFilter(if_root, if_default, pfconf->iface, &pfconf->bpf_filter);
|
||||
|
||||
if (EngineModeIsIPS()) {
|
||||
FatalError("IPS mode not supported in PF_RING.");
|
||||
}
|
||||
|
||||
if (ConfGet("pfring.cluster-type", &tmpctype) == 1) {
|
||||
SCLogDebug("Going to use command-line provided cluster-type");
|
||||
getctype = 1;
|
||||
} else {
|
||||
if (strncmp(pfconf->iface, "zc", 2) == 0) {
|
||||
SCLogInfo("%s: ZC interface detected, not setting cluster type for PF_RING",
|
||||
pfconf->iface);
|
||||
} else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
|
||||
SCLogInfo("%s: DNA interface detected, not setting cluster type for PF_RING",
|
||||
pfconf->iface);
|
||||
} else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-type", &tmpctype) != 1) {
|
||||
SCLogError("Could not get cluster-type from config");
|
||||
} else {
|
||||
getctype = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (getctype) {
|
||||
if (strcmp(tmpctype, "cluster_round_robin") == 0) {
|
||||
SCLogInfo("%s: Using round-robin cluster mode for PF_RING."
|
||||
" This mode is not recommended.",
|
||||
pfconf->iface);
|
||||
pfconf->ctype = CLUSTER_ROUND_ROBIN;
|
||||
} else if (strcmp(tmpctype, "cluster_flow") == 0) {
|
||||
SCLogInfo("%s: Using flow cluster mode for PF_RING", pfconf->iface);
|
||||
pfconf->ctype = CLUSTER_FLOW;
|
||||
} else if (strcmp(tmpctype, "cluster_inner_flow") == 0) {
|
||||
SCLogInfo("%s: Using flow cluster mode inner mode for PF_RING", pfconf->iface);
|
||||
pfconf->ctype = CLUSTER_INNER_FLOW;
|
||||
} else if (strcmp(tmpctype, "cluster_inner_flow_2_tuple") == 0) {
|
||||
SCLogInfo("%s: Using flow cluster inner 2 tuple mode for PF_RING", pfconf->iface);
|
||||
pfconf->ctype = CLUSTER_INNER_FLOW_2_TUPLE;
|
||||
} else if (strcmp(tmpctype, "cluster_inner_flow_4_tuple") == 0) {
|
||||
SCLogInfo("%s: Using flow cluster inner 4 tuple mode for PF_RING", pfconf->iface);
|
||||
pfconf->ctype = CLUSTER_INNER_FLOW_4_TUPLE;
|
||||
} else if (strcmp(tmpctype, "cluster_inner_flow_5_tuple") == 0) {
|
||||
SCLogInfo("%s: Using flow cluster inner 5 tuple mode for PF_RING", pfconf->iface);
|
||||
pfconf->ctype = CLUSTER_INNER_FLOW_5_TUPLE;
|
||||
} else {
|
||||
SCLogError("invalid cluster-type %s", tmpctype);
|
||||
SCFree(pfconf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
|
||||
if (strcmp(tmpctype, "auto") == 0) {
|
||||
pfconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
|
||||
} else if (ConfValIsTrue(tmpctype)) {
|
||||
pfconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
|
||||
} else if (ConfValIsFalse(tmpctype)) {
|
||||
pfconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
|
||||
} else if (strcmp(tmpctype, "rx-only") == 0) {
|
||||
pfconf->checksum_mode = CHECKSUM_VALIDATION_RXONLY;
|
||||
} else {
|
||||
SCLogError("%s: Invalid value for checksum-checks", pfconf->iface);
|
||||
}
|
||||
}
|
||||
|
||||
if (ConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &bool_val) == 1) {
|
||||
if (bool_val) {
|
||||
#ifdef HAVE_PF_RING_FLOW_OFFLOAD
|
||||
SCLogConfig("%s: Enabling bypass support in PF_RING (if supported by underlying hw)",
|
||||
pfconf->iface);
|
||||
pfconf->flags |= PFRING_CONF_FLAGS_BYPASS;
|
||||
#else
|
||||
SCLogError("Bypass is not supported by this Pfring version, please upgrade");
|
||||
SCFree(pfconf);
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (LiveGetOffload() == 0) {
|
||||
if (GetIfaceOffloading(iface, 0, 1) == 1) {
|
||||
SCLogWarning("Using PF_RING with offloading activated leads to capture problems");
|
||||
}
|
||||
} else {
|
||||
DisableIfaceOffloading(LiveGetDevice(iface), 0, 1);
|
||||
}
|
||||
return pfconf;
|
||||
}
|
||||
|
||||
static int PfringConfigGetThreadsCount(void *conf)
|
||||
{
|
||||
PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
|
||||
return pfp->threads;
|
||||
}
|
||||
|
||||
static int PfringConfLevel(void)
|
||||
{
|
||||
const char *def_dev = NULL;
|
||||
/* 1.0 config should return a string */
|
||||
if (ConfGet("pfring.interface", &def_dev) != 1) {
|
||||
return PFRING_CONF_V2;
|
||||
} else {
|
||||
return PFRING_CONF_V1;
|
||||
}
|
||||
}
|
||||
|
||||
static int GetDevAndParser(const char **live_dev, ConfigIfaceParserFunc *parser)
|
||||
{
|
||||
ConfGet("pfring.live-interface", live_dev);
|
||||
|
||||
/* determine which config type we have */
|
||||
if (PfringConfLevel() > PFRING_CONF_V1) {
|
||||
*parser = ParsePfringConfig;
|
||||
} else {
|
||||
SCLogInfo("Using 1.0 style configuration for pfring");
|
||||
*parser = OldParsePfringConfig;
|
||||
/* In v1: try to get interface name from config */
|
||||
if (*live_dev == NULL) {
|
||||
if (ConfGet("pfring.interface", live_dev) == 1) {
|
||||
SCLogInfo("Using interface %s", *live_dev);
|
||||
LiveRegisterDevice(*live_dev);
|
||||
} else {
|
||||
SCLogInfo("No interface found, problem incoming");
|
||||
*live_dev = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int RunModeIdsPfringAutoFp(void)
|
||||
{
|
||||
SCEnter();
|
||||
|
||||
/* We include only if pfring is enabled */
|
||||
#ifdef HAVE_PFRING
|
||||
int ret;
|
||||
const char *live_dev = NULL;
|
||||
ConfigIfaceParserFunc tparser;
|
||||
|
||||
TimeModeSetLive();
|
||||
|
||||
ret = GetDevAndParser(&live_dev, &tparser);
|
||||
if (ret != 0) {
|
||||
FatalError("Unable to get parser and interface params");
|
||||
}
|
||||
|
||||
ret = RunModeSetLiveCaptureAutoFp(tparser, PfringConfigGetThreadsCount, "ReceivePfring",
|
||||
"DecodePfring", thread_name_autofp, live_dev);
|
||||
if (ret != 0) {
|
||||
FatalError("Runmode start failed");
|
||||
}
|
||||
|
||||
SCLogInfo("RunModeIdsPfringAutoFp initialised");
|
||||
#endif /* HAVE_PFRING */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RunModeIdsPfringSingle(void)
|
||||
{
|
||||
SCEnter();
|
||||
|
||||
/* We include only if pfring is enabled */
|
||||
#ifdef HAVE_PFRING
|
||||
int ret;
|
||||
const char *live_dev = NULL;
|
||||
ConfigIfaceParserFunc tparser;
|
||||
|
||||
TimeModeSetLive();
|
||||
|
||||
ret = GetDevAndParser(&live_dev, &tparser);
|
||||
if (ret != 0) {
|
||||
FatalError("Unable to get parser and interface params");
|
||||
}
|
||||
|
||||
ret = RunModeSetLiveCaptureSingle(tparser,
|
||||
PfringConfigGetThreadsCount,
|
||||
"ReceivePfring",
|
||||
"DecodePfring", thread_name_single,
|
||||
live_dev);
|
||||
if (ret != 0) {
|
||||
FatalError("Runmode start failed");
|
||||
}
|
||||
|
||||
SCLogInfo("RunModeIdsPfringSingle initialised");
|
||||
#endif /* HAVE_PFRING */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RunModeIdsPfringWorkers(void)
|
||||
{
|
||||
SCEnter();
|
||||
|
||||
/* We include only if pfring is enabled */
|
||||
#ifdef HAVE_PFRING
|
||||
int ret;
|
||||
const char *live_dev = NULL;
|
||||
ConfigIfaceParserFunc tparser;
|
||||
|
||||
TimeModeSetLive();
|
||||
|
||||
ret = GetDevAndParser(&live_dev, &tparser);
|
||||
if (ret != 0) {
|
||||
FatalError("Unable to get parser and interface params");
|
||||
}
|
||||
|
||||
ret = RunModeSetLiveCaptureWorkers(tparser, PfringConfigGetThreadsCount, "ReceivePfring",
|
||||
"DecodePfring", thread_name_workers, live_dev);
|
||||
if (ret != 0) {
|
||||
FatalError("Runmode start failed");
|
||||
}
|
||||
|
||||
SCLogInfo("RunModeIdsPfringWorkers initialised");
|
||||
#endif /* HAVE_PFRING */
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/* Copyright (C) 2007-2010 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
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
*
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
*/
|
||||
|
||||
#ifndef SURICATA_RUNMODE_PFRING_H
|
||||
#define SURICATA_RUNMODE_PFRING_H
|
||||
|
||||
#include "suricata-common.h"
|
||||
|
||||
int RunModeIdsPfringAutoFp(void);
|
||||
int RunModeIdsPfringSingle(void);
|
||||
int RunModeIdsPfringWorkers(void);
|
||||
void RunModeIdsPfringRegister(void);
|
||||
const char *RunModeIdsPfringGetDefaultMode(void);
|
||||
|
||||
#endif /* SURICATA_RUNMODE_PFRING_H */
|
@ -1,777 +0,0 @@
|
||||
/* Copyright (C) 2007-2019 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
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author William Metcalf <william.metcalf@gmail.com>
|
||||
* \author Eric Leblond <eric@regit.org>
|
||||
*
|
||||
* PF_RING packet acquisition support
|
||||
*
|
||||
* \todo remove requirement for setting cluster so old 3.x versions are supported
|
||||
* \todo implement DNA support
|
||||
* \todo Allow ring options such as snaplen etc, to be user configurable.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "suricata.h"
|
||||
#include "conf.h"
|
||||
#include "decode.h"
|
||||
#include "packet-queue.h"
|
||||
#include "threads.h"
|
||||
#include "threadvars.h"
|
||||
#include "tm-queuehandlers.h"
|
||||
#include "tm-threads.h"
|
||||
#include "source-pfring.h"
|
||||
#include "util-debug.h"
|
||||
#include "util-checksum.h"
|
||||
#include "util-privs.h"
|
||||
#include "util-datalink.h"
|
||||
#include "util-device.h"
|
||||
#include "util-host-info.h"
|
||||
#include "runmodes.h"
|
||||
#include "util-profiling.h"
|
||||
|
||||
TmEcode ReceivePfringLoop(ThreadVars *tv, void *data, void *slot);
|
||||
TmEcode PfringBreakLoop(ThreadVars *tv, void *data);
|
||||
TmEcode ReceivePfringThreadInit(ThreadVars *, const void *, void **);
|
||||
void ReceivePfringThreadExitStats(ThreadVars *, void *);
|
||||
TmEcode ReceivePfringThreadDeinit(ThreadVars *, void *);
|
||||
|
||||
TmEcode DecodePfringThreadInit(ThreadVars *, const void *, void **);
|
||||
TmEcode DecodePfring(ThreadVars *, Packet *, void *);
|
||||
TmEcode DecodePfringThreadDeinit(ThreadVars *tv, void *data);
|
||||
|
||||
extern uint16_t max_pending_packets;
|
||||
|
||||
#ifndef HAVE_PFRING
|
||||
|
||||
/*Handle cases where we don't have PF_RING support built-in*/
|
||||
TmEcode NoPfringSupportExit(ThreadVars *, const void *, void **);
|
||||
|
||||
void TmModuleReceivePfringRegister (void)
|
||||
{
|
||||
tmm_modules[TMM_RECEIVEPFRING].name = "ReceivePfring";
|
||||
tmm_modules[TMM_RECEIVEPFRING].ThreadInit = NoPfringSupportExit;
|
||||
tmm_modules[TMM_RECEIVEPFRING].Func = NULL;
|
||||
tmm_modules[TMM_RECEIVEPFRING].ThreadExitPrintStats = NULL;
|
||||
tmm_modules[TMM_RECEIVEPFRING].ThreadDeinit = NULL;
|
||||
tmm_modules[TMM_RECEIVEPFRING].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW |
|
||||
SC_CAP_NET_BIND_SERVICE | SC_CAP_NET_BROADCAST;
|
||||
tmm_modules[TMM_RECEIVEPFRING].flags = TM_FLAG_RECEIVE_TM;
|
||||
}
|
||||
|
||||
void TmModuleDecodePfringRegister (void)
|
||||
{
|
||||
tmm_modules[TMM_DECODEPFRING].name = "DecodePfring";
|
||||
tmm_modules[TMM_DECODEPFRING].ThreadInit = NoPfringSupportExit;
|
||||
tmm_modules[TMM_DECODEPFRING].Func = NULL;
|
||||
tmm_modules[TMM_DECODEPFRING].ThreadExitPrintStats = NULL;
|
||||
tmm_modules[TMM_DECODEPFRING].ThreadDeinit = NULL;
|
||||
tmm_modules[TMM_DECODEPFRING].cap_flags = 0;
|
||||
tmm_modules[TMM_DECODEPFRING].flags = TM_FLAG_DECODE_TM;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief this function prints an error message and exits.
|
||||
* \param tv pointer to ThreadVars
|
||||
* \param initdata pointer to the interface passed from the user
|
||||
* \param data pointer gets populated with PfringThreadVars
|
||||
*/
|
||||
TmEcode NoPfringSupportExit(ThreadVars *tv, const void *initdata, void **data)
|
||||
{
|
||||
SCLogError("Error creating thread %s: you do not have support for pfring "
|
||||
"enabled please recompile with --enable-pfring",
|
||||
tv->name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#else /* implied we do have PF_RING support */
|
||||
|
||||
#include <pfring.h>
|
||||
|
||||
/** protect pfring_set_bpf_filter, as it is not thread safe */
|
||||
static SCMutex pfring_bpf_set_filter_lock = SCMUTEX_INITIALIZER;
|
||||
|
||||
/* XXX replace with user configurable options */
|
||||
#define LIBPFRING_PROMISC 1
|
||||
#define LIBPFRING_REENTRANT 0
|
||||
#define LIBPFRING_WAIT_FOR_INCOMING 1
|
||||
|
||||
/* PfringThreadVars flags */
|
||||
#define PFRING_FLAGS_ZERO_COPY (1 << 0)
|
||||
#define PFRING_FLAGS_BYPASS (1 << 1)
|
||||
|
||||
/**
|
||||
* \brief Structure to hold thread specific variables.
|
||||
*/
|
||||
struct PfringThreadVars_
|
||||
{
|
||||
/* thread specific handle */
|
||||
pfring *pd;
|
||||
|
||||
/* counters */
|
||||
uint64_t bytes;
|
||||
uint64_t pkts;
|
||||
|
||||
uint16_t capture_kernel_packets;
|
||||
uint16_t capture_kernel_drops;
|
||||
uint16_t capture_bypassed;
|
||||
|
||||
uint32_t flags;
|
||||
|
||||
ThreadVars *tv;
|
||||
TmSlot *slot;
|
||||
|
||||
int vlan_in_ext_header;
|
||||
|
||||
/* threads count */
|
||||
int threads;
|
||||
|
||||
cluster_type ctype;
|
||||
|
||||
uint8_t cluster_id;
|
||||
char *interface;
|
||||
LiveDevice *livedev;
|
||||
|
||||
char *bpf_filter;
|
||||
|
||||
ChecksumValidationMode checksum_mode;
|
||||
|
||||
bool vlan_hdr_warned;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Registration Function for ReceivePfring.
|
||||
* \todo Unit tests are needed for this module.
|
||||
*/
|
||||
void TmModuleReceivePfringRegister (void)
|
||||
{
|
||||
tmm_modules[TMM_RECEIVEPFRING].name = "ReceivePfring";
|
||||
tmm_modules[TMM_RECEIVEPFRING].ThreadInit = ReceivePfringThreadInit;
|
||||
tmm_modules[TMM_RECEIVEPFRING].Func = NULL;
|
||||
tmm_modules[TMM_RECEIVEPFRING].PktAcqLoop = ReceivePfringLoop;
|
||||
tmm_modules[TMM_RECEIVEPFRING].PktAcqBreakLoop = PfringBreakLoop;
|
||||
tmm_modules[TMM_RECEIVEPFRING].ThreadExitPrintStats = ReceivePfringThreadExitStats;
|
||||
tmm_modules[TMM_RECEIVEPFRING].ThreadDeinit = ReceivePfringThreadDeinit;
|
||||
tmm_modules[TMM_RECEIVEPFRING].flags = TM_FLAG_RECEIVE_TM;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Registration Function for DecodePfring.
|
||||
* \todo Unit tests are needed for this module.
|
||||
*/
|
||||
void TmModuleDecodePfringRegister (void)
|
||||
{
|
||||
tmm_modules[TMM_DECODEPFRING].name = "DecodePfring";
|
||||
tmm_modules[TMM_DECODEPFRING].ThreadInit = DecodePfringThreadInit;
|
||||
tmm_modules[TMM_DECODEPFRING].Func = DecodePfring;
|
||||
tmm_modules[TMM_DECODEPFRING].ThreadExitPrintStats = NULL;
|
||||
tmm_modules[TMM_DECODEPFRING].ThreadDeinit = DecodePfringThreadDeinit;
|
||||
tmm_modules[TMM_DECODEPFRING].flags = TM_FLAG_DECODE_TM;
|
||||
}
|
||||
|
||||
static inline void PfringDumpCounters(PfringThreadVars *ptv)
|
||||
{
|
||||
pfring_stat pfring_s;
|
||||
if (likely((pfring_stats(ptv->pd, &pfring_s) >= 0))) {
|
||||
/* pfring counter is per socket and is not cleared after read.
|
||||
* So to get the number of packet on the interface we can add
|
||||
* the newly seen packets and drops for this thread and add it
|
||||
* 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);
|
||||
SC_ATOMIC_ADD(ptv->livedev->pkts, pfring_s.recv - th_pkts);
|
||||
SC_ATOMIC_ADD(ptv->livedev->drop, pfring_s.drop - th_drops);
|
||||
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
|
||||
if (ptv->flags & PFRING_FLAGS_BYPASS) {
|
||||
uint64_t th_bypassed = StatsGetLocalCounterValue(ptv->tv, ptv->capture_bypassed);
|
||||
SC_ATOMIC_ADD(ptv->livedev->bypassed, pfring_s.shunt - th_bypassed);
|
||||
StatsSetUI64(ptv->tv, ptv->capture_bypassed, pfring_s.shunt);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Pfring Packet Process function.
|
||||
*
|
||||
* This function fills in our packet structure from libpfring.
|
||||
* From here the packets are picked up by the DecodePfring thread.
|
||||
*
|
||||
* \param user pointer to PfringThreadVars
|
||||
* \param h pointer to pfring packet header
|
||||
* \param p pointer to the current packet
|
||||
*/
|
||||
static inline void PfringProcessPacket(void *user, struct pfring_pkthdr *h, Packet *p)
|
||||
{
|
||||
PfringThreadVars *ptv = (PfringThreadVars *)user;
|
||||
|
||||
ptv->bytes += h->caplen;
|
||||
ptv->pkts++;
|
||||
p->livedev = ptv->livedev;
|
||||
|
||||
/* PF_RING may fail to set timestamp */
|
||||
if (h->ts.tv_sec == 0) {
|
||||
struct timeval tmp_ts;
|
||||
gettimeofday(&tmp_ts, NULL);
|
||||
h->ts = tmp_ts;
|
||||
}
|
||||
|
||||
p->ts = SCTIME_FROM_TIMEVAL(&h->ts);
|
||||
|
||||
/* PF_RING all packets are marked as a link type of ethernet
|
||||
* so that is what we do here. */
|
||||
p->datalink = LINKTYPE_ETHERNET;
|
||||
|
||||
/* In the past, we needed this vlan handling in cases
|
||||
* where the vlan header was stripped from the raw packet.
|
||||
* With modern (at least >= 6) versions of PF_RING, the
|
||||
* 'copy_data_to_ring' function (kernel/pf_ring.c) makes
|
||||
* sure that if the hardware stripped the vlan header,
|
||||
* it is put back by PF_RING.
|
||||
*
|
||||
* PF_RING should put it back in all cases, but as a extra
|
||||
* precaution keep the check here. If the vlan header is
|
||||
* part of the raw packet, the vlan_offset will be set.
|
||||
* So if it is not set, use the parsed info from PF_RING's
|
||||
* extended header.
|
||||
*/
|
||||
if (ptv->vlan_in_ext_header &&
|
||||
h->extended_hdr.parsed_pkt.offset.vlan_offset == 0 &&
|
||||
h->extended_hdr.parsed_pkt.vlan_id)
|
||||
{
|
||||
p->vlan_id[0] = h->extended_hdr.parsed_pkt.vlan_id & 0x0fff;
|
||||
p->vlan_idx = 1;
|
||||
|
||||
if (!ptv->vlan_hdr_warned) {
|
||||
SCLogWarning("no VLAN header in the raw "
|
||||
"packet. See ticket #2355.");
|
||||
ptv->vlan_hdr_warned = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ptv->checksum_mode) {
|
||||
case CHECKSUM_VALIDATION_RXONLY:
|
||||
if (h->extended_hdr.rx_direction == 0) {
|
||||
p->flags |= PKT_IGNORE_CHECKSUM;
|
||||
}
|
||||
break;
|
||||
case CHECKSUM_VALIDATION_DISABLE:
|
||||
p->flags |= PKT_IGNORE_CHECKSUM;
|
||||
break;
|
||||
case CHECKSUM_VALIDATION_AUTO:
|
||||
if (ChecksumAutoModeCheck(ptv->pkts,
|
||||
SC_ATOMIC_GET(ptv->livedev->pkts),
|
||||
SC_ATOMIC_GET(ptv->livedev->invalid_checksums))) {
|
||||
ptv->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
|
||||
p->flags |= PKT_IGNORE_CHECKSUM;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
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 (!(PacketIsTCP(p) || PacketIsUDP(p))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bypassing tunneled packets is currently not supported */
|
||||
if (PacketIsTunnel(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 Receives packets from an interface via libpfring.
|
||||
*
|
||||
* This function receives packets from an interface and passes
|
||||
* the packet on to the pfring callback function.
|
||||
*
|
||||
* \param tv pointer to ThreadVars
|
||||
* \param data pointer that gets cast into PfringThreadVars for ptv
|
||||
* \param slot slot containing task information
|
||||
* \retval TM_ECODE_OK on success
|
||||
* \retval TM_ECODE_FAILED on failure
|
||||
*/
|
||||
TmEcode ReceivePfringLoop(ThreadVars *tv, void *data, void *slot)
|
||||
{
|
||||
SCEnter();
|
||||
|
||||
PfringThreadVars *ptv = (PfringThreadVars *)data;
|
||||
Packet *p = NULL;
|
||||
struct pfring_pkthdr hdr;
|
||||
TmSlot *s = (TmSlot *)slot;
|
||||
SCTime_t last_dump = SCTIME_INITIALIZER;
|
||||
u_int buffer_size;
|
||||
u_char *pkt_buffer;
|
||||
|
||||
ptv->slot = s->slot_next;
|
||||
|
||||
/* we have to enable the ring here as we need to do it after all
|
||||
* the threads have called pfring_set_cluster(). */
|
||||
int rc = pfring_enable_ring(ptv->pd);
|
||||
if (rc != 0) {
|
||||
SCLogError("pfring_enable_ring failed returned %d ", rc);
|
||||
SCReturnInt(TM_ECODE_FAILED);
|
||||
}
|
||||
|
||||
// Indicate that the thread is actually running its application level code (i.e., it can poll
|
||||
// packets)
|
||||
TmThreadsSetFlag(tv, THV_RUNNING);
|
||||
|
||||
while(1) {
|
||||
if (suricata_ctl_flags & SURICATA_STOP) {
|
||||
SCReturnInt(TM_ECODE_OK);
|
||||
}
|
||||
|
||||
/* make sure we have at least one packet in the packet pool, to prevent
|
||||
* us from alloc'ing packets at line rate */
|
||||
PacketPoolWait();
|
||||
|
||||
p = PacketGetFromQueueOrAlloc();
|
||||
if (p == NULL) {
|
||||
SCReturnInt(TM_ECODE_FAILED);
|
||||
}
|
||||
PKT_SET_SRC(p, PKT_SRC_WIRE);
|
||||
|
||||
/* Some flavours of PF_RING may fail to set timestamp - see PF-RING-enabled libpcap code*/
|
||||
hdr.ts.tv_sec = hdr.ts.tv_usec = 0;
|
||||
|
||||
/* Check for Zero-copy mode */
|
||||
if (ptv->flags & PFRING_FLAGS_ZERO_COPY) {
|
||||
buffer_size = 0;
|
||||
pkt_buffer = NULL;
|
||||
} else {
|
||||
buffer_size = GET_PKT_DIRECT_MAX_SIZE(p);
|
||||
pkt_buffer = GET_PKT_DIRECT_DATA(p);
|
||||
}
|
||||
|
||||
int r = pfring_recv(ptv->pd, &pkt_buffer,
|
||||
buffer_size,
|
||||
&hdr,
|
||||
LIBPFRING_WAIT_FOR_INCOMING);
|
||||
if (likely(r == 1)) {
|
||||
/* profiling started before blocking pfring_recv call, so
|
||||
* reset it here */
|
||||
PACKET_PROFILING_RESTART(p);
|
||||
|
||||
#ifdef HAVE_PF_RING_FLOW_OFFLOAD
|
||||
if (ptv->flags & PFRING_FLAGS_BYPASS) {
|
||||
/* pkt hash contains the flow id in this configuration */
|
||||
p->pfring_v.flow_id = hdr.extended_hdr.pkt_hash;
|
||||
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);
|
||||
}
|
||||
|
||||
PfringProcessPacket(ptv, &hdr, p);
|
||||
|
||||
if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
|
||||
SCReturnInt(TM_ECODE_FAILED);
|
||||
}
|
||||
|
||||
/* Trigger one dump of stats every second */
|
||||
if (SCTIME_CMP_NEQ(p->ts, last_dump)) {
|
||||
PfringDumpCounters(ptv);
|
||||
last_dump = p->ts;
|
||||
}
|
||||
} else if (unlikely(r == 0)) {
|
||||
if (suricata_ctl_flags & SURICATA_STOP) {
|
||||
TmqhOutputPacketpool(ptv->tv, p);
|
||||
SCReturnInt(TM_ECODE_OK);
|
||||
}
|
||||
|
||||
/* pfring didn't use the packet yet */
|
||||
TmThreadsCaptureHandleTimeout(tv, p);
|
||||
|
||||
} else {
|
||||
SCLogError("pfring_recv error %" PRId32 "", r);
|
||||
TmqhOutputPacketpool(ptv->tv, p);
|
||||
SCReturnInt(TM_ECODE_FAILED);
|
||||
}
|
||||
StatsSyncCountersIfSignalled(tv);
|
||||
}
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stop function for ReceivePfringLoop.
|
||||
*
|
||||
* This function forces ReceivePfringLoop to stop the
|
||||
* execution, exiting the packet capture loop.
|
||||
*
|
||||
* \param tv pointer to ThreadVars
|
||||
* \param data pointer that gets cast into PfringThreadVars for ptv
|
||||
* \retval TM_ECODE_OK on success
|
||||
* \retval TM_ECODE_FAILED on failure
|
||||
*/
|
||||
TmEcode PfringBreakLoop(ThreadVars *tv, void *data)
|
||||
{
|
||||
PfringThreadVars *ptv = (PfringThreadVars *)data;
|
||||
|
||||
/* Safety check */
|
||||
if (ptv->pd == NULL) {
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
pfring_breakloop(ptv->pd);
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Init function for ReceivePfring.
|
||||
*
|
||||
* This is a setup function for receiving packets
|
||||
* via libpfring.
|
||||
*
|
||||
* \param tv pointer to ThreadVars
|
||||
* \param initdata pointer to the interface passed from the user
|
||||
* \param data pointer gets populated with PfringThreadVars
|
||||
* \todo add a config option for setting cluster id
|
||||
* \todo Create a general pfring setup function.
|
||||
* \retval TM_ECODE_OK on success
|
||||
* \retval TM_ECODE_FAILED on error
|
||||
*/
|
||||
TmEcode ReceivePfringThreadInit(ThreadVars *tv, const void *initdata, void **data)
|
||||
{
|
||||
int rc;
|
||||
u_int32_t version = 0;
|
||||
PfringIfaceConfig *pfconf = (PfringIfaceConfig *) initdata;
|
||||
unsigned int opflag;
|
||||
char const *active_runmode = RunmodeGetActive();
|
||||
|
||||
if (pfconf == NULL)
|
||||
return TM_ECODE_FAILED;
|
||||
|
||||
PfringThreadVars *ptv = SCCalloc(1, sizeof(PfringThreadVars));
|
||||
if (unlikely(ptv == NULL)) {
|
||||
pfconf->DerefFunc(pfconf);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
ptv->tv = tv;
|
||||
ptv->threads = 1;
|
||||
|
||||
ptv->interface = SCStrdup(pfconf->iface);
|
||||
if (unlikely(ptv->interface == NULL)) {
|
||||
SCLogError("Unable to allocate device string");
|
||||
SCFree(ptv);
|
||||
SCReturnInt(TM_ECODE_FAILED);
|
||||
}
|
||||
|
||||
ptv->livedev = LiveGetDevice(pfconf->iface);
|
||||
if (ptv->livedev == NULL) {
|
||||
SCLogError("Unable to find Live device");
|
||||
SCFree(ptv);
|
||||
SCReturnInt(TM_ECODE_FAILED);
|
||||
}
|
||||
|
||||
/* enable zero-copy mode for workers runmode */
|
||||
if (active_runmode && strcmp("workers", active_runmode) == 0) {
|
||||
ptv->flags |= PFRING_FLAGS_ZERO_COPY;
|
||||
SCLogPerf("Enabling zero-copy for %s", ptv->interface);
|
||||
}
|
||||
|
||||
ptv->checksum_mode = pfconf->checksum_mode;
|
||||
|
||||
opflag = PF_RING_PROMISC;
|
||||
|
||||
/* if we have a recent kernel, we need to use parsed_pkt to get VLAN info */
|
||||
if (ptv->vlan_in_ext_header) {
|
||||
opflag |= PF_RING_LONG_HEADER;
|
||||
}
|
||||
|
||||
if (ptv->checksum_mode == CHECKSUM_VALIDATION_RXONLY) {
|
||||
if (strncmp(ptv->interface, "dna", 3) == 0) {
|
||||
SCLogWarning("Can't use rxonly checksum-checks on DNA interface,"
|
||||
" resetting to auto");
|
||||
ptv->checksum_mode = CHECKSUM_VALIDATION_AUTO;
|
||||
} else {
|
||||
opflag |= PF_RING_LONG_HEADER;
|
||||
}
|
||||
}
|
||||
|
||||
#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("Failed to open %s: pfring_open error."
|
||||
" Check if %s exists and pf_ring module is loaded.",
|
||||
ptv->interface, ptv->interface);
|
||||
pfconf->DerefFunc(pfconf);
|
||||
SCFree(ptv);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
pfring_set_application_name(ptv->pd, (char *)PROG_NAME);
|
||||
pfring_version(ptv->pd, &version);
|
||||
|
||||
/* We only set cluster info if the number of pfring threads is greater than 1 */
|
||||
ptv->threads = pfconf->threads;
|
||||
|
||||
ptv->cluster_id = pfconf->cluster_id;
|
||||
|
||||
if ((ptv->threads == 1) && (strncmp(ptv->interface, "dna", 3) == 0)) {
|
||||
SCLogInfo("DNA interface detected, not adding thread to cluster");
|
||||
} else if (strncmp(ptv->interface, "zc", 2) == 0) {
|
||||
SCLogInfo("ZC interface detected, not adding thread to cluster");
|
||||
} else {
|
||||
ptv->ctype = (cluster_type)pfconf->ctype;
|
||||
rc = pfring_set_cluster(ptv->pd, ptv->cluster_id, ptv->ctype);
|
||||
|
||||
if (rc != 0) {
|
||||
SCLogError("pfring_set_cluster "
|
||||
"returned %d for cluster-id: %d",
|
||||
rc, ptv->cluster_id);
|
||||
if (rc != PF_RING_ERROR_NOT_SUPPORTED || (pfconf->flags & PFRING_CONF_FLAGS_CLUSTER)) {
|
||||
/* cluster is mandatory as explicitly specified in the configuration */
|
||||
pfconf->DerefFunc(pfconf);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ptv->threads > 1) {
|
||||
SCLogPerf("(%s) Using PF_RING v.%d.%d.%d, interface %s, cluster-id %d",
|
||||
tv->name, (version & 0xFFFF0000) >> 16, (version & 0x0000FF00) >> 8,
|
||||
version & 0x000000FF, ptv->interface, ptv->cluster_id);
|
||||
} else {
|
||||
SCLogPerf("(%s) Using PF_RING v.%d.%d.%d, interface %s, cluster-id %d, single-pfring-thread",
|
||||
tv->name, (version & 0xFFFF0000) >> 16, (version & 0x0000FF00) >> 8,
|
||||
version & 0x000000FF, ptv->interface, ptv->cluster_id);
|
||||
}
|
||||
|
||||
if (pfconf->bpf_filter) {
|
||||
ptv->bpf_filter = SCStrdup(pfconf->bpf_filter);
|
||||
if (unlikely(ptv->bpf_filter == NULL)) {
|
||||
SCLogError("Set PF_RING bpf filter failed.");
|
||||
} else {
|
||||
SCMutexLock(&pfring_bpf_set_filter_lock);
|
||||
rc = pfring_set_bpf_filter(ptv->pd, ptv->bpf_filter);
|
||||
SCMutexUnlock(&pfring_bpf_set_filter_lock);
|
||||
|
||||
if (rc < 0) {
|
||||
SCLogError("Failed to compile BPF \"%s\"", ptv->bpf_filter);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
|
||||
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
|
||||
|
||||
/* If kernel is older than 3.0, VLAN is not stripped so we don't
|
||||
* get the info from packet extended header but we will use a standard
|
||||
* parsing */
|
||||
ptv->vlan_in_ext_header = 1;
|
||||
if (! SCKernelVersionIsAtLeast(3, 0)) {
|
||||
ptv->vlan_in_ext_header = 0;
|
||||
}
|
||||
|
||||
/* If VLAN tags are not in the extended header, set cluster type to 5-tuple
|
||||
* or in case of a ZC interface, do nothing */
|
||||
if ((! ptv->vlan_in_ext_header) && ptv->ctype == CLUSTER_FLOW &&
|
||||
strncmp(ptv->interface, "zc", 2) != 0) {
|
||||
SCLogPerf("VLAN not in extended header, setting cluster type to CLUSTER_FLOW_5_TUPLE");
|
||||
rc = pfring_set_cluster(ptv->pd, ptv->cluster_id, CLUSTER_FLOW_5_TUPLE);
|
||||
|
||||
if (rc != 0) {
|
||||
SCLogError("pfring_set_cluster "
|
||||
"returned %d for cluster-id: %d",
|
||||
rc, ptv->cluster_id);
|
||||
pfconf->DerefFunc(pfconf);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
DatalinkSetGlobalType(LINKTYPE_ETHERNET);
|
||||
|
||||
*data = (void *)ptv;
|
||||
pfconf->DerefFunc(pfconf);
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief This function prints stats to the screen at exit.
|
||||
* \param tv pointer to ThreadVars
|
||||
* \param data pointer that gets cast into PfringThreadVars for ptv
|
||||
*/
|
||||
void ReceivePfringThreadExitStats(ThreadVars *tv, void *data)
|
||||
{
|
||||
PfringThreadVars *ptv = (PfringThreadVars *)data;
|
||||
|
||||
PfringDumpCounters(ptv);
|
||||
SCLogPerf("(%s) Kernel: Packets %" PRIu64 ", dropped %" PRIu64 "",
|
||||
tv->name,
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief DeInit function closes pd at exit.
|
||||
* \param tv pointer to ThreadVars
|
||||
* \param data pointer that gets cast into PfringThreadVars for ptvi
|
||||
* \retval TM_ECODE_OK is always returned
|
||||
*/
|
||||
TmEcode ReceivePfringThreadDeinit(ThreadVars *tv, void *data)
|
||||
{
|
||||
PfringThreadVars *ptv = (PfringThreadVars *)data;
|
||||
if (ptv->interface)
|
||||
SCFree(ptv->interface);
|
||||
pfring_remove_from_cluster(ptv->pd);
|
||||
|
||||
if (ptv->bpf_filter) {
|
||||
pfring_remove_bpf_filter(ptv->pd);
|
||||
SCFree(ptv->bpf_filter);
|
||||
}
|
||||
|
||||
pfring_close(ptv->pd);
|
||||
SCFree(ptv);
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief This function passes off to link type decoders.
|
||||
*
|
||||
* DecodePfring decodes raw packets from PF_RING. Inside of libpcap version of
|
||||
* PF_RING all packets are marked as a link type of ethernet so that is what we do here.
|
||||
*
|
||||
* \param tv pointer to ThreadVars
|
||||
* \param p pointer to the current packet
|
||||
* \param data pointer that gets cast into PfringThreadVars for ptv
|
||||
*
|
||||
* \todo Verify that PF_RING only deals with ethernet traffic
|
||||
*
|
||||
* \warning This function bypasses the pkt buf and len macro's
|
||||
*
|
||||
* \retval TM_ECODE_OK is always returned
|
||||
*/
|
||||
TmEcode DecodePfring(ThreadVars *tv, Packet *p, void *data)
|
||||
{
|
||||
DecodeThreadVars *dtv = (DecodeThreadVars *)data;
|
||||
|
||||
BUG_ON(PKT_IS_PSEUDOPKT(p));
|
||||
|
||||
/* update counters */
|
||||
DecodeUpdatePacketCounters(tv, dtv, p);
|
||||
|
||||
/* If suri has set vlan during reading, we increase vlan counter */
|
||||
if (p->vlan_idx) {
|
||||
StatsIncr(tv, dtv->counter_vlan);
|
||||
}
|
||||
|
||||
DecodeEthernet(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
|
||||
|
||||
PacketDecodeFinalize(tv, dtv, p);
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief This an Init function for DecodePfring
|
||||
*
|
||||
* \param tv pointer to ThreadVars
|
||||
* \param initdata pointer to initialization data.
|
||||
* \param data pointer that gets cast into PfringThreadVars for ptv
|
||||
* \retval TM_ECODE_OK is returned on success
|
||||
* \retval TM_ECODE_FAILED is returned on error
|
||||
*/
|
||||
TmEcode DecodePfringThreadInit(ThreadVars *tv, const void *initdata, void **data)
|
||||
{
|
||||
DecodeThreadVars *dtv = NULL;
|
||||
|
||||
dtv = DecodeThreadVarsAlloc(tv);
|
||||
if (dtv == NULL)
|
||||
SCReturnInt(TM_ECODE_FAILED);
|
||||
|
||||
DecodeRegisterPerfCounters(dtv, tv);
|
||||
|
||||
*data = (void *)dtv;
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
TmEcode DecodePfringThreadDeinit(ThreadVars *tv, void *data)
|
||||
{
|
||||
if (data != NULL)
|
||||
DecodeThreadVarsFree(tv, data);
|
||||
SCReturnInt(TM_ECODE_OK);
|
||||
}
|
||||
|
||||
#endif /* HAVE_PFRING */
|
||||
/* eof */
|
@ -1,84 +0,0 @@
|
||||
/* Copyright (C) 2007-2010 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
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author William Metcalf <william.metcalf@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef SURICATA_SOURCE_PFRING_H
|
||||
#define SURICATA_SOURCE_PFRING_H
|
||||
|
||||
#define PFRING_IFACE_NAME_LENGTH 48
|
||||
|
||||
typedef struct PfringThreadVars_ PfringThreadVars;
|
||||
|
||||
/* PfringIfaceConfig flags */
|
||||
#define PFRING_CONF_FLAGS_CLUSTER (1 << 0)
|
||||
#define PFRING_CONF_FLAGS_BYPASS (1 << 1)
|
||||
|
||||
typedef struct PfringIfaceConfig_
|
||||
{
|
||||
uint32_t flags;
|
||||
|
||||
/* cluster param */
|
||||
int cluster_id;
|
||||
unsigned int ctype;
|
||||
|
||||
char iface[PFRING_IFACE_NAME_LENGTH];
|
||||
/* number of threads */
|
||||
int threads;
|
||||
|
||||
const char *bpf_filter;
|
||||
|
||||
ChecksumValidationMode checksum_mode;
|
||||
SC_ATOMIC_DECLARE(unsigned int, ref);
|
||||
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);
|
||||
void TmModuleDecodePfringRegister (void);
|
||||
|
||||
int PfringConfGetThreads(void);
|
||||
void PfringLoadConfig(void);
|
||||
|
||||
/*
|
||||
* We don't have to use an enum that sucks in our code
|
||||
* these values must match with cluster_type in the kernel
|
||||
* include file pf_ring.h
|
||||
*/
|
||||
#define CLUSTER_FLOW 0
|
||||
#define CLUSTER_ROUND_ROBIN 1
|
||||
#define CLUSTER_FLOW_5_TUPLE 4
|
||||
#define CLUSTER_INNER_FLOW 6
|
||||
#define CLUSTER_INNER_FLOW_2_TUPLE 7
|
||||
#define CLUSTER_INNER_FLOW_4_TUPLE 8
|
||||
#define CLUSTER_INNER_FLOW_5_TUPLE 9
|
||||
#endif /* SURICATA_SOURCE_PFRING_H */
|
Loading…
Reference in New Issue