Coverage Report

Created: 2026-09-28 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/source-pcap-file-helper.c
Line
Count
Source
1
/* Copyright (C) 2007-2020 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Danny Browning <danny.browning@protectwise.com>
22
 *
23
 * File based pcap packet acquisition support
24
 */
25
26
#include "source-pcap-file-helper.h"
27
#include "suricata.h"
28
#include "util-datalink.h"
29
#include "util-checksum.h"
30
#include "util-profiling.h"
31
#include "source-pcap-file.h"
32
#include "util-exception-policy.h"
33
34
extern uint32_t max_pending_packets;
35
extern PcapFileGlobalVars pcap_g;
36
37
static void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt);
38
39
void CleanupPcapFileFileVars(PcapFileFileVars *pfv)
40
7.98k
{
41
7.98k
    if (pfv != NULL) {
42
7.98k
        if (pfv->pcap_handle != NULL) {
43
7.30k
            pcap_close(pfv->pcap_handle);
44
7.30k
            pfv->pcap_handle = NULL;
45
7.30k
        }
46
7.98k
        if (pfv->filename != NULL) {
47
7.98k
            if (pfv->shared != NULL && pfv->shared->should_delete) {
48
0
                SCLogDebug("Deleting pcap file %s", pfv->filename);
49
0
                if (unlink(pfv->filename) != 0) {
50
0
                    SCLogWarning("Failed to delete %s: %s", pfv->filename, strerror(errno));
51
0
                }
52
0
            }
53
7.98k
            SCFree(pfv->filename);
54
7.98k
            pfv->filename = NULL;
55
7.98k
        }
56
7.98k
        pfv->shared = NULL;
57
7.98k
        SCFree(pfv);
58
7.98k
    }
59
7.98k
}
60
61
void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt)
62
586k
{
63
586k
    SCEnter();
64
#ifdef QA_SIMULATION
65
    if (unlikely((pcap_g.cnt + 1ULL) == g_eps_pcap_packet_loss)) {
66
        SCLogNotice("skipping packet %" PRIu64, g_eps_pcap_packet_loss);
67
        pcap_g.cnt++;
68
        SCReturn;
69
    }
70
#endif
71
586k
    PcapFileFileVars *ptv = (PcapFileFileVars *)user;
72
586k
    Packet *p = PacketGetFromQueueOrAlloc();
73
74
586k
    if (unlikely(p == NULL)) {
75
0
        SCReturn;
76
0
    }
77
586k
    PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE);
78
79
586k
    PKT_SET_SRC(p, PKT_SRC_WIRE);
80
586k
    p->ts = SCTIME_FROM_TIMEVAL_UNTRUSTED(&h->ts);
81
586k
    SCLogDebug("p->ts.tv_sec %" PRIuMAX "", (uintmax_t)SCTIME_SECS(p->ts));
82
586k
    p->datalink = ptv->datalink;
83
586k
    p->pcap_cnt = ++pcap_g.cnt;
84
85
586k
    p->pcap_v.tenant_id = ptv->shared->tenant_id;
86
586k
    ptv->shared->pkts++;
87
586k
    ptv->shared->bytes += h->caplen;
88
89
586k
    if (unlikely(PacketCopyData(p, pkt, h->caplen))) {
90
24
        TmqhOutputPacketpool(ptv->shared->tv, p);
91
24
        PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
92
24
        SCReturn;
93
24
    }
94
95
    /* We only check for checksum disable */
96
586k
    if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
97
586k
        p->flags |= PKT_IGNORE_CHECKSUM;
98
586k
    } else if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_AUTO) {
99
0
        if (ChecksumAutoModeCheck(ptv->shared->pkts, p->pcap_cnt,
100
0
                                  SC_ATOMIC_GET(pcap_g.invalid_checksums))) {
101
0
            pcap_g.checksum_mode = CHECKSUM_VALIDATION_DISABLE;
102
0
            p->flags |= PKT_IGNORE_CHECKSUM;
103
0
        }
104
0
    }
105
106
586k
    PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE);
107
108
586k
    if (TmThreadsSlotProcessPkt(ptv->shared->tv, ptv->shared->slot, p) != TM_ECODE_OK) {
109
0
        pcap_breakloop(ptv->pcap_handle);
110
0
        ptv->shared->cb_result = TM_ECODE_FAILED;
111
0
    }
112
113
586k
    SCReturn;
114
586k
}
115
116
char pcap_filename[PATH_MAX] = "unknown";
117
118
const char *PcapFileGetFilename(void)
119
0
{
120
0
    return pcap_filename;
121
0
}
122
123
/**
124
 *  \brief Main PCAP file reading Loop function
125
 */
126
TmEcode PcapFileDispatch(PcapFileFileVars *ptv)
127
16.8k
{
128
16.8k
    SCEnter();
129
130
    /* initialize all the thread's initial timestamp */
131
16.8k
    if (likely(ptv->first_pkt_hdr != NULL)) {
132
16.8k
        TmThreadsInitThreadsTimestamp(SCTIME_FROM_TIMEVAL(&ptv->first_pkt_ts));
133
16.8k
        PcapFileCallbackLoop((char *)ptv, ptv->first_pkt_hdr,
134
16.8k
                (u_char *)ptv->first_pkt_data);
135
16.8k
        ptv->first_pkt_hdr = NULL;
136
16.8k
        ptv->first_pkt_data = NULL;
137
16.8k
    }
138
139
16.8k
    int packet_q_len = 64;
140
16.8k
    TmEcode loop_result = TM_ECODE_OK;
141
16.8k
    strlcpy(pcap_filename, ptv->filename, sizeof(pcap_filename));
142
143
50.9k
    while (loop_result == TM_ECODE_OK) {
144
34.1k
        if (suricata_ctl_flags & SURICATA_STOP) {
145
0
            SCReturnInt(TM_ECODE_OK);
146
0
        }
147
148
        /* make sure we have at least one packet in the packet pool, to prevent
149
         * us from alloc'ing packets at line rate */
150
34.1k
        PacketPoolWait();
151
152
        /* Right now we just support reading packets one at a time. */
153
34.1k
        int r = pcap_dispatch(ptv->pcap_handle, packet_q_len,
154
34.1k
                          (pcap_handler)PcapFileCallbackLoop, (u_char *)ptv);
155
34.1k
        if (unlikely(r == -1)) {
156
8.52k
            SCLogError("error code %" PRId32 " %s for %s", r, pcap_geterr(ptv->pcap_handle),
157
8.52k
                    ptv->filename);
158
8.52k
            if (ptv->shared->cb_result == TM_ECODE_FAILED) {
159
0
                SCReturnInt(TM_ECODE_FAILED);
160
0
            }
161
8.52k
            loop_result = TM_ECODE_DONE;
162
25.6k
        } else if (unlikely(r == 0)) {
163
8.27k
            SCLogInfo("pcap file %s end of file reached (pcap err code %" PRId32 ")",
164
8.27k
                      ptv->filename, r);
165
8.27k
            ptv->shared->files++;
166
8.27k
            loop_result = TM_ECODE_DONE;
167
17.3k
        } else if (ptv->shared->cb_result == TM_ECODE_FAILED) {
168
0
            SCLogError("Pcap callback PcapFileCallbackLoop failed for %s", ptv->filename);
169
0
            loop_result = TM_ECODE_FAILED;
170
0
        }
171
34.1k
        StatsSyncCountersIfSignalled(ptv->shared->tv);
172
34.1k
    }
173
174
16.8k
    SCReturnInt(loop_result);
175
16.8k
}
176
177
/** \internal
178
 *  \brief get the timestamp of the first packet and rewind
179
 *  \param pfv pcap file variables for storing the timestamp
180
 *  \retval bool true on success, false on error
181
 */
182
static bool PeekFirstPacketTimestamp(PcapFileFileVars *pfv)
183
18.6k
{
184
18.6k
    int r = pcap_next_ex(pfv->pcap_handle, &pfv->first_pkt_hdr, &pfv->first_pkt_data);
185
18.6k
    if (r <= 0 || pfv->first_pkt_hdr == NULL) {
186
1.31k
        SCLogError("failed to get first packet timestamp. pcap_next_ex(): %d", r);
187
1.31k
        return false;
188
1.31k
    }
189
    /* timestamp in pfv->first_pkt_hdr may not be 'struct timeval' so
190
     * do a manual copy of the members. */
191
17.3k
    pfv->first_pkt_ts.tv_sec = pfv->first_pkt_hdr->ts.tv_sec;
192
17.3k
    pfv->first_pkt_ts.tv_usec = pfv->first_pkt_hdr->ts.tv_usec;
193
17.3k
    return true;
194
18.6k
}
195
196
TmEcode InitPcapFile(PcapFileFileVars *pfv)
197
19.9k
{
198
19.9k
    char errbuf[PCAP_ERRBUF_SIZE] = "";
199
200
19.9k
    if(unlikely(pfv->filename == NULL)) {
201
0
        SCLogError("Filename was null");
202
0
        SCReturnInt(TM_ECODE_FAILED);
203
0
    }
204
205
19.9k
    pfv->pcap_handle = pcap_open_offline(pfv->filename, errbuf);
206
19.9k
    if (pfv->pcap_handle == NULL) {
207
1.31k
        SCLogError("%s", errbuf);
208
1.31k
        SCReturnInt(TM_ECODE_FAILED);
209
1.31k
    }
210
211
18.6k
#if defined(HAVE_SETVBUF) && defined(OS_LINUX)
212
18.6k
    if (pcap_g.read_buffer_size > 0) {
213
18.6k
        struct stat sb;
214
18.6k
        int fd = fileno(pcap_file(pfv->pcap_handle));
215
18.6k
        if (fd >= 0 && fstat(fd, &sb) == 0 && !S_ISREG(sb.st_mode)) {
216
0
            SCLogInfo("%s: skipping setvbuf, underlying fd is not a regular file", pfv->filename);
217
18.6k
        } else {
218
18.6k
            errno = 0;
219
18.6k
            if (setvbuf(pcap_file(pfv->pcap_handle), pfv->buffer, _IOFBF,
220
18.6k
                        pcap_g.read_buffer_size) != 0) {
221
0
                SCLogWarning("Failed to setvbuf on PCAP file handle: %s", strerror(errno));
222
0
            }
223
18.6k
        }
224
18.6k
    }
225
18.6k
#endif
226
227
18.6k
    if (pfv->shared != NULL && pfv->shared->bpf_string != NULL) {
228
0
        SCLogInfo("using bpf-filter \"%s\"", pfv->shared->bpf_string);
229
230
0
        if (pcap_compile(pfv->pcap_handle, &pfv->filter, pfv->shared->bpf_string, 1, 0) < 0) {
231
0
            SCLogError("bpf compilation error %s for %s", pcap_geterr(pfv->pcap_handle),
232
0
                    pfv->filename);
233
0
            SCReturnInt(TM_ECODE_FAILED);
234
0
        }
235
236
0
        if (pcap_setfilter(pfv->pcap_handle, &pfv->filter) < 0) {
237
0
            SCLogError("could not set bpf filter %s for %s", pcap_geterr(pfv->pcap_handle),
238
0
                    pfv->filename);
239
0
            pcap_freecode(&pfv->filter);
240
0
            SCReturnInt(TM_ECODE_FAILED);
241
0
        }
242
0
        pcap_freecode(&pfv->filter);
243
0
    }
244
245
18.6k
    pfv->datalink = pcap_datalink(pfv->pcap_handle);
246
18.6k
    SCLogDebug("datalink %" PRId32 "", pfv->datalink);
247
18.6k
    DatalinkSetGlobalType(pfv->datalink);
248
249
18.6k
    if (!PeekFirstPacketTimestamp(pfv))
250
1.31k
        SCReturnInt(TM_ECODE_FAILED);
251
252
17.3k
    DecoderFunc UnusedFnPtr;
253
17.3k
    TmEcode validated = ValidateLinkType(pfv->datalink, &UnusedFnPtr);
254
17.3k
    SCReturnInt(validated);
255
18.6k
}
256
257
TmEcode ValidateLinkType(int datalink, DecoderFunc *DecoderFn)
258
15.6M
{
259
15.6M
    switch (datalink) {
260
83.0k
        case LINKTYPE_LINUX_SLL2:
261
83.0k
            *DecoderFn = DecodeSll2;
262
83.0k
            break;
263
40.5k
        case LINKTYPE_LINUX_SLL:
264
40.5k
            *DecoderFn = DecodeSll;
265
40.5k
            break;
266
8.08M
        case LINKTYPE_ETHERNET:
267
8.08M
            *DecoderFn = DecodeEthernet;
268
8.08M
            break;
269
43.0k
        case LINKTYPE_PPP:
270
43.0k
            *DecoderFn = DecodePPP;
271
43.0k
            break;
272
349k
        case LINKTYPE_IPV4:
273
356k
        case LINKTYPE_IPV6:
274
426k
        case LINKTYPE_RAW:
275
426k
        case LINKTYPE_RAW2:
276
446k
        case LINKTYPE_GRE_OVER_IP:
277
446k
            *DecoderFn = DecodeRaw;
278
446k
            break;
279
6.85M
        case LINKTYPE_NULL:
280
6.85M
            *DecoderFn = DecodeNull;
281
6.85M
            break;
282
69.2k
        case LINKTYPE_CISCO_HDLC:
283
69.2k
            *DecoderFn = DecodeCHDLC;
284
69.2k
            break;
285
286
733
        default:
287
733
            SCLogError(
288
733
                    "datalink type %" PRId32 " not (yet) supported in module PcapFile.", datalink);
289
733
            SCReturnInt(TM_ECODE_FAILED);
290
15.6M
    }
291
292
15.6M
    SCReturnInt(TM_ECODE_OK);
293
15.6M
}