summaryrefslogtreecommitdiffstats
path: root/src/source-pcap-file-helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/source-pcap-file-helper.c')
-rw-r--r--src/source-pcap-file-helper.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/src/source-pcap-file-helper.c b/src/source-pcap-file-helper.c
new file mode 100644
index 0000000..936b65f
--- /dev/null
+++ b/src/source-pcap-file-helper.c
@@ -0,0 +1,273 @@
+/* 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 "suricata.h"
+#include "util-datalink.h"
+#include "util-checksum.h"
+#include "util-profiling.h"
+#include "source-pcap-file.h"
+#include "util-exception-policy.h"
+
+extern uint16_t 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("Failed to delete %s: %s", pfv->filename, strerror(errno));
+ }
+ }
+ SCFree(pfv->filename);
+ pfv->filename = NULL;
+ }
+ pfv->shared = NULL;
+ SCFree(pfv);
+ }
+}
+
+void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
+{
+ SCEnter();
+#ifdef DEBUG
+ if (unlikely((pcap_g.cnt + 1ULL) == g_eps_pcap_packet_loss)) {
+ SCLogNotice("skipping packet %" PRIu64, g_eps_pcap_packet_loss);
+ pcap_g.cnt++;
+ SCReturn;
+ }
+#endif
+ 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 = SCTIME_FROM_TIMEVAL_UNTRUSTED(&h->ts);
+ SCLogDebug("p->ts.tv_sec %" PRIuMAX "", (uintmax_t)SCTIME_SECS(p->ts));
+ 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(SCTIME_FROM_TIMEVAL(&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("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("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("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("Filename was null");
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ pfv->pcap_handle = pcap_open_offline(pfv->filename, errbuf);
+ if (pfv->pcap_handle == NULL) {
+ SCLogError("%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("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("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);
+ DatalinkSetGlobalType(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(
+ "datalink type %" PRId32 " not (yet) supported in module PcapFile.", datalink);
+ SCReturnInt(TM_ECODE_FAILED);
+ }
+
+ SCReturnInt(TM_ECODE_OK);
+}