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

269 lines
8.7 KiB
C

/* Copyright (C) 2007-2020 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 Danny Browning <danny.browning@protectwise.com>
*
* File based pcap packet acquisition support
*/
#include "source-pcap-file-helper.h"
#include "util-checksum.h"
#include "util-profiling.h"
#include "source-pcap-file.h"
extern int max_pending_packets;
extern PcapFileGlobalVars pcap_g;
static void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt);
void CleanupPcapFileFileVars(PcapFileFileVars *pfv)
{
if (pfv != NULL) {
if (pfv->pcap_handle != NULL) {
pcap_close(pfv->pcap_handle);
pfv->pcap_handle = NULL;
}
if (pfv->filename != NULL) {
if (pfv->shared != NULL && pfv->shared->should_delete) {
SCLogDebug("Deleting pcap file %s", pfv->filename);
if (unlink(pfv->filename) != 0) {
SCLogWarning(SC_ERR_PCAP_FILE_DELETE_FAILED,
"Failed to delete %s", pfv->filename);
}
}
SCFree(pfv->filename);
pfv->filename = NULL;
}
pfv->shared = NULL;
SCFree(pfv);
}
}
void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
{
SCEnter();
PcapFileFileVars *ptv = (PcapFileFileVars *)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 = ptv->datalink;
p->pcap_cnt = ++pcap_g.cnt;
p->pcap_v.tenant_id = ptv->shared->tenant_id;
ptv->shared->pkts++;
ptv->shared->bytes += h->caplen;
if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
TmqhOutputPacketpool(ptv->shared->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->shared->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->shared->tv, ptv->shared->slot, p) != TM_ECODE_OK) {
pcap_breakloop(ptv->pcap_handle);
ptv->shared->cb_result = TM_ECODE_FAILED;
}
SCReturn;
}
char pcap_filename[PATH_MAX] = "unknown";
const char *PcapFileGetFilename(void)
{
return pcap_filename;
}
/**
* \brief Main PCAP file reading Loop function
*/
TmEcode PcapFileDispatch(PcapFileFileVars *ptv)
{
SCEnter();
/* initialize all the thread's initial timestamp */
if (likely(ptv->first_pkt_hdr != NULL)) {
TmThreadsInitThreadsTimestamp(&ptv->first_pkt_ts);
PcapFileCallbackLoop((char *)ptv, ptv->first_pkt_hdr,
(u_char *)ptv->first_pkt_data);
ptv->first_pkt_hdr = NULL;
ptv->first_pkt_data = NULL;
}
int packet_q_len = 64;
TmEcode loop_result = TM_ECODE_OK;
strlcpy(pcap_filename, ptv->filename, sizeof(pcap_filename));
while (loop_result == TM_ECODE_OK) {
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();
/* Right now we just support reading packets one at a time. */
int r = pcap_dispatch(ptv->pcap_handle, packet_q_len,
(pcap_handler)PcapFileCallbackLoop, (u_char *)ptv);
if (unlikely(r == -1)) {
SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s for %s",
r, pcap_geterr(ptv->pcap_handle), ptv->filename);
if (ptv->shared->cb_result == TM_ECODE_FAILED) {
SCReturnInt(TM_ECODE_FAILED);
}
loop_result = TM_ECODE_DONE;
} else if (unlikely(r == 0)) {
SCLogInfo("pcap file %s end of file reached (pcap err code %" PRId32 ")",
ptv->filename, r);
ptv->shared->files++;
loop_result = TM_ECODE_DONE;
} else if (ptv->shared->cb_result == TM_ECODE_FAILED) {
SCLogError(SC_ERR_PCAP_DISPATCH,
"Pcap callback PcapFileCallbackLoop failed for %s", ptv->filename);
loop_result = TM_ECODE_FAILED;
}
StatsSyncCountersIfSignalled(ptv->shared->tv);
}
SCReturnInt(loop_result);
}
/** \internal
* \brief get the timestamp of the first packet and rewind
* \param pfv pcap file variables for storing the timestamp
* \retval bool true on success, false on error
*/
static bool PeekFirstPacketTimestamp(PcapFileFileVars *pfv)
{
int r = pcap_next_ex(pfv->pcap_handle, &pfv->first_pkt_hdr, &pfv->first_pkt_data);
if (r <= 0 || pfv->first_pkt_hdr == NULL) {
SCLogError(SC_ERR_PCAP_OPEN_OFFLINE,
"failed to get first packet timestamp. pcap_next_ex(): %d", r);
return false;
}
/* timestamp in pfv->first_pkt_hdr may not be 'struct timeval' so
* do a manual copy of the members. */
pfv->first_pkt_ts.tv_sec = pfv->first_pkt_hdr->ts.tv_sec;
pfv->first_pkt_ts.tv_usec = pfv->first_pkt_hdr->ts.tv_usec;
return true;
}
TmEcode InitPcapFile(PcapFileFileVars *pfv)
{
char errbuf[PCAP_ERRBUF_SIZE] = "";
if(unlikely(pfv->filename == NULL)) {
SCLogError(SC_ERR_INVALID_ARGUMENT, "Filename was null");
SCReturnInt(TM_ECODE_FAILED);
}
pfv->pcap_handle = pcap_open_offline(pfv->filename, errbuf);
if (pfv->pcap_handle == NULL) {
SCLogError(SC_ERR_FOPEN, "%s", errbuf);
SCReturnInt(TM_ECODE_FAILED);
}
if (pfv->shared != NULL && pfv->shared->bpf_string != NULL) {
SCLogInfo("using bpf-filter \"%s\"", pfv->shared->bpf_string);
if (pcap_compile(pfv->pcap_handle, &pfv->filter, pfv->shared->bpf_string, 1, 0) < 0) {
SCLogError(SC_ERR_BPF, "bpf compilation error %s for %s",
pcap_geterr(pfv->pcap_handle), pfv->filename);
SCReturnInt(TM_ECODE_FAILED);
}
if (pcap_setfilter(pfv->pcap_handle, &pfv->filter) < 0) {
SCLogError(SC_ERR_BPF,"could not set bpf filter %s for %s",
pcap_geterr(pfv->pcap_handle), pfv->filename);
pcap_freecode(&pfv->filter);
SCReturnInt(TM_ECODE_FAILED);
}
pcap_freecode(&pfv->filter);
}
pfv->datalink = pcap_datalink(pfv->pcap_handle);
SCLogDebug("datalink %" PRId32 "", pfv->datalink);
if (!PeekFirstPacketTimestamp(pfv))
SCReturnInt(TM_ECODE_FAILED);
DecoderFunc UnusedFnPtr;
TmEcode validated = ValidateLinkType(pfv->datalink, &UnusedFnPtr);
SCReturnInt(validated);
}
TmEcode ValidateLinkType(int datalink, DecoderFunc *DecoderFn)
{
switch (datalink) {
case LINKTYPE_LINUX_SLL:
*DecoderFn = DecodeSll;
break;
case LINKTYPE_ETHERNET:
*DecoderFn = DecodeEthernet;
break;
case LINKTYPE_PPP:
*DecoderFn = DecodePPP;
break;
case LINKTYPE_IPV4:
case LINKTYPE_RAW:
case LINKTYPE_RAW2:
case LINKTYPE_GRE_OVER_IP:
*DecoderFn = DecodeRaw;
break;
case LINKTYPE_NULL:
*DecoderFn = DecodeNull;
break;
case LINKTYPE_CISCO_HDLC:
*DecoderFn = DecodeCHDLC;
break;
default:
SCLogError(SC_ERR_UNIMPLEMENTED,
"datalink type %"PRId32" not (yet) supported in module PcapFile.",
datalink);
SCReturnInt(TM_ECODE_FAILED);
}
SCReturnInt(TM_ECODE_OK);
}