pfring: hw bypass support

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.
pull/3009/head
Alfredo Cardigliano 7 years ago committed by Victor Julien
parent ac18ef01c2
commit b6baafb3e3

@ -1185,6 +1185,24 @@
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

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

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

@ -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
}
/**

@ -31,9 +31,11 @@
#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);

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

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

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

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

Loading…
Cancel
Save