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-file.c

464 lines
15 KiB
C

/* 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>
*
* File based 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 "source-pcap-file.h"
#include "util-time.h"
#include "util-debug.h"
#include "conf.h"
#include "util-error.h"
#include "util-privs.h"
#include "tmqh-packetpool.h"
#include "tm-threads.h"
#include "util-optimize.h"
#include "flow-manager.h"
#include "util-profiling.h"
#include "runmode-unix-socket.h"
#include "util-checksum.h"
#include "util-atomic.h"
#ifdef __SC_CUDA_SUPPORT__
#include "util-cuda.h"
#include "util-cuda-buffer.h"
#include "util-mpm-ac.h"
#include "util-cuda-handlers.h"
#include "detect-engine.h"
#include "detect-engine-mpm.h"
#include "util-cuda-vars.h"
#endif /* __SC_CUDA_SUPPORT__ */
extern uint8_t suricata_ctl_flags;
extern int max_pending_packets;
//static int pcap_max_read_packets = 0;
typedef struct PcapFileGlobalVars_ {
pcap_t *pcap_handle;
int (*Decoder)(ThreadVars *, DecodeThreadVars *, Packet *, u_int8_t *, u_int16_t, PacketQueue *);
int datalink;
struct bpf_program filter;
uint64_t cnt; /** packet counter */
ChecksumValidationMode conf_checksum_mode;
ChecksumValidationMode checksum_mode;
SC_ATOMIC_DECLARE(unsigned int, invalid_checksums);
} PcapFileGlobalVars;
/** max packets < 65536 */
//#define PCAP_FILE_MAX_PKTS 256
typedef struct PcapFileThreadVars_
{
/* counters */
uint32_t pkts;
uint64_t bytes;
ThreadVars *tv;
TmSlot *slot;
/** callback result -- set if one of the thread module failed. */
int cb_result;
uint8_t done;
uint32_t errs;
} PcapFileThreadVars;
static PcapFileGlobalVars pcap_g;
TmEcode ReceivePcapFileLoop(ThreadVars *, void *, void *);
TmEcode ReceivePcapFileThreadInit(ThreadVars *, void *, void **);
void ReceivePcapFileThreadExitStats(ThreadVars *, void *);
TmEcode ReceivePcapFileThreadDeinit(ThreadVars *, void *);
TmEcode DecodePcapFile(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
TmEcode DecodePcapFileThreadInit(ThreadVars *, void *, void **);
TmEcode DecodePcapFileThreadDeinit(ThreadVars *tv, void *data);
void TmModuleReceivePcapFileRegister (void) {
memset(&pcap_g, 0x00, sizeof(pcap_g));
tmm_modules[TMM_RECEIVEPCAPFILE].name = "ReceivePcapFile";
tmm_modules[TMM_RECEIVEPCAPFILE].ThreadInit = ReceivePcapFileThreadInit;
tmm_modules[TMM_RECEIVEPCAPFILE].Func = NULL;
tmm_modules[TMM_RECEIVEPCAPFILE].PktAcqLoop = ReceivePcapFileLoop;
tmm_modules[TMM_RECEIVEPCAPFILE].ThreadExitPrintStats = ReceivePcapFileThreadExitStats;
tmm_modules[TMM_RECEIVEPCAPFILE].ThreadDeinit = ReceivePcapFileThreadDeinit;
tmm_modules[TMM_RECEIVEPCAPFILE].RegisterTests = NULL;
tmm_modules[TMM_RECEIVEPCAPFILE].cap_flags = 0;
tmm_modules[TMM_RECEIVEPCAPFILE].flags = TM_FLAG_RECEIVE_TM;
}
void TmModuleDecodePcapFileRegister (void) {
tmm_modules[TMM_DECODEPCAPFILE].name = "DecodePcapFile";
tmm_modules[TMM_DECODEPCAPFILE].ThreadInit = DecodePcapFileThreadInit;
tmm_modules[TMM_DECODEPCAPFILE].Func = DecodePcapFile;
tmm_modules[TMM_DECODEPCAPFILE].ThreadExitPrintStats = NULL;
tmm_modules[TMM_DECODEPCAPFILE].ThreadDeinit = DecodePcapFileThreadDeinit;
tmm_modules[TMM_DECODEPCAPFILE].RegisterTests = NULL;
tmm_modules[TMM_DECODEPCAPFILE].cap_flags = 0;
tmm_modules[TMM_DECODEPCAPFILE].flags = TM_FLAG_DECODE_TM;
}
void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt) {
SCEnter();
PcapFileThreadVars *ptv = (PcapFileThreadVars *)user;
Packet *p = PacketGetFromQueueOrAlloc();
if (unlikely(p == NULL)) {
SCReturn;
}
PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE);
PKT_SET_SRC(p, PKT_SRC_WIRE);
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 = pcap_g.datalink;
p->pcap_cnt = ++pcap_g.cnt;
ptv->pkts++;
ptv->bytes += h->caplen;
if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
TmqhOutputPacketpool(ptv->tv, p);
PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
SCReturn;
}
/* We only check for checksum disable */
if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
p->flags |= PKT_IGNORE_CHECKSUM;
} else if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_AUTO) {
if (ChecksumAutoModeCheck(ptv->pkts, p->pcap_cnt,
SC_ATOMIC_GET(pcap_g.invalid_checksums))) {
pcap_g.checksum_mode = CHECKSUM_VALIDATION_DISABLE;
p->flags |= PKT_IGNORE_CHECKSUM;
}
}
PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) {
pcap_breakloop(pcap_g.pcap_handle);
ptv->cb_result = TM_ECODE_FAILED;
}
SCReturn;
}
/**
* \brief Main PCAP file reading Loop function
*/
TmEcode ReceivePcapFileLoop(ThreadVars *tv, void *data, void *slot)
{
SCEnter();
uint16_t packet_q_len = 0;
PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
int r;
TmSlot *s = (TmSlot *)slot;
ptv->slot = s->slot_next;
ptv->cb_result = TM_ECODE_OK;
while (1) {
if (suricata_ctl_flags & (SURICATA_STOP | SURICATA_KILL)) {
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 */
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(pcap_g.pcap_handle, (int)packet_q_len,
(pcap_handler)PcapFileCallbackLoop, (u_char *)ptv);
if (unlikely(r == -1)) {
SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s",
r, pcap_geterr(pcap_g.pcap_handle));
if (! RunModeUnixSocketIsActive()) {
/* in the error state we just kill the engine */
EngineKill();
SCReturnInt(TM_ECODE_FAILED);
} else {
pcap_close(pcap_g.pcap_handle);
pcap_g.pcap_handle = NULL;
UnixSocketPcapFile(TM_ECODE_DONE);
SCReturnInt(TM_ECODE_DONE);
}
} else if (unlikely(r == 0)) {
SCLogInfo("pcap file end of file reached (pcap err code %" PRId32 ")", r);
if (! RunModeUnixSocketIsActive()) {
EngineStop();
} else {
pcap_close(pcap_g.pcap_handle);
pcap_g.pcap_handle = NULL;
UnixSocketPcapFile(TM_ECODE_DONE);
SCReturnInt(TM_ECODE_DONE);
}
break;
} else if (ptv->cb_result == TM_ECODE_FAILED) {
SCLogError(SC_ERR_PCAP_DISPATCH, "Pcap callback PcapFileCallbackLoop failed");
if (! RunModeUnixSocketIsActive()) {
EngineKill();
SCReturnInt(TM_ECODE_FAILED);
} else {
pcap_close(pcap_g.pcap_handle);
pcap_g.pcap_handle = NULL;
UnixSocketPcapFile(TM_ECODE_DONE);
SCReturnInt(TM_ECODE_DONE);
}
}
SCPerfSyncCountersIfSignalled(tv);
}
SCReturnInt(TM_ECODE_OK);
}
TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, void *initdata, void **data) {
SCEnter();
char *tmpbpfstring = NULL;
char *tmpstring = NULL;
if (initdata == NULL) {
SCLogError(SC_ERR_INVALID_ARGUMENT, "error: initdata == NULL");
SCReturnInt(TM_ECODE_FAILED);
}
SCLogInfo("reading pcap file %s", (char *)initdata);
PcapFileThreadVars *ptv = SCMalloc(sizeof(PcapFileThreadVars));
if (unlikely(ptv == NULL))
SCReturnInt(TM_ECODE_FAILED);
memset(ptv, 0, sizeof(PcapFileThreadVars));
char errbuf[PCAP_ERRBUF_SIZE] = "";
pcap_g.pcap_handle = pcap_open_offline((char *)initdata, errbuf);
if (pcap_g.pcap_handle == NULL) {
SCLogError(SC_ERR_FOPEN, "%s\n", errbuf);
SCFree(ptv);
if (! RunModeUnixSocketIsActive()) {
return TM_ECODE_FAILED;
} else {
UnixSocketPcapFile(TM_ECODE_FAILED);
SCReturnInt(TM_ECODE_DONE);
}
}
if (ConfGet("bpf-filter", &tmpbpfstring) != 1) {
SCLogDebug("could not get bpf or none specified");
} else {
SCLogInfo("using bpf-filter \"%s\"", tmpbpfstring);
if(pcap_compile(pcap_g.pcap_handle,&pcap_g.filter,tmpbpfstring,1,0) < 0) {
SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(pcap_g.pcap_handle));
SCFree(ptv);
return TM_ECODE_FAILED;
}
if(pcap_setfilter(pcap_g.pcap_handle,&pcap_g.filter) < 0) {
SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(pcap_g.pcap_handle));
SCFree(ptv);
return TM_ECODE_FAILED;
}
}
pcap_g.datalink = pcap_datalink(pcap_g.pcap_handle);
SCLogDebug("datalink %" PRId32 "", pcap_g.datalink);
switch(pcap_g.datalink) {
case LINKTYPE_LINUX_SLL:
pcap_g.Decoder = DecodeSll;
break;
case LINKTYPE_ETHERNET:
pcap_g.Decoder = DecodeEthernet;
break;
case LINKTYPE_PPP:
pcap_g.Decoder = DecodePPP;
break;
case LINKTYPE_RAW:
pcap_g.Decoder = DecodeRaw;
break;
default:
SCLogError(SC_ERR_UNIMPLEMENTED, "datalink type %" PRId32 " not "
"(yet) supported in module PcapFile.\n", pcap_g.datalink);
SCFree(ptv);
if (! RunModeUnixSocketIsActive()) {
SCReturnInt(TM_ECODE_FAILED);
} else {
pcap_close(pcap_g.pcap_handle);
pcap_g.pcap_handle = NULL;
UnixSocketPcapFile(TM_ECODE_DONE);
SCReturnInt(TM_ECODE_DONE);
}
}
if (ConfGet("pcap-file.checksum-checks", &tmpstring) != 1) {
pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_AUTO;
} else {
if (strcmp(tmpstring, "auto") == 0) {
pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_AUTO;
} else if (strcmp(tmpstring, "yes") == 0) {
pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_ENABLE;
} else if (strcmp(tmpstring, "no") == 0) {
pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_DISABLE;
}
}
pcap_g.checksum_mode = pcap_g.conf_checksum_mode;
ptv->tv = tv;
*data = (void *)ptv;
SCReturnInt(TM_ECODE_OK);
}
void ReceivePcapFileThreadExitStats(ThreadVars *tv, void *data) {
SCEnter();
PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
if (pcap_g.conf_checksum_mode == CHECKSUM_VALIDATION_AUTO &&
pcap_g.cnt < CHECKSUM_SAMPLE_COUNT &&
SC_ATOMIC_GET(pcap_g.invalid_checksums)) {
uint64_t chrate = pcap_g.cnt / SC_ATOMIC_GET(pcap_g.invalid_checksums);
if (chrate < CHECKSUM_INVALID_RATIO)
SCLogWarning(SC_ERR_INVALID_CHECKSUM,
"1/%" PRIu64 "th of packets have an invalid checksum,"
" consider setting pcap-file.checksum-checks variable to no"
" or use '-k none' option on command line.",
chrate);
else
SCLogInfo("1/%" PRIu64 "th of packets have an invalid checksum",
chrate);
}
SCLogNotice("Pcap-file module read %" PRIu32 " packets, %" PRIu64 " bytes", ptv->pkts, ptv->bytes);
return;
}
TmEcode ReceivePcapFileThreadDeinit(ThreadVars *tv, void *data) {
SCEnter();
PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
if (ptv) {
SCFree(ptv);
}
SCReturnInt(TM_ECODE_OK);
}
double prev_signaled_ts = 0;
TmEcode DecodePcapFile(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
{
SCEnter();
DecodeThreadVars *dtv = (DecodeThreadVars *)data;
/* XXX HACK: flow timeout can call us for injected pseudo packets
* see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
if (p->flags & PKT_PSEUDO_STREAM_END)
return TM_ECODE_OK;
/* 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));
double curr_ts = p->ts.tv_sec + p->ts.tv_usec / 1000.0;
if (curr_ts < prev_signaled_ts || (curr_ts - prev_signaled_ts) > 60.0) {
prev_signaled_ts = curr_ts;
FlowWakeupFlowManagerThread();
}
/* update the engine time representation based on the timestamp
* of the packet. */
TimeSet(&p->ts);
/* call the decoder */
pcap_g.Decoder(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
#ifdef DEBUG
BUG_ON(p->pkt_src != PKT_SRC_WIRE && p->pkt_src != PKT_SRC_FFR_V2);
#endif
PacketDecodeFinalize(tv, dtv, p);
SCReturnInt(TM_ECODE_OK);
}
TmEcode DecodePcapFileThreadInit(ThreadVars *tv, void *initdata, void **data)
{
SCEnter();
DecodeThreadVars *dtv = NULL;
dtv = DecodeThreadVarsAlloc(tv);
if (dtv == NULL)
SCReturnInt(TM_ECODE_FAILED);
DecodeRegisterPerfCounters(dtv, tv);
#ifdef __SC_CUDA_SUPPORT__
if (CudaThreadVarsInit(&dtv->cuda_vars) < 0)
SCReturnInt(TM_ECODE_FAILED);
#endif
*data = (void *)dtv;
SCReturnInt(TM_ECODE_OK);
}
TmEcode DecodePcapFileThreadDeinit(ThreadVars *tv, void *data)
{
if (data != NULL)
DecodeThreadVarsFree(data);
SCReturnInt(TM_ECODE_OK);
}
void PcapIncreaseInvalidChecksum()
{
(void) SC_ATOMIC_ADD(pcap_g.invalid_checksums, 1);
}
/* eof */