|
|
|
/* 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>
|
|
|
|
*
|
|
|
|
* Live pcap packet acquisition support
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if LIBPCAP_VERSION_MAJOR == 1
|
|
|
|
#include <pcap/pcap.h>
|
|
|
|
#else
|
|
|
|
#include <pcap.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#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-debug.h"
|
|
|
|
#include "util-error.h"
|
|
|
|
#include "util-privs.h"
|
|
|
|
#include "util-device.h"
|
|
|
|
#include "util-optimize.h"
|
|
|
|
#include "tmqh-packetpool.h"
|
|
|
|
|
|
|
|
extern uint8_t suricata_ctl_flags;
|
|
|
|
extern int max_pending_packets;
|
|
|
|
|
|
|
|
/** control how many packet libpcap may read in one go */
|
|
|
|
static int pcap_max_read_packets = 0;
|
|
|
|
|
|
|
|
/** max packets < 65536 */
|
|
|
|
#define PCAP_FILE_MAX_PKTS 256
|
|
|
|
#define PCAP_IFACE_NAME_LENGTH 48
|
|
|
|
|
|
|
|
#define PCAP_STATE_DOWN 0
|
|
|
|
#define PCAP_STATE_UP 1
|
|
|
|
|
|
|
|
#define PCAP_RECONNECT_TIMEOUT 500000
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Structure to hold thread specific variables.
|
|
|
|
*/
|
|
|
|
typedef struct PcapThreadVars_
|
|
|
|
{
|
|
|
|
/* thread specific handle */
|
|
|
|
pcap_t *pcap_handle;
|
|
|
|
/* handle state */
|
|
|
|
unsigned char pcap_state;
|
|
|
|
#if LIBPCAP_VERSION_MAJOR == 0
|
|
|
|
char iface[PCAP_IFACE_NAME_LENGTH];
|
|
|
|
#endif
|
|
|
|
/* thread specific bpf */
|
|
|
|
struct bpf_program filter;
|
|
|
|
|
|
|
|
/* data link type for the thread */
|
|
|
|
int datalink;
|
|
|
|
|
|
|
|
/* counters */
|
|
|
|
uint32_t pkts;
|
|
|
|
uint64_t bytes;
|
|
|
|
uint32_t errs;
|
|
|
|
|
|
|
|
/* pcap buffer size */
|
|
|
|
int pcap_buffer_size;
|
|
|
|
|
|
|
|
ThreadVars *tv;
|
|
|
|
TmSlot *slot;
|
|
|
|
|
|
|
|
Packet *in_p;
|
|
|
|
|
|
|
|
Packet *array[PCAP_FILE_MAX_PKTS];
|
|
|
|
uint16_t array_idx;
|
|
|
|
} PcapThreadVars;
|
|
|
|
|
|
|
|
TmEcode ReceivePcap(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
|
|
|
|
TmEcode ReceivePcapThreadInit(ThreadVars *, void *, void **);
|
|
|
|
void ReceivePcapThreadExitStats(ThreadVars *, void *);
|
|
|
|
TmEcode ReceivePcapThreadDeinit(ThreadVars *, void *);
|
|
|
|
TmEcode ReceivePcapLoop(ThreadVars *tv, void *data, void *slot);
|
|
|
|
|
|
|
|
TmEcode DecodePcapThreadInit(ThreadVars *, void *, void **);
|
|
|
|
TmEcode DecodePcap(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Registration Function for RecievePcap.
|
|
|
|
* \todo Unit tests are needed for this module.
|
|
|
|
*/
|
|
|
|
void TmModuleReceivePcapRegister (void) {
|
|
|
|
tmm_modules[TMM_RECEIVEPCAP].name = "ReceivePcap";
|
|
|
|
tmm_modules[TMM_RECEIVEPCAP].ThreadInit = ReceivePcapThreadInit;
|
|
|
|
tmm_modules[TMM_RECEIVEPCAP].Func = ReceivePcap;
|
|
|
|
tmm_modules[TMM_RECEIVEPCAP].PktAcqLoop = ReceivePcapLoop;
|
|
|
|
tmm_modules[TMM_RECEIVEPCAP].ThreadExitPrintStats = ReceivePcapThreadExitStats;
|
|
|
|
tmm_modules[TMM_RECEIVEPCAP].ThreadDeinit = NULL;
|
|
|
|
tmm_modules[TMM_RECEIVEPCAP].RegisterTests = NULL;
|
|
|
|
tmm_modules[TMM_RECEIVEPCAP].cap_flags = SC_CAP_NET_RAW;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Registration Function for DecodePcap.
|
|
|
|
* \todo Unit tests are needed for this module.
|
|
|
|
*/
|
|
|
|
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].ThreadExitPrintStats = NULL;
|
|
|
|
tmm_modules[TMM_DECODEPCAP].ThreadDeinit = NULL;
|
|
|
|
tmm_modules[TMM_DECODEPCAP].RegisterTests = NULL;
|
|
|
|
tmm_modules[TMM_DECODEPCAP].cap_flags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Pcap callback function.
|
|
|
|
*
|
|
|
|
* This function fills in our packet structure from libpcap.
|
|
|
|
* From here the packets are picked up by the DecodePcap thread.
|
|
|
|
*
|
|
|
|
* \param user pointer to PcapThreadVars passed from pcap_dispatch
|
|
|
|
* \param h pointer to pcap packet header
|
|
|
|
* \param pkt pointer to raw packet data
|
|
|
|
*/
|
|
|
|
void PcapCallback(char *user, struct pcap_pkthdr *h, u_char *pkt) {
|
|
|
|
SCLogDebug("user %p, h %p, pkt %p", user, h, pkt);
|
|
|
|
PcapThreadVars *ptv = (PcapThreadVars *)user;
|
|
|
|
|
|
|
|
Packet *p = NULL;
|
|
|
|
if (ptv->array_idx == 0) {
|
|
|
|
p = ptv->in_p;
|
|
|
|
} else {
|
|
|
|
p = PacketGetFromQueueOrAlloc();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p == NULL) {
|
|
|
|
SCReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->ts.tv_sec = h->ts.tv_sec;
|
|
|
|
p->ts.tv_usec = h->ts.tv_usec;
|
|
|
|
|
|
|
|
ptv->pkts++;
|
|
|
|
ptv->bytes += h->caplen;
|
|
|
|
|
|
|
|
p->datalink = ptv->datalink;
|
|
|
|
SET_PKT_LEN(p, h->caplen);
|
|
|
|
if (PacketCopyData(p, pkt, GET_PKT_LEN(p)) == -1)
|
|
|
|
SCReturn;
|
|
|
|
SCLogDebug("pktlen: %" PRIu32 " (pkt %02x, pkt data %02x)",
|
|
|
|
GET_PKT_LEN(p), *pkt, *GET_PKT_DATA(p));
|
|
|
|
|
|
|
|
/* store the packet in our array */
|
|
|
|
ptv->array[ptv->array_idx] = p;
|
|
|
|
ptv->array_idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if LIBPCAP_VERSION_MAJOR == 1
|
|
|
|
static int PcapTryReopen(PcapThreadVars *ptv)
|
|
|
|
{
|
|
|
|
int pcap_activate_r;
|
|
|
|
char *tmpbpfstring;
|
|
|
|
|
|
|
|
ptv->pcap_state = PCAP_STATE_DOWN;
|
|
|
|
pcap_activate_r = pcap_activate(ptv->pcap_handle);
|
|
|
|
if (pcap_activate_r != 0) {
|
|
|
|
return pcap_activate_r;
|
|
|
|
}
|
|
|
|
/* set bpf filter if we have one */
|
|
|
|
if (ConfGet("bpf-filter", &tmpbpfstring) != 1) {
|
|
|
|
SCLogDebug("could not get bpf or none specified");
|
|
|
|
} else {
|
|
|
|
SCLogInfo("using bpf-filter \"%s\"", tmpbpfstring);
|
|
|
|
|
|
|
|
if(pcap_compile(ptv->pcap_handle,&ptv->filter,tmpbpfstring,1,0) < 0) {
|
|
|
|
SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(ptv->pcap_handle));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pcap_setfilter(ptv->pcap_handle,&ptv->filter) < 0) {
|
|
|
|
SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(ptv->pcap_handle));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SCLogInfo("Recovering interface listening");
|
|
|
|
ptv->pcap_state = PCAP_STATE_UP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else /* implied LIBPCAP_VERSION_MAJOR == 0 */
|
|
|
|
static int PcapTryReopen(PcapThreadVars *ptv)
|
|
|
|
{
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE] = "";
|
|
|
|
char *tmpbpfstring;
|
|
|
|
|
|
|
|
ptv->pcap_state = PCAP_STATE_DOWN;
|
|
|
|
pcap_close(ptv->pcap_handle);
|
|
|
|
|
|
|
|
ptv->pcap_handle = pcap_open_live((char *)ptv->iface, LIBPCAP_SNAPLEN,
|
|
|
|
LIBPCAP_PROMISC, LIBPCAP_COPYWAIT, errbuf);
|
|
|
|
if (ptv->pcap_handle == NULL) {
|
|
|
|
SCLogError(SC_ERR_PCAP_OPEN_LIVE, "Problem creating pcap handler for live mode, error %s", errbuf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set bpf filter if we have one */
|
|
|
|
if (ConfGet("bpf-filter", &tmpbpfstring) != 1) {
|
|
|
|
SCLogDebug("could not get bpf or none specified");
|
|
|
|
} else {
|
|
|
|
SCLogInfo("using bpf-filter \"%s\"", tmpbpfstring);
|
|
|
|
|
|
|
|
if(pcap_compile(ptv->pcap_handle,&ptv->filter,tmpbpfstring,1,0) < 0) {
|
|
|
|
SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(ptv->pcap_handle));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pcap_setfilter(ptv->pcap_handle,&ptv->filter) < 0) {
|
|
|
|
SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(ptv->pcap_handle));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SCLogInfo("Recovering interface listening");
|
|
|
|
ptv->pcap_state = PCAP_STATE_UP;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void PcapCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt) {
|
|
|
|
SCEnter();
|
|
|
|
|
|
|
|
PcapThreadVars *ptv = (PcapThreadVars *)user;
|
|
|
|
Packet *p = PacketGetFromQueueOrAlloc();
|
|
|
|
|
|
|
|
if (unlikely(p == NULL)) {
|
|
|
|
SCReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->ts.tv_sec = h->ts.tv_sec;
|
|
|
|
p->ts.tv_usec = h->ts.tv_usec;
|
|
|
|
SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec);
|
|
|
|
p->datalink = ptv->datalink;
|
|
|
|
|
|
|
|
ptv->pkts++;
|
|
|
|
ptv->bytes += h->caplen;
|
|
|
|
|
|
|
|
if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
|
|
|
|
TmqhOutputPacketpool(ptv->tv, p);
|
|
|
|
SCReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p);
|
|
|
|
|
|
|
|
SCReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Main PCAP reading Loop function
|
|
|
|
*/
|
|
|
|
TmEcode ReceivePcapLoop(ThreadVars *tv, void *data, void *slot)
|
|
|
|
{
|
|
|
|
uint16_t packet_q_len = 0;
|
|
|
|
PcapThreadVars *ptv = (PcapThreadVars *)data;
|
|
|
|
TmSlot *s = (TmSlot *)slot;
|
|
|
|
ptv->slot = s->slot_next;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
SCEnter();
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (suricata_ctl_flags & SURICATA_STOP ||
|
|
|
|
suricata_ctl_flags & SURICATA_KILL)
|
|
|
|
{
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure we have at least one packet in the packet pool, to prevent
|
|
|
|
* us from alloc'ing packets at line rate */
|
|
|
|
do {
|
|
|
|
packet_q_len = PacketPoolSize();
|
|
|
|
if (unlikely(packet_q_len == 0)) {
|
|
|
|
PacketPoolWait();
|
|
|
|
}
|
|
|
|
} while (packet_q_len == 0);
|
|
|
|
|
|
|
|
/* Right now we just support reading packets one at a time. */
|
|
|
|
r = pcap_dispatch(ptv->pcap_handle, (int)packet_q_len,
|
|
|
|
(pcap_handler)PcapCallbackLoop, (u_char *)ptv);
|
|
|
|
if (unlikely(r < 0)) {
|
|
|
|
int dbreak = 0;
|
|
|
|
SCLogError(SC_ERR_PCAP_DISPATCH, "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) {
|
|
|
|
r = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Recieves packets from an interface via libpcap.
|
|
|
|
*
|
|
|
|
* This function recieves packets from an interface and passes
|
|
|
|
* the packet on to the pcap callback function.
|
|
|
|
*
|
|
|
|
* \param tv pointer to ThreadVars
|
|
|
|
* \param data pointer that gets cast into PcapThreadVars for ptv
|
|
|
|
* \param pq pointer to the PacketQueue (not used here but part of the api)
|
|
|
|
* \retval TM_ECODE_FAILED on failure and TM_ECODE_OK on success
|
|
|
|
*/
|
|
|
|
TmEcode ReceivePcap(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) {
|
|
|
|
SCEnter();
|
|
|
|
uint16_t packet_q_len = 0;
|
|
|
|
|
|
|
|
PcapThreadVars *ptv = (PcapThreadVars *)data;
|
|
|
|
|
|
|
|
/* test pcap handle */
|
|
|
|
if (ptv->pcap_state == PCAP_STATE_DOWN) {
|
|
|
|
int r;
|
|
|
|
do {
|
|
|
|
usleep(PCAP_RECONNECT_TIMEOUT);
|
|
|
|
if (suricata_ctl_flags != 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
r = PcapTryReopen(ptv);
|
|
|
|
} while (r < 0);
|
|
|
|
}
|
|
|
|
/* make sure we have at least one packet in the packet pool, to prevent
|
|
|
|
* us from alloc'ing packets at line rate */
|
|
|
|
while (packet_q_len == 0) {
|
|
|
|
packet_q_len = PacketPoolSize();
|
|
|
|
if (packet_q_len == 0) {
|
|
|
|
PacketPoolWait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (postpq == NULL)
|
|
|
|
pcap_max_read_packets = 1;
|
|
|
|
|
|
|
|
ptv->array_idx = 0;
|
|
|
|
ptv->in_p = p;
|
|
|
|
|
|
|
|
int r = 0;
|
|
|
|
while (r == 0) {
|
|
|
|
r = pcap_dispatch(ptv->pcap_handle, (pcap_max_read_packets < packet_q_len) ? pcap_max_read_packets : packet_q_len,
|
|
|
|
(pcap_handler)PcapCallback, (u_char *)ptv);
|
|
|
|
if (suricata_ctl_flags != 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (r < 0) {
|
|
|
|
int dbreak = 0;
|
|
|
|
SCLogError(SC_ERR_PCAP_DISPATCH, "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) {
|
|
|
|
r = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t cnt = 0;
|
|
|
|
for (cnt = 0; cnt < ptv->array_idx; cnt++) {
|
|
|
|
Packet *pp = ptv->array[cnt];
|
|
|
|
|
|
|
|
/* enqueue all but the first in the postpq, the first
|
|
|
|
* pkt is handled by the tv "out handler" */
|
|
|
|
if (cnt > 0) {
|
|
|
|
PacketEnqueue(postpq, pp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s",
|
|
|
|
r, pcap_geterr(ptv->pcap_handle));
|
|
|
|
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (suricata_ctl_flags != 0) {
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Init function for ReceivePcap.
|
|
|
|
*
|
|
|
|
* This is a setup function for recieving 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.
|
|
|
|
*/
|
|
|
|
#if LIBPCAP_VERSION_MAJOR == 1
|
|
|
|
TmEcode ReceivePcapThreadInit(ThreadVars *tv, void *initdata, void **data) {
|
|
|
|
SCEnter();
|
|
|
|
char *tmpbpfstring;
|
|
|
|
|
|
|
|
/* use max_pending_packets as pcap read size unless it's bigger than
|
|
|
|
* our size limit */
|
|
|
|
pcap_max_read_packets = (PCAP_FILE_MAX_PKTS < max_pending_packets) ?
|
|
|
|
PCAP_FILE_MAX_PKTS : max_pending_packets;
|
|
|
|
|
|
|
|
if (initdata == NULL) {
|
|
|
|
SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
PcapThreadVars *ptv = SCMalloc(sizeof(PcapThreadVars));
|
|
|
|
if (ptv == NULL)
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
memset(ptv, 0, sizeof(PcapThreadVars));
|
|
|
|
|
|
|
|
ptv->tv = tv;
|
|
|
|
|
|
|
|
SCLogInfo("using interface %s", (char *)initdata);
|
|
|
|
/* XXX create a general pcap setup function */
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
|
|
ptv->pcap_handle = pcap_create((char *)initdata, errbuf);
|
|
|
|
if (ptv->pcap_handle == NULL) {
|
|
|
|
SCLogError(SC_ERR_PCAP_CREATE, "Coudn't create a new pcap handler, error %s", pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set Snaplen, Promisc, and Timeout. Must be called before pcap_activate */
|
|
|
|
int pcap_set_snaplen_r = pcap_set_snaplen(ptv->pcap_handle,LIBPCAP_SNAPLEN);
|
|
|
|
//printf("ReceivePcapThreadInit: pcap_set_snaplen(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_snaplen_r);
|
|
|
|
if (pcap_set_snaplen_r != 0) {
|
|
|
|
SCLogError(SC_ERR_PCAP_SET_SNAPLEN, "Couldn't set snaplen, error: %s", pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_set_promisc_r = pcap_set_promisc(ptv->pcap_handle,LIBPCAP_PROMISC);
|
|
|
|
//printf("ReceivePcapThreadInit: pcap_set_promisc(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_promisc_r);
|
|
|
|
if (pcap_set_promisc_r != 0) {
|
|
|
|
SCLogError(SC_ERR_PCAP_SET_PROMISC, "Couldn't set promisc mode, error %s", pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pcap_set_timeout_r = pcap_set_timeout(ptv->pcap_handle,LIBPCAP_COPYWAIT);
|
|
|
|
//printf("ReceivePcapThreadInit: pcap_set_timeout(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_timeout_r);
|
|
|
|
if (pcap_set_timeout_r != 0) {
|
|
|
|
SCLogError(SC_ERR_PCAP_SET_TIMEOUT, "Problems setting timeout, error %s", pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
#ifdef HAVE_PCAP_SET_BUFF
|
|
|
|
char *tmppcapbuffsize;
|
|
|
|
/* set pcap buffer size if specified and supported. Must be done prior to activating the handle */
|
|
|
|
if (ConfGet("pcap.buffer-size", &tmppcapbuffsize) == 1){
|
|
|
|
if (atoi(tmppcapbuffsize) >= 0 && atoi(tmppcapbuffsize) <= INT_MAX) {
|
|
|
|
ptv->pcap_buffer_size = (int)atoi(tmppcapbuffsize);
|
|
|
|
SCLogInfo("Going to use pcap buffer size of %" PRId32 "", ptv->pcap_buffer_size);
|
|
|
|
|
|
|
|
int pcap_set_buffer_size_r = pcap_set_buffer_size(ptv->pcap_handle,ptv->pcap_buffer_size);
|
|
|
|
//printf("ReceivePcapThreadInit: pcap_set_timeout(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_set_buffer_size_r);
|
|
|
|
if (pcap_set_buffer_size_r != 0) {
|
|
|
|
SCLogError(SC_ERR_PCAP_SET_BUFF_SIZE, "Problems setting pcap buffer size, error %s", pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* HAVE_PCAP_SET_BUFF */
|
|
|
|
|
|
|
|
/* activate the handle */
|
|
|
|
int pcap_activate_r = pcap_activate(ptv->pcap_handle);
|
|
|
|
//printf("ReceivePcapThreadInit: pcap_activate(%p) returned %" PRId32 "\n", ptv->pcap_handle, pcap_activate_r);
|
|
|
|
if (pcap_activate_r != 0) {
|
|
|
|
SCLogError(SC_ERR_PCAP_ACTIVATE_HANDLE, "Couldn't activate the pcap handler, error %s", pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
ptv->pcap_state = PCAP_STATE_DOWN;
|
|
|
|
} else {
|
|
|
|
ptv->pcap_state = PCAP_STATE_UP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set bpf filter if we have one */
|
|
|
|
if (ConfGet("bpf-filter", &tmpbpfstring) != 1) {
|
|
|
|
SCLogDebug("could not get bpf or none specified");
|
|
|
|
} else {
|
|
|
|
SCLogInfo("using bpf-filter \"%s\"", tmpbpfstring);
|
|
|
|
|
|
|
|
if(pcap_compile(ptv->pcap_handle,&ptv->filter,tmpbpfstring,1,0) < 0) {
|
|
|
|
SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
return TM_ECODE_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pcap_setfilter(ptv->pcap_handle,&ptv->filter) < 0) {
|
|
|
|
SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
return TM_ECODE_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ptv->datalink = pcap_datalink(ptv->pcap_handle);
|
|
|
|
|
|
|
|
*data = (void *)ptv;
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
#else /* implied LIBPCAP_VERSION_MAJOR == 0 */
|
|
|
|
TmEcode ReceivePcapThreadInit(ThreadVars *tv, void *initdata, void **data) {
|
|
|
|
SCEnter();
|
|
|
|
|
|
|
|
char *tmpbpfstring;
|
|
|
|
|
|
|
|
/* use max_pending_packets as pcap read size unless it's bigger than
|
|
|
|
* our size limit */
|
|
|
|
pcap_max_read_packets = (PCAP_FILE_MAX_PKTS < max_pending_packets) ?
|
|
|
|
PCAP_FILE_MAX_PKTS : max_pending_packets;
|
|
|
|
|
|
|
|
if (initdata == NULL) {
|
|
|
|
SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
PcapThreadVars *ptv = SCMalloc(sizeof(PcapThreadVars));
|
|
|
|
if (ptv == NULL)
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
memset(ptv, 0, sizeof(PcapThreadVars));
|
|
|
|
|
|
|
|
ptv->tv = tv;
|
|
|
|
|
|
|
|
SCLogInfo("using interface %s", (char *)initdata);
|
|
|
|
if(strlen(initdata)>PCAP_IFACE_NAME_LENGTH) {
|
|
|
|
SCFree(ptv);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
strlcpy(ptv->iface, (char *)initdata, PCAP_IFACE_NAME_LENGTH);
|
|
|
|
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE] = "";
|
|
|
|
ptv->pcap_handle = pcap_open_live((char *)initdata, LIBPCAP_SNAPLEN,
|
|
|
|
LIBPCAP_PROMISC, LIBPCAP_COPYWAIT, errbuf);
|
|
|
|
if (ptv->pcap_handle == NULL) {
|
|
|
|
SCLogError(SC_ERR_PCAP_OPEN_LIVE, "Problem creating pcap handler for live mode, error %s", errbuf);
|
|
|
|
SCFree(ptv);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set bpf filter if we have one */
|
|
|
|
if (ConfGet("bpf-filter", &tmpbpfstring) != 1) {
|
|
|
|
SCLogDebug("could not get bpf or none specified");
|
|
|
|
} else {
|
|
|
|
SCLogInfo("using bpf-filter \"%s\"", tmpbpfstring);
|
|
|
|
|
|
|
|
if(pcap_compile(ptv->pcap_handle,&ptv->filter,tmpbpfstring,1,0) < 0) {
|
|
|
|
SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
return TM_ECODE_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pcap_setfilter(ptv->pcap_handle,&ptv->filter) < 0) {
|
|
|
|
SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCFree(ptv);
|
|
|
|
return TM_ECODE_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ptv->datalink = pcap_datalink(ptv->pcap_handle);
|
|
|
|
|
|
|
|
*data = (void *)ptv;
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
#endif /* LIBPCAP_VERSION_MAJOR */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \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
|
|
|
|
*/
|
|
|
|
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(SC_ERR_STAT,"(%s) Failed to get pcap_stats: %s", tv->name, pcap_geterr(ptv->pcap_handle));
|
|
|
|
SCLogInfo("(%s) Packets %" PRIu32 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes);
|
|
|
|
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
SCLogInfo("(%s) Packets %" PRIu32 ", bytes %" PRIu64 "", tv->name, 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.
|
|
|
|
*/
|
|
|
|
SCLogInfo("(%s) Pcap Total:%" PRIu64 " Recv:%" PRIu64 " Drop:%" PRIu64 " (%02.1f%%).", tv->name,
|
|
|
|
(uint64_t)pcap_s.ps_recv, (uint64_t)pcap_s.ps_recv - (uint64_t)pcap_s.ps_drop, (uint64_t)pcap_s.ps_drop,
|
|
|
|
(((float)(uint64_t)pcap_s.ps_drop)/(float)(uint64_t)pcap_s.ps_recv)*100);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief DeInit function closes pcap_handle at exit.
|
|
|
|
* \param tv pointer to ThreadVars
|
|
|
|
* \param data pointer that gets cast into PcapThreadVars for ptv
|
|
|
|
*/
|
|
|
|
TmEcode ReceivePcapThreadDeinit(ThreadVars *tv, void *data) {
|
|
|
|
PcapThreadVars *ptv = (PcapThreadVars *)data;
|
|
|
|
|
|
|
|
pcap_close(ptv->pcap_handle);
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief This function passes off to link type decoders.
|
|
|
|
*
|
|
|
|
* DecodePcap reads packets from the PacketQueue 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
|
|
|
|
* \param pq pointer to the current PacketQueue
|
|
|
|
*/
|
|
|
|
TmEcode DecodePcap(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
|
|
|
|
{
|
|
|
|
SCEnter();
|
|
|
|
DecodeThreadVars *dtv = (DecodeThreadVars *)data;
|
|
|
|
|
|
|
|
/* update counters */
|
|
|
|
SCPerfCounterIncr(dtv->counter_pkts, tv->sc_perf_pca);
|
|
|
|
SCPerfCounterIncr(dtv->counter_pkts_per_sec, tv->sc_perf_pca);
|
|
|
|
|
|
|
|
SCPerfCounterAddUI64(dtv->counter_bytes, tv->sc_perf_pca, GET_PKT_LEN(p));
|
|
|
|
#if 0
|
|
|
|
SCPerfCounterAddDouble(dtv->counter_bytes_per_sec, tv->sc_perf_pca, GET_PKT_LEN(p));
|
|
|
|
SCPerfCounterAddDouble(dtv->counter_mbit_per_sec, tv->sc_perf_pca,
|
|
|
|
(GET_PKT_LEN(p) * 8)/1000000.0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SCPerfCounterAddUI64(dtv->counter_avg_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p));
|
|
|
|
SCPerfCounterSetUI64(dtv->counter_max_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p));
|
|
|
|
|
|
|
|
/* call the decoder */
|
|
|
|
switch(p->datalink) {
|
|
|
|
case LINKTYPE_LINUX_SLL:
|
|
|
|
DecodeSll(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
|
|
|
|
break;
|
|
|
|
case LINKTYPE_ETHERNET:
|
|
|
|
DecodeEthernet(tv, dtv, p,GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
|
|
|
|
break;
|
|
|
|
case LINKTYPE_PPP:
|
|
|
|
DecodePPP(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
|
|
|
|
break;
|
|
|
|
case LINKTYPE_RAW:
|
|
|
|
DecodeRaw(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SCLogError(SC_ERR_DATALINK_UNIMPLEMENTED, "Error: datalink type %" PRId32 " not yet supported in module DecodePcap", p->datalink);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
TmEcode DecodePcapThreadInit(ThreadVars *tv, void *initdata, void **data)
|
|
|
|
{
|
|
|
|
SCEnter();
|
|
|
|
DecodeThreadVars *dtv = NULL;
|
|
|
|
|
|
|
|
dtv = DecodeThreadVarsAlloc();
|
|
|
|
|
|
|
|
if (dtv == NULL)
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
|
|
|
|
DecodeRegisterPerfCounters(dtv, tv);
|
|
|
|
|
|
|
|
*data = (void *)dtv;
|
|
|
|
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PcapTranslateIPToDevice(char *pcap_dev, size_t len)
|
|
|
|
{
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
|
|
pcap_if_t *alldevsp = NULL;
|
|
|
|
pcap_if_t *devsp = NULL;
|
|
|
|
|
|
|
|
struct addrinfo aiHints;
|
|
|
|
struct addrinfo *aiList = NULL;
|
|
|
|
int retVal = 0;
|
|
|
|
|
|
|
|
memset(&aiHints, 0, sizeof(aiHints));
|
|
|
|
aiHints.ai_family = AF_UNSPEC;
|
|
|
|
aiHints.ai_flags = AI_NUMERICHOST;
|
|
|
|
|
|
|
|
/* try to translate IP */
|
|
|
|
if ((retVal = getaddrinfo(pcap_dev, NULL, &aiHints, &aiList)) != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcap_findalldevs(&alldevsp, errbuf)) {
|
|
|
|
freeaddrinfo(aiList);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (devsp = alldevsp; devsp ; devsp = devsp->next) {
|
|
|
|
pcap_addr_t *ip = NULL;
|
|
|
|
|
|
|
|
for (ip = devsp->addresses; ip ; ip = ip->next) {
|
|
|
|
|
|
|
|
if (aiList->ai_family != ip->addr->sa_family) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ip->addr->sa_family == AF_INET) {
|
|
|
|
if (memcmp(&((struct sockaddr_in*)aiList->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*)aiList->ai_addr)->sin6_addr, &((struct sockaddr_in6*)ip->addr)->sin6_addr, sizeof(struct in6_addr))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(aiList);
|
|
|
|
|
|
|
|
memset(pcap_dev, 0, len);
|
|
|
|
strlcpy(pcap_dev, devsp->name, len);
|
|
|
|
|
|
|
|
pcap_freealldevs(alldevsp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(aiList);
|
|
|
|
|
|
|
|
pcap_freealldevs(alldevsp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* eof */
|
|
|
|
|