|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if LIBPCAP_VERSION_MAJOR == 1
|
|
|
|
#include <pcap/pcap.h>
|
|
|
|
#else
|
|
|
|
#include <pcap.h>
|
|
|
|
#endif /* LIBPCAP_VERSION_MAJOR */
|
|
|
|
|
|
|
|
#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"
|
|
|
|
|
|
|
|
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;
|
|
|
|
void (*Decoder)(ThreadVars *, DecodeThreadVars *, Packet *, u_int8_t *, u_int16_t, PacketQueue *);
|
|
|
|
int datalink;
|
|
|
|
struct bpf_program filter;
|
|
|
|
uint64_t cnt; /** packet counter */
|
|
|
|
} PcapFileGlobalVars;
|
|
|
|
|
|
|
|
/** max packets < 65536 */
|
|
|
|
//#define PCAP_FILE_MAX_PKTS 256
|
|
|
|
|
|
|
|
typedef struct PcapFileThreadVars_
|
|
|
|
{
|
|
|
|
/* counters */
|
|
|
|
uint32_t pkts;
|
|
|
|
uint64_t bytes;
|
|
|
|
|
|
|
|
ThreadVars *tv;
|
|
|
|
TmSlot *slot;
|
|
|
|
|
|
|
|
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 **);
|
|
|
|
|
|
|
|
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 = NULL;
|
|
|
|
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 = NULL;
|
|
|
|
tmm_modules[TMM_DECODEPCAPFILE].RegisterTests = NULL;
|
|
|
|
tmm_modules[TMM_DECODEPCAPFILE].cap_flags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt) {
|
|
|
|
SCEnter();
|
|
|
|
|
|
|
|
PcapFileThreadVars *ptv = (PcapFileThreadVars *)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 = pcap_g.datalink;
|
Add per packet profiling.
Per packet profiling uses tick based accounting. It has 2 outputs, a summary
and a csv file that contains per packet stats.
Stats per packet include:
1) total ticks spent
2) ticks spent per individual thread module
3) "threading overhead" which is simply calculated by subtracting (2) of (1).
A number of changes were made to integrate the new code in a clean way:
a number of generic enums are now placed in tm-threads-common.h so we can
include them from any part of the engine.
Code depends on --enable-profiling just like the rule profiling code.
New yaml parameters:
profiling:
# packet profiling
packets:
# Profiling can be disabled here, but it will still have a
# performance impact if compiled in.
enabled: yes
filename: packet_stats.log
append: yes
# per packet csv output
csv:
# Output can be disabled here, but it will still have a
# performance impact if compiled in.
enabled: no
filename: packet_stats.csv
Example output of summary stats:
IP ver Proto cnt min max avg
------ ----- ------ ------ ---------- -------
IPv4 6 19436 11448 5404365 32993
IPv4 256 4 11511 49968 30575
Per Thread module stats:
Thread Module IP ver Proto cnt min max avg
------------------------ ------ ----- ------ ------ ---------- -------
TMM_DECODEPCAPFILE IPv4 6 19434 1242 47889 1770
TMM_DETECT IPv4 6 19436 1107 137241 1504
TMM_ALERTFASTLOG IPv4 6 19436 90 1323 155
TMM_ALERTUNIFIED2ALERT IPv4 6 19436 108 1359 138
TMM_ALERTDEBUGLOG IPv4 6 19436 90 1134 154
TMM_LOGHTTPLOG IPv4 6 19436 414 5392089 7944
TMM_STREAMTCP IPv4 6 19434 828 1299159 19438
The proto 256 is a counter for handling of pseudo/tunnel packets.
Example output of csv:
pcap_cnt,ipver,ipproto,total,TMM_DECODENFQ,TMM_VERDICTNFQ,TMM_RECEIVENFQ,TMM_RECEIVEPCAP,TMM_RECEIVEPCAPFILE,TMM_DECODEPCAP,TMM_DECODEPCAPFILE,TMM_RECEIVEPFRING,TMM_DECODEPFRING,TMM_DETECT,TMM_ALERTFASTLOG,TMM_ALERTFASTLOG4,TMM_ALERTFASTLOG6,TMM_ALERTUNIFIEDLOG,TMM_ALERTUNIFIEDALERT,TMM_ALERTUNIFIED2ALERT,TMM_ALERTPRELUDE,TMM_ALERTDEBUGLOG,TMM_ALERTSYSLOG,TMM_LOGDROPLOG,TMM_ALERTSYSLOG4,TMM_ALERTSYSLOG6,TMM_RESPONDREJECT,TMM_LOGHTTPLOG,TMM_LOGHTTPLOG4,TMM_LOGHTTPLOG6,TMM_PCAPLOG,TMM_STREAMTCP,TMM_DECODEIPFW,TMM_VERDICTIPFW,TMM_RECEIVEIPFW,TMM_RECEIVEERFFILE,TMM_DECODEERFFILE,TMM_RECEIVEERFDAG,TMM_DECODEERFDAG,threading
1,4,6,172008,0,0,0,0,0,0,47889,0,0,48582,1323,0,0,0,0,1359,0,1134,0,0,0,0,0,8028,0,0,0,49356,0,0,0,0,0,0,0,14337
First line of the file contains labels.
2 example gnuplot scripts added to plot the data.
14 years ago
|
|
|
p->pcap_cnt = ++pcap_g.cnt;
|
|
|
|
|
|
|
|
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 file reading Loop function
|
|
|
|
*/
|
|
|
|
TmEcode ReceivePcapFileLoop(ThreadVars *tv, void *data, void *slot) {
|
|
|
|
uint16_t packet_q_len = 0;
|
|
|
|
PcapFileThreadVars *ptv = (PcapFileThreadVars *)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_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 < 0)) {
|
|
|
|
SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s",
|
|
|
|
r, pcap_geterr(pcap_g.pcap_handle));
|
|
|
|
|
|
|
|
/* in the error state we just kill the engine */
|
|
|
|
EngineKill();
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
} else if (unlikely(r == 0)) {
|
|
|
|
SCLogInfo("pcap file end of file reached (pcap err code %" PRId32 ")", r);
|
|
|
|
|
|
|
|
EngineStop();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
SCPerfSyncCountersIfSignalled(tv, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, void *initdata, void **data) {
|
|
|
|
SCEnter();
|
|
|
|
char *tmpbpfstring = 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 (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);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
SCReturnInt(TM_ECODE_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
ptv->tv = tv;
|
|
|
|
*data = (void *)ptv;
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReceivePcapFileThreadExitStats(ThreadVars *tv, void *data) {
|
|
|
|
SCEnter();
|
|
|
|
PcapFileThreadVars *ptv = (PcapFileThreadVars *)data;
|
|
|
|
|
|
|
|
SCLogInfo(" - (%s) Packets %" PRIu32 ", bytes %" PRIu64 ".", tv->name, ptv->pkts, ptv->bytes);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TmEcode ReceivePcapFileThreadDeinit(ThreadVars *tv, void *data) {
|
|
|
|
SCEnter();
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
TmEcode DecodePcapFile(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));
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
SCReturnInt(TM_ECODE_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
TmEcode DecodePcapFileThreadInit(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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* eof */
|
|
|
|
|