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.
suricata/src/source-pcap.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 */