diff options
Diffstat (limited to 'framework/src/suricata/src/source-pcap-file.c')
-rw-r--r-- | framework/src/suricata/src/source-pcap-file.c | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/framework/src/suricata/src/source-pcap-file.c b/framework/src/suricata/src/source-pcap-file.c new file mode 100644 index 00000000..0b982fd3 --- /dev/null +++ b/framework/src/suricata/src/source-pcap-file.c @@ -0,0 +1,475 @@ +/* Copyright (C) 2007-2014 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 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_ +{ + uint32_t tenant_id; + + /* 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 PcapFileGlobalInit() +{ + SC_ATOMIC_INIT(pcap_g.invalid_checksums); +} + +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; + + p->pcap_v.tenant_id = ptv->tenant_id; + 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(); + + int packet_q_len = 64; + 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 */ + PacketPoolWait(); + + /* Right now we just support reading packets one at a time. */ + r = pcap_dispatch(pcap_g.pcap_handle, 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); + } + } + StatsSyncCountersIfSignalled(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)); + + intmax_t tenant = 0; + if (ConfGetInt("pcap-file.tenant-id", &tenant) == 1) { + if (tenant > 0 && tenant < UINT_MAX) { + ptv->tenant_id = (uint32_t)tenant; + SCLogInfo("tenant %u", ptv->tenant_id); + } else { + SCLogError(SC_ERR_INVALID_ARGUMENT, "tenant out of range"); + } + } + + 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; + case LINKTYPE_NULL: + pcap_g.Decoder = DecodeNull; + 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 */ + DecodeUpdatePacketCounters(tv, dtv, 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); +#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(tv, data); + SCReturnInt(TM_ECODE_OK); +} + +void PcapIncreaseInvalidChecksum() +{ + (void) SC_ATOMIC_ADD(pcap_g.invalid_checksums, 1); +} + +/* eof */ + |