/src/suricata7/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 uint16_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 | 23.8k | { |
41 | 23.8k | if (pfv != NULL) { |
42 | 23.8k | if (pfv->pcap_handle != NULL) { |
43 | 22.2k | pcap_close(pfv->pcap_handle); |
44 | 22.2k | pfv->pcap_handle = NULL; |
45 | 22.2k | } |
46 | 23.8k | if (pfv->filename != NULL) { |
47 | 23.8k | 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 | 23.8k | SCFree(pfv->filename); |
54 | 23.8k | pfv->filename = NULL; |
55 | 23.8k | } |
56 | 23.8k | pfv->shared = NULL; |
57 | 23.8k | SCFree(pfv); |
58 | 23.8k | } |
59 | 23.8k | } |
60 | | |
61 | | void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt) |
62 | 1.06M | { |
63 | 1.06M | SCEnter(); |
64 | | #ifdef DEBUG |
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 | 1.06M | PcapFileFileVars *ptv = (PcapFileFileVars *)user; |
72 | 1.06M | Packet *p = PacketGetFromQueueOrAlloc(); |
73 | | |
74 | 1.06M | if (unlikely(p == NULL)) { |
75 | 0 | SCReturn; |
76 | 0 | } |
77 | 1.06M | PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE); |
78 | | |
79 | 1.06M | PKT_SET_SRC(p, PKT_SRC_WIRE); |
80 | 1.06M | p->ts = SCTIME_FROM_TIMEVAL_UNTRUSTED(&h->ts); |
81 | 1.06M | SCLogDebug("p->ts.tv_sec %" PRIuMAX "", (uintmax_t)SCTIME_SECS(p->ts)); |
82 | 1.06M | p->datalink = ptv->datalink; |
83 | 1.06M | p->pcap_cnt = ++pcap_g.cnt; |
84 | | |
85 | 1.06M | p->pcap_v.tenant_id = ptv->shared->tenant_id; |
86 | 1.06M | ptv->shared->pkts++; |
87 | 1.06M | ptv->shared->bytes += h->caplen; |
88 | | |
89 | 1.06M | if (unlikely(PacketCopyData(p, pkt, h->caplen))) { |
90 | 41 | TmqhOutputPacketpool(ptv->shared->tv, p); |
91 | 41 | PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE); |
92 | 41 | SCReturn; |
93 | 41 | } |
94 | | |
95 | | /* We only check for checksum disable */ |
96 | 1.06M | if (pcap_g.checksum_mode == CHECKSUM_VALIDATION_DISABLE) { |
97 | 1.06M | p->flags |= PKT_IGNORE_CHECKSUM; |
98 | 1.06M | } 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 | 1.06M | PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE); |
107 | | |
108 | 1.06M | 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 | 1.06M | SCReturn; |
114 | 1.06M | } |
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 | 9.71k | { |
128 | 9.71k | SCEnter(); |
129 | | |
130 | | /* initialize all the thread's initial timestamp */ |
131 | 9.71k | if (likely(ptv->first_pkt_hdr != NULL)) { |
132 | 9.71k | TmThreadsInitThreadsTimestamp(SCTIME_FROM_TIMEVAL(&ptv->first_pkt_ts)); |
133 | 9.71k | PcapFileCallbackLoop((char *)ptv, ptv->first_pkt_hdr, |
134 | 9.71k | (u_char *)ptv->first_pkt_data); |
135 | 9.71k | ptv->first_pkt_hdr = NULL; |
136 | 9.71k | ptv->first_pkt_data = NULL; |
137 | 9.71k | } |
138 | | |
139 | 9.71k | int packet_q_len = 64; |
140 | 9.71k | TmEcode loop_result = TM_ECODE_OK; |
141 | 9.71k | strlcpy(pcap_filename, ptv->filename, sizeof(pcap_filename)); |
142 | | |
143 | 25.0k | while (loop_result == TM_ECODE_OK) { |
144 | 15.3k | 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 | 15.3k | PacketPoolWait(); |
151 | | |
152 | | /* Right now we just support reading packets one at a time. */ |
153 | 15.3k | int r = pcap_dispatch(ptv->pcap_handle, packet_q_len, |
154 | 15.3k | (pcap_handler)PcapFileCallbackLoop, (u_char *)ptv); |
155 | 15.3k | if (unlikely(r == -1)) { |
156 | 3.83k | SCLogError("error code %" PRId32 " %s for %s", r, pcap_geterr(ptv->pcap_handle), |
157 | 3.83k | ptv->filename); |
158 | 3.83k | if (ptv->shared->cb_result == TM_ECODE_FAILED) { |
159 | 0 | SCReturnInt(TM_ECODE_FAILED); |
160 | 0 | } |
161 | 3.83k | loop_result = TM_ECODE_DONE; |
162 | 11.4k | } else if (unlikely(r == 0)) { |
163 | 5.87k | SCLogInfo("pcap file %s end of file reached (pcap err code %" PRId32 ")", |
164 | 5.87k | ptv->filename, r); |
165 | 5.87k | ptv->shared->files++; |
166 | 5.87k | loop_result = TM_ECODE_DONE; |
167 | 5.87k | } 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 | 15.3k | StatsSyncCountersIfSignalled(ptv->shared->tv); |
172 | 15.3k | } |
173 | | |
174 | 9.71k | SCReturnInt(loop_result); |
175 | 9.71k | } |
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 | 22.2k | { |
184 | 22.2k | int r = pcap_next_ex(pfv->pcap_handle, &pfv->first_pkt_hdr, &pfv->first_pkt_data); |
185 | 22.2k | if (r <= 0 || pfv->first_pkt_hdr == NULL) { |
186 | 1.80k | SCLogError("failed to get first packet timestamp. pcap_next_ex(): %d", r); |
187 | 1.80k | return false; |
188 | 1.80k | } |
189 | | /* timestamp in pfv->first_pkt_hdr may not be 'struct timeval' so |
190 | | * do a manual copy of the members. */ |
191 | 20.4k | pfv->first_pkt_ts.tv_sec = pfv->first_pkt_hdr->ts.tv_sec; |
192 | 20.4k | pfv->first_pkt_ts.tv_usec = pfv->first_pkt_hdr->ts.tv_usec; |
193 | 20.4k | return true; |
194 | 22.2k | } |
195 | | |
196 | | TmEcode InitPcapFile(PcapFileFileVars *pfv) |
197 | 11.7k | { |
198 | 11.7k | char errbuf[PCAP_ERRBUF_SIZE] = ""; |
199 | | |
200 | 11.7k | if(unlikely(pfv->filename == NULL)) { |
201 | 0 | SCLogError("Filename was null"); |
202 | 0 | SCReturnInt(TM_ECODE_FAILED); |
203 | 0 | } |
204 | | |
205 | 11.7k | pfv->pcap_handle = pcap_open_offline(pfv->filename, errbuf); |
206 | 11.7k | if (pfv->pcap_handle == NULL) { |
207 | 749 | SCLogError("%s", errbuf); |
208 | 749 | SCReturnInt(TM_ECODE_FAILED); |
209 | 749 | } |
210 | | |
211 | 11.0k | if (pfv->shared != NULL && pfv->shared->bpf_string != NULL) { |
212 | 0 | SCLogInfo("using bpf-filter \"%s\"", pfv->shared->bpf_string); |
213 | |
|
214 | 0 | if (pcap_compile(pfv->pcap_handle, &pfv->filter, pfv->shared->bpf_string, 1, 0) < 0) { |
215 | 0 | SCLogError("bpf compilation error %s for %s", pcap_geterr(pfv->pcap_handle), |
216 | 0 | pfv->filename); |
217 | 0 | SCReturnInt(TM_ECODE_FAILED); |
218 | 0 | } |
219 | | |
220 | 0 | if (pcap_setfilter(pfv->pcap_handle, &pfv->filter) < 0) { |
221 | 0 | SCLogError("could not set bpf filter %s for %s", pcap_geterr(pfv->pcap_handle), |
222 | 0 | pfv->filename); |
223 | 0 | pcap_freecode(&pfv->filter); |
224 | 0 | SCReturnInt(TM_ECODE_FAILED); |
225 | 0 | } |
226 | 0 | pcap_freecode(&pfv->filter); |
227 | 0 | } |
228 | | |
229 | 11.0k | pfv->datalink = pcap_datalink(pfv->pcap_handle); |
230 | 11.0k | SCLogDebug("datalink %" PRId32 "", pfv->datalink); |
231 | 11.0k | DatalinkSetGlobalType(pfv->datalink); |
232 | | |
233 | 11.0k | if (!PeekFirstPacketTimestamp(pfv)) |
234 | 951 | SCReturnInt(TM_ECODE_FAILED); |
235 | | |
236 | 10.0k | DecoderFunc UnusedFnPtr; |
237 | 10.0k | TmEcode validated = ValidateLinkType(pfv->datalink, &UnusedFnPtr); |
238 | 10.0k | SCReturnInt(validated); |
239 | 11.0k | } |
240 | | |
241 | | TmEcode ValidateLinkType(int datalink, DecoderFunc *DecoderFn) |
242 | 8.80M | { |
243 | 8.80M | switch (datalink) { |
244 | 14.5k | case LINKTYPE_LINUX_SLL: |
245 | 14.5k | *DecoderFn = DecodeSll; |
246 | 14.5k | break; |
247 | 3.48M | case LINKTYPE_ETHERNET: |
248 | 3.48M | *DecoderFn = DecodeEthernet; |
249 | 3.48M | break; |
250 | 15.7k | case LINKTYPE_PPP: |
251 | 15.7k | *DecoderFn = DecodePPP; |
252 | 15.7k | break; |
253 | 59.8k | case LINKTYPE_IPV4: |
254 | 60.5k | case LINKTYPE_IPV6: |
255 | 182k | case LINKTYPE_RAW: |
256 | 182k | case LINKTYPE_RAW2: |
257 | 204k | case LINKTYPE_GRE_OVER_IP: |
258 | 204k | *DecoderFn = DecodeRaw; |
259 | 204k | break; |
260 | 5.05M | case LINKTYPE_NULL: |
261 | 5.05M | *DecoderFn = DecodeNull; |
262 | 5.05M | break; |
263 | 27.7k | case LINKTYPE_CISCO_HDLC: |
264 | 27.7k | *DecoderFn = DecodeCHDLC; |
265 | 27.7k | break; |
266 | | |
267 | 460 | default: |
268 | 460 | SCLogError( |
269 | 460 | "datalink type %" PRId32 " not (yet) supported in module PcapFile.", datalink); |
270 | 460 | SCReturnInt(TM_ECODE_FAILED); |
271 | 8.80M | } |
272 | | |
273 | 8.80M | SCReturnInt(TM_ECODE_OK); |
274 | 8.80M | } |