mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
732 lines
22 KiB
C
732 lines
22 KiB
C
/* 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 Victor Julien <victor@inliniac.net>
|
|
*
|
|
* Live pcap packet acquisition support
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
#include "suricata.h"
|
|
#include "decode.h"
|
|
#include "packet-queue.h"
|
|
#include "threads.h"
|
|
#include "threadvars.h"
|
|
#include "tm-queuehandlers.h"
|
|
#include "tm-threads.h"
|
|
#include "source-pcap.h"
|
|
#include "conf.h"
|
|
#include "util-bpf.h"
|
|
#include "util-debug.h"
|
|
#include "util-error.h"
|
|
#include "util-privs.h"
|
|
#include "util-datalink.h"
|
|
#include "util-device.h"
|
|
#include "util-optimize.h"
|
|
#include "util-checksum.h"
|
|
#include "util-ioctl.h"
|
|
#include "util-time.h"
|
|
#include "tmqh-packetpool.h"
|
|
|
|
#define PCAP_STATE_DOWN 0
|
|
#define PCAP_STATE_UP 1
|
|
|
|
#define PCAP_RECONNECT_TIMEOUT 500000
|
|
|
|
/**
|
|
* \brief 64bit pcap stats counters.
|
|
*
|
|
* libpcap only supports 32bit counters. They will eventually wrap around.
|
|
*
|
|
* Keep track of libpcap counters as 64bit counters to keep on counting even
|
|
* if libpcap's 32bit counters wrap around.
|
|
* Requires pcap_stats() to be called before 32bit stats wrap around twice,
|
|
* which we do.
|
|
*/
|
|
typedef struct PcapStats64_ {
|
|
uint64_t ps_recv;
|
|
uint64_t ps_drop;
|
|
uint64_t ps_ifdrop;
|
|
} PcapStats64;
|
|
|
|
/**
|
|
* \brief Structure to hold thread specific variables.
|
|
*/
|
|
typedef struct PcapThreadVars_
|
|
{
|
|
/* thread specific handle */
|
|
pcap_t *pcap_handle;
|
|
/* handle state */
|
|
unsigned char pcap_state;
|
|
/* thread specific bpf */
|
|
struct bpf_program filter;
|
|
/* ptr to string from config */
|
|
const char *bpf_filter;
|
|
|
|
time_t last_stats_dump;
|
|
|
|
/* data link type for the thread */
|
|
int datalink;
|
|
|
|
/* counters */
|
|
uint64_t pkts;
|
|
uint64_t bytes;
|
|
|
|
uint16_t capture_kernel_packets;
|
|
uint16_t capture_kernel_drops;
|
|
uint16_t capture_kernel_ifdrops;
|
|
|
|
ThreadVars *tv;
|
|
TmSlot *slot;
|
|
|
|
/** callback result -- set if one of the thread module failed. */
|
|
int cb_result;
|
|
|
|
/* pcap buffer size */
|
|
int pcap_buffer_size;
|
|
int pcap_snaplen;
|
|
int promisc;
|
|
|
|
ChecksumValidationMode checksum_mode;
|
|
|
|
LiveDevice *livedev;
|
|
|
|
PcapStats64 last_stats64;
|
|
} PcapThreadVars;
|
|
|
|
static TmEcode ReceivePcapThreadInit(ThreadVars *, const void *, void **);
|
|
static TmEcode ReceivePcapThreadDeinit(ThreadVars *tv, void *data);
|
|
static void ReceivePcapThreadExitStats(ThreadVars *, void *);
|
|
static TmEcode ReceivePcapLoop(ThreadVars *tv, void *data, void *slot);
|
|
static TmEcode ReceivePcapBreakLoop(ThreadVars *tv, void *data);
|
|
|
|
static TmEcode DecodePcapThreadInit(ThreadVars *, const void *, void **);
|
|
static TmEcode DecodePcapThreadDeinit(ThreadVars *tv, void *data);
|
|
static TmEcode DecodePcap(ThreadVars *, Packet *, void *);
|
|
|
|
#ifdef UNITTESTS
|
|
static void SourcePcapRegisterTests(void);
|
|
#endif
|
|
|
|
/** protect pcap_compile and pcap_setfilter, as they are not thread safe:
|
|
* http://seclists.org/tcpdump/2009/q1/62 */
|
|
static SCMutex pcap_bpf_compile_lock = SCMUTEX_INITIALIZER;
|
|
|
|
/**
|
|
* \brief Registration Function for ReceivePcap.
|
|
*/
|
|
void TmModuleReceivePcapRegister (void)
|
|
{
|
|
tmm_modules[TMM_RECEIVEPCAP].name = "ReceivePcap";
|
|
tmm_modules[TMM_RECEIVEPCAP].ThreadInit = ReceivePcapThreadInit;
|
|
tmm_modules[TMM_RECEIVEPCAP].ThreadDeinit = ReceivePcapThreadDeinit;
|
|
tmm_modules[TMM_RECEIVEPCAP].PktAcqLoop = ReceivePcapLoop;
|
|
tmm_modules[TMM_RECEIVEPCAP].PktAcqBreakLoop = ReceivePcapBreakLoop;
|
|
tmm_modules[TMM_RECEIVEPCAP].ThreadExitPrintStats = ReceivePcapThreadExitStats;
|
|
tmm_modules[TMM_RECEIVEPCAP].cap_flags = SC_CAP_NET_RAW;
|
|
tmm_modules[TMM_RECEIVEPCAP].flags = TM_FLAG_RECEIVE_TM;
|
|
#ifdef UNITTESTS
|
|
tmm_modules[TMM_RECEIVEPCAP].RegisterTests = SourcePcapRegisterTests;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* \brief Registration Function for DecodePcap.
|
|
*/
|
|
void TmModuleDecodePcapRegister (void)
|
|
{
|
|
tmm_modules[TMM_DECODEPCAP].name = "DecodePcap";
|
|
tmm_modules[TMM_DECODEPCAP].ThreadInit = DecodePcapThreadInit;
|
|
tmm_modules[TMM_DECODEPCAP].Func = DecodePcap;
|
|
tmm_modules[TMM_DECODEPCAP].ThreadDeinit = DecodePcapThreadDeinit;
|
|
tmm_modules[TMM_DECODEPCAP].flags = TM_FLAG_DECODE_TM;
|
|
}
|
|
|
|
/**
|
|
* \brief Update 64 bit |last| value from |current32| value taking one
|
|
* wrap-around into account.
|
|
*/
|
|
static inline void UpdatePcapStatsValue64(uint64_t *last, uint32_t current32)
|
|
{
|
|
/* uint64_t -> uint32_t is defined behaviour. It slices lower 32bits. */
|
|
uint32_t last32 = (uint32_t)*last;
|
|
|
|
/* Branchless code as wrap-around is defined for unsigned */
|
|
*last += (uint32_t)(current32 - last32);
|
|
|
|
/* Same calculation as:
|
|
if (likely(current32 >= last32)) {
|
|
*last += current32 - last32;
|
|
} else {
|
|
*last += (1ull << 32) + current32 - last32;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/**
|
|
* \brief Update 64 bit |last| stat values with values from |current|
|
|
* 32 bit pcap_stat.
|
|
*/
|
|
static inline void UpdatePcapStats64(
|
|
PcapStats64 *last, const struct pcap_stat *current)
|
|
{
|
|
UpdatePcapStatsValue64(&last->ps_recv, current->ps_recv);
|
|
UpdatePcapStatsValue64(&last->ps_drop, current->ps_drop);
|
|
UpdatePcapStatsValue64(&last->ps_ifdrop, current->ps_ifdrop);
|
|
}
|
|
|
|
static inline void PcapDumpCounters(PcapThreadVars *ptv)
|
|
{
|
|
struct pcap_stat pcap_s;
|
|
if (likely((pcap_stats(ptv->pcap_handle, &pcap_s) >= 0))) {
|
|
UpdatePcapStats64(&ptv->last_stats64, &pcap_s);
|
|
|
|
StatsSetUI64(ptv->tv, ptv->capture_kernel_packets,
|
|
ptv->last_stats64.ps_recv);
|
|
StatsSetUI64(
|
|
ptv->tv, ptv->capture_kernel_drops, ptv->last_stats64.ps_drop);
|
|
(void)SC_ATOMIC_SET(ptv->livedev->drop, ptv->last_stats64.ps_drop);
|
|
StatsSetUI64(ptv->tv, ptv->capture_kernel_ifdrops,
|
|
ptv->last_stats64.ps_ifdrop);
|
|
}
|
|
}
|
|
|
|
static int PcapOpenInterface(PcapThreadVars *ptv)
|
|
{
|
|
const char *iface = ptv->livedev->dev;
|
|
|
|
if (ptv->pcap_handle) {
|
|
pcap_close(ptv->pcap_handle);
|
|
ptv->pcap_handle = NULL;
|
|
if (ptv->filter.bf_insns) {
|
|
SCBPFFree(&ptv->filter);
|
|
}
|
|
}
|
|
|
|
if (LiveGetOffload() == 0) {
|
|
(void)GetIfaceOffloading(iface, 1, 1);
|
|
} else {
|
|
DisableIfaceOffloading(ptv->livedev, 1, 1);
|
|
}
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
ptv->pcap_handle = pcap_create(iface, errbuf);
|
|
if (ptv->pcap_handle == NULL) {
|
|
if (strlen(errbuf)) {
|
|
SCLogError("%s: could not create a new pcap handler, error %s", iface, errbuf);
|
|
} else {
|
|
SCLogError("%s: could not create a new pcap handler", iface);
|
|
}
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
if (ptv->pcap_snaplen > 0) {
|
|
/* set Snaplen. Must be called before pcap_activate */
|
|
int pcap_set_snaplen_r = pcap_set_snaplen(ptv->pcap_handle, ptv->pcap_snaplen);
|
|
if (pcap_set_snaplen_r != 0) {
|
|
SCLogError(
|
|
"%s: could not set snaplen, error: %s", iface, pcap_geterr(ptv->pcap_handle));
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
SCLogInfo("%s: snaplen set to %d", iface, ptv->pcap_snaplen);
|
|
}
|
|
|
|
if (ptv->promisc) {
|
|
/* set Promisc, and Timeout. Must be called before pcap_activate */
|
|
int pcap_set_promisc_r = pcap_set_promisc(ptv->pcap_handle, ptv->promisc);
|
|
if (pcap_set_promisc_r != 0) {
|
|
SCLogError("%s: could not set promisc mode, error %s", iface,
|
|
pcap_geterr(ptv->pcap_handle));
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
}
|
|
|
|
int pcap_set_timeout_r = pcap_set_timeout(ptv->pcap_handle, LIBPCAP_COPYWAIT);
|
|
if (pcap_set_timeout_r != 0) {
|
|
SCLogError("%s: could not set timeout, error %s", iface, pcap_geterr(ptv->pcap_handle));
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
#ifdef HAVE_PCAP_SET_BUFF
|
|
if (ptv->pcap_buffer_size > 0) {
|
|
SCLogInfo("%s: going to use pcap buffer size of %" PRId32, iface, ptv->pcap_buffer_size);
|
|
|
|
int pcap_set_buffer_size_r = pcap_set_buffer_size(ptv->pcap_handle, ptv->pcap_buffer_size);
|
|
if (pcap_set_buffer_size_r != 0) {
|
|
SCLogError("%s: could not set pcap buffer size, error %s", iface,
|
|
pcap_geterr(ptv->pcap_handle));
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
}
|
|
#endif /* HAVE_PCAP_SET_BUFF */
|
|
|
|
/* activate the handle */
|
|
int pcap_activate_r = pcap_activate(ptv->pcap_handle);
|
|
if (pcap_activate_r != 0) {
|
|
SCLogError("%s: could not activate the pcap handler, error %s", iface,
|
|
pcap_geterr(ptv->pcap_handle));
|
|
pcap_close(ptv->pcap_handle);
|
|
ptv->pcap_handle = NULL;
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
ptv->pcap_state = PCAP_STATE_UP;
|
|
|
|
/* set bpf filter if we have one */
|
|
if (ptv->bpf_filter) {
|
|
SCMutexLock(&pcap_bpf_compile_lock);
|
|
|
|
if (pcap_compile(ptv->pcap_handle, &ptv->filter, (char *)ptv->bpf_filter, 1, 0) < 0) {
|
|
SCLogError("%s: bpf compilation error %s", iface, pcap_geterr(ptv->pcap_handle));
|
|
SCMutexUnlock(&pcap_bpf_compile_lock);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
if (pcap_setfilter(ptv->pcap_handle, &ptv->filter) < 0) {
|
|
SCLogError("%s: could not set bpf filter %s", iface, pcap_geterr(ptv->pcap_handle));
|
|
SCMutexUnlock(&pcap_bpf_compile_lock);
|
|
return TM_ECODE_FAILED;
|
|
}
|
|
|
|
SCMutexUnlock(&pcap_bpf_compile_lock);
|
|
}
|
|
|
|
/* no offloading supported at all */
|
|
(void)GetIfaceOffloading(iface, 1, 1);
|
|
return TM_ECODE_OK;
|
|
}
|
|
|
|
static int PcapTryReopen(PcapThreadVars *ptv)
|
|
{
|
|
ptv->pcap_state = PCAP_STATE_DOWN;
|
|
|
|
if (PcapOpenInterface(ptv) != TM_ECODE_OK)
|
|
return -1;
|
|
|
|
SCLogInfo("%s: interface recovered, state is now \"up\"", ptv->livedev->dev);
|
|
ptv->pcap_state = PCAP_STATE_UP;
|
|
return 0;
|
|
}
|
|
|
|
static void PcapCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
|
|
{
|
|
SCEnter();
|
|
|
|
PcapThreadVars *ptv = (PcapThreadVars *)user;
|
|
Packet *p = PacketGetFromQueueOrAlloc();
|
|
|
|
if (unlikely(p == NULL)) {
|
|
SCReturn;
|
|
}
|
|
|
|
PKT_SET_SRC(p, PKT_SRC_WIRE);
|
|
p->ts = SCTIME_FROM_TIMEVAL(&h->ts);
|
|
SCLogDebug("p->ts.tv_sec %" PRIuMAX "", (uintmax_t)SCTIME_SECS(p->ts));
|
|
p->datalink = ptv->datalink;
|
|
|
|
ptv->pkts++;
|
|
ptv->bytes += h->caplen;
|
|
(void) SC_ATOMIC_ADD(ptv->livedev->pkts, 1);
|
|
p->livedev = ptv->livedev;
|
|
|
|
if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
|
|
TmqhOutputPacketpool(ptv->tv, p);
|
|
SCReturn;
|
|
}
|
|
|
|
switch (ptv->checksum_mode) {
|
|
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;
|
|
case CHECKSUM_VALIDATION_DISABLE:
|
|
p->flags |= PKT_IGNORE_CHECKSUM;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
|
|
pcap_breakloop(ptv->pcap_handle);
|
|
ptv->cb_result = TM_ECODE_FAILED;
|
|
}
|
|
|
|
/* Trigger one dump of stats every second */
|
|
SCTime_t current_time = TimeGet();
|
|
if ((time_t)SCTIME_SECS(current_time) != ptv->last_stats_dump) {
|
|
PcapDumpCounters(ptv);
|
|
ptv->last_stats_dump = SCTIME_SECS(current_time);
|
|
}
|
|
|
|
SCReturn;
|
|
}
|
|
|
|
#ifndef PCAP_ERROR_BREAK
|
|
#define PCAP_ERROR_BREAK -2
|
|
#endif
|
|
|
|
/**
|
|
* \brief Main PCAP reading Loop function
|
|
*/
|
|
static TmEcode ReceivePcapLoop(ThreadVars *tv, void *data, void *slot)
|
|
{
|
|
SCEnter();
|
|
|
|
int packet_q_len = 64;
|
|
PcapThreadVars *ptv = (PcapThreadVars *)data;
|
|
TmSlot *s = (TmSlot *)slot;
|
|
|
|
ptv->slot = s->slot_next;
|
|
ptv->cb_result = TM_ECODE_OK;
|
|
|
|
// 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();
|
|
|
|
int r = pcap_dispatch(ptv->pcap_handle, packet_q_len,
|
|
(pcap_handler)PcapCallbackLoop, (u_char *)ptv);
|
|
if (unlikely(r == 0 || r == PCAP_ERROR_BREAK || (r > 0 && r < packet_q_len))) {
|
|
if (r == PCAP_ERROR_BREAK && ptv->cb_result == TM_ECODE_FAILED) {
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
TmThreadsCaptureHandleTimeout(tv, NULL);
|
|
} else if (unlikely(r < 0)) {
|
|
int dbreak = 0;
|
|
SCLogError("error code %" PRId32 " %s", r, pcap_geterr(ptv->pcap_handle));
|
|
do {
|
|
usleep(PCAP_RECONNECT_TIMEOUT);
|
|
if (suricata_ctl_flags != 0) {
|
|
dbreak = 1;
|
|
break;
|
|
}
|
|
r = PcapTryReopen(ptv);
|
|
} while (r < 0);
|
|
if (dbreak) {
|
|
break;
|
|
}
|
|
} else if (ptv->cb_result == TM_ECODE_FAILED) {
|
|
SCLogError("Pcap callback PcapCallbackLoop failed");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
StatsSyncCountersIfSignalled(tv);
|
|
}
|
|
|
|
PcapDumpCounters(ptv);
|
|
StatsSyncCountersIfSignalled(tv);
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
/**
|
|
* \brief PCAP Break Loop function.
|
|
*/
|
|
static TmEcode ReceivePcapBreakLoop(ThreadVars *tv, void *data)
|
|
{
|
|
SCEnter();
|
|
PcapThreadVars *ptv = (PcapThreadVars *)data;
|
|
if (ptv->pcap_handle == NULL) {
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
pcap_breakloop(ptv->pcap_handle);
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
/**
|
|
* \brief Init function for ReceivePcap.
|
|
*
|
|
* This is a setup function for receiving packets
|
|
* via libpcap. There are two versions of this function
|
|
* depending on the major version of libpcap used.
|
|
* For versions prior to 1.x we use open_pcap_live,
|
|
* for versions 1.x and greater we use pcap_create + pcap_activate.
|
|
*
|
|
* \param tv pointer to ThreadVars
|
|
* \param initdata pointer to the interface passed from the user
|
|
* \param data pointer gets populated with PcapThreadVars
|
|
*
|
|
* \todo Create a general pcap setup function.
|
|
*/
|
|
static TmEcode ReceivePcapThreadInit(ThreadVars *tv, const void *initdata, void **data)
|
|
{
|
|
SCEnter();
|
|
PcapIfaceConfig *pcapconfig = (PcapIfaceConfig *)initdata;
|
|
|
|
if (initdata == NULL) {
|
|
SCLogError("initdata == NULL");
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
PcapThreadVars *ptv = SCCalloc(1, sizeof(PcapThreadVars));
|
|
if (unlikely(ptv == NULL)) {
|
|
pcapconfig->DerefFunc(pcapconfig);
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
ptv->tv = tv;
|
|
|
|
ptv->livedev = LiveGetDevice(pcapconfig->iface);
|
|
if (ptv->livedev == NULL) {
|
|
SCLogError("unable to find Live device");
|
|
ReceivePcapThreadDeinit(tv, ptv);
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
|
|
if (LiveGetOffload() == 0) {
|
|
(void)GetIfaceOffloading((char *)pcapconfig->iface, 1, 1);
|
|
} else {
|
|
DisableIfaceOffloading(ptv->livedev, 1, 1);
|
|
}
|
|
|
|
ptv->checksum_mode = pcapconfig->checksum_mode;
|
|
if (ptv->checksum_mode == CHECKSUM_VALIDATION_AUTO) {
|
|
SCLogInfo("%s: running in 'auto' checksum mode. Detection of interface "
|
|
"state will require %llu packets",
|
|
ptv->livedev->dev, CHECKSUM_SAMPLE_COUNT);
|
|
}
|
|
|
|
if (pcapconfig->snaplen == 0) {
|
|
/* We set snaplen if we can get the MTU */
|
|
ptv->pcap_snaplen = GetIfaceMaxPacketSize(ptv->livedev);
|
|
} else {
|
|
ptv->pcap_snaplen = pcapconfig->snaplen;
|
|
}
|
|
|
|
ptv->promisc = pcapconfig->promisc;
|
|
ptv->pcap_buffer_size = pcapconfig->buffer_size;
|
|
ptv->bpf_filter = pcapconfig->bpf_filter;
|
|
|
|
if (PcapOpenInterface(ptv) != TM_ECODE_OK) {
|
|
ReceivePcapThreadDeinit(tv, ptv);
|
|
pcapconfig->DerefFunc(pcapconfig);
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
}
|
|
ptv->pcap_state = PCAP_STATE_UP;
|
|
|
|
ptv->datalink = pcap_datalink(ptv->pcap_handle);
|
|
DatalinkSetGlobalType(ptv->datalink);
|
|
|
|
pcapconfig->DerefFunc(pcapconfig);
|
|
|
|
ptv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
|
|
ptv->tv);
|
|
ptv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
|
|
ptv->tv);
|
|
ptv->capture_kernel_ifdrops = StatsRegisterCounter("capture.kernel_ifdrops",
|
|
ptv->tv);
|
|
|
|
*data = (void *)ptv;
|
|
SCReturnInt(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 PcapThreadVars for ptv
|
|
*/
|
|
static void ReceivePcapThreadExitStats(ThreadVars *tv, void *data)
|
|
{
|
|
SCEnter();
|
|
PcapThreadVars *ptv = (PcapThreadVars *)data;
|
|
struct pcap_stat pcap_s;
|
|
|
|
if (pcap_stats(ptv->pcap_handle, &pcap_s) < 0) {
|
|
SCLogError("%s: failed to get pcap_stats: %s", ptv->livedev->dev,
|
|
pcap_geterr(ptv->pcap_handle));
|
|
SCLogInfo("%s: packets %" PRIu64 ", bytes %" PRIu64 "", ptv->livedev->dev, ptv->pkts,
|
|
ptv->bytes);
|
|
} else {
|
|
SCLogInfo("%s: packets %" PRIu64 ", bytes %" PRIu64 "", ptv->livedev->dev, ptv->pkts,
|
|
ptv->bytes);
|
|
|
|
/* these numbers are not entirely accurate as ps_recv contains packets
|
|
* that are still waiting to be processed at exit. ps_drop only contains
|
|
* packets dropped by the driver and not any packets dropped by the interface.
|
|
* Additionally see http://tracker.icir.org/bro/ticket/18
|
|
*
|
|
* Note: ps_recv includes dropped packets and should be considered total.
|
|
* Unless we start to look at ps_ifdrop which isn't supported everywhere.
|
|
*/
|
|
UpdatePcapStats64(&ptv->last_stats64, &pcap_s);
|
|
float drop_percent =
|
|
likely(ptv->last_stats64.ps_recv > 0)
|
|
? (((float)ptv->last_stats64.ps_drop) /
|
|
(float)ptv->last_stats64.ps_recv) *
|
|
100
|
|
: 0;
|
|
SCLogInfo("%s: pcap total:%" PRIu64 " recv:%" PRIu64 " drop:%" PRIu64 " (%02.1f%%)",
|
|
ptv->livedev->dev, ptv->last_stats64.ps_recv,
|
|
ptv->last_stats64.ps_recv - ptv->last_stats64.ps_drop, ptv->last_stats64.ps_drop,
|
|
drop_percent);
|
|
}
|
|
}
|
|
|
|
static TmEcode ReceivePcapThreadDeinit(ThreadVars *tv, void *data)
|
|
{
|
|
SCEnter();
|
|
PcapThreadVars *ptv = (PcapThreadVars *)data;
|
|
if (ptv != NULL) {
|
|
if (ptv->pcap_handle != NULL) {
|
|
pcap_close(ptv->pcap_handle);
|
|
}
|
|
if (ptv->filter.bf_insns) {
|
|
SCBPFFree(&ptv->filter);
|
|
}
|
|
SCFree(ptv);
|
|
}
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
/**
|
|
* \brief This function passes off to link type decoders.
|
|
*
|
|
* DecodePcap decodes packets from libpcap and passes
|
|
* them off to the proper link type decoder.
|
|
*
|
|
* \param t pointer to ThreadVars
|
|
* \param p pointer to the current packet
|
|
* \param data pointer that gets cast into PcapThreadVars for ptv
|
|
*/
|
|
static TmEcode DecodePcap(ThreadVars *tv, Packet *p, void *data)
|
|
{
|
|
SCEnter();
|
|
DecodeThreadVars *dtv = (DecodeThreadVars *)data;
|
|
|
|
BUG_ON(PKT_IS_PSEUDOPKT(p));
|
|
|
|
/* update counters */
|
|
DecodeUpdatePacketCounters(tv, dtv, p);
|
|
|
|
DecodeLinkLayer(tv, dtv, p->datalink, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
|
|
|
|
PacketDecodeFinalize(tv, dtv, p);
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
static TmEcode DecodePcapThreadInit(ThreadVars *tv, const void *initdata, void **data)
|
|
{
|
|
SCEnter();
|
|
|
|
DecodeThreadVars *dtv = DecodeThreadVarsAlloc(tv);
|
|
if (dtv == NULL)
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
DecodeRegisterPerfCounters(dtv, tv);
|
|
|
|
*data = (void *)dtv;
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
static TmEcode DecodePcapThreadDeinit(ThreadVars *tv, void *data)
|
|
{
|
|
if (data != NULL)
|
|
DecodeThreadVarsFree(tv, data);
|
|
SCReturnInt(TM_ECODE_OK);
|
|
}
|
|
|
|
void PcapTranslateIPToDevice(char *pcap_dev, size_t len)
|
|
{
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
pcap_if_t *alldevsp = NULL;
|
|
|
|
struct addrinfo ai_hints;
|
|
struct addrinfo *ai_list = NULL;
|
|
|
|
memset(&ai_hints, 0, sizeof(ai_hints));
|
|
ai_hints.ai_family = AF_UNSPEC;
|
|
ai_hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
/* try to translate IP */
|
|
if (getaddrinfo(pcap_dev, NULL, &ai_hints, &ai_list) != 0) {
|
|
return;
|
|
}
|
|
|
|
if (pcap_findalldevs(&alldevsp, errbuf)) {
|
|
freeaddrinfo(ai_list);
|
|
return;
|
|
}
|
|
|
|
for (pcap_if_t *devsp = alldevsp; devsp ; devsp = devsp->next) {
|
|
for (pcap_addr_t *ip = devsp->addresses; ip ; ip = ip->next) {
|
|
|
|
if (ai_list->ai_family != ip->addr->sa_family) {
|
|
continue;
|
|
}
|
|
|
|
if (ip->addr->sa_family == AF_INET) {
|
|
if (memcmp(&((struct sockaddr_in*)ai_list->ai_addr)->sin_addr,
|
|
&((struct sockaddr_in*)ip->addr)->sin_addr,
|
|
sizeof(struct in_addr)))
|
|
{
|
|
continue;
|
|
}
|
|
} else if (ip->addr->sa_family == AF_INET6) {
|
|
if (memcmp(&((struct sockaddr_in6*)ai_list->ai_addr)->sin6_addr,
|
|
&((struct sockaddr_in6*)ip->addr)->sin6_addr,
|
|
sizeof(struct in6_addr)))
|
|
{
|
|
continue;
|
|
}
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
freeaddrinfo(ai_list);
|
|
|
|
memset(pcap_dev, 0, len);
|
|
strlcpy(pcap_dev, devsp->name, len);
|
|
|
|
pcap_freealldevs(alldevsp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
freeaddrinfo(ai_list);
|
|
|
|
pcap_freealldevs(alldevsp);
|
|
}
|
|
|
|
/*
|
|
* unittests
|
|
*/
|
|
|
|
#ifdef UNITTESTS
|
|
#include "tests/source-pcap.c"
|
|
/**
|
|
* \brief Register the Unit tests for pcap source
|
|
*/
|
|
static void SourcePcapRegisterTests(void)
|
|
{
|
|
SourcePcapRegisterStatsTests();
|
|
}
|
|
#endif /* UNITTESTS */
|