]>
Commit | Line | Data |
---|---|---|
2eef905c | 1 | /* Copyright (C) 2007-2010 Open Information Security Foundation |
ce019275 WM |
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. | |
9c7f5afa | 11 | * |
ce019275 WM |
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 | |
9c7f5afa | 20 | * |
ce019275 | 21 | * \author Victor Julien <victor@inliniac.net> |
9c7f5afa | 22 | * |
ce019275 | 23 | * File based pcap packet acquisition support |
9c7f5afa VJ |
24 | */ |
25 | ||
af992242 | 26 | #if LIBPCAP_VERSION_MAJOR == 1 |
9c7f5afa | 27 | #include <pcap/pcap.h> |
af992242 WM |
28 | #else |
29 | #include <pcap.h> | |
ba46c16a | 30 | #endif /* LIBPCAP_VERSION_MAJOR */ |
9c7f5afa | 31 | |
ecf86f9c VJ |
32 | #include "suricata-common.h" |
33 | #include "suricata.h" | |
9c7f5afa VJ |
34 | #include "decode.h" |
35 | #include "packet-queue.h" | |
36 | #include "threads.h" | |
37 | #include "threadvars.h" | |
38 | #include "tm-queuehandlers.h" | |
9c7f5afa VJ |
39 | #include "source-pcap-file.h" |
40 | #include "util-time.h" | |
9ececacd | 41 | #include "util-debug.h" |
ba46c16a | 42 | #include "conf.h" |
29d51a61 | 43 | #include "util-error.h" |
070ed778 | 44 | #include "util-privs.h" |
6519a86e | 45 | #include "tmqh-packetpool.h" |
b753ecce VJ |
46 | #include "tm-threads.h" |
47 | #include "util-optimize.h" | |
5133098b | 48 | #include "flow-manager.h" |
41e9dba2 | 49 | #include "util-profiling.h" |
ba46c16a | 50 | |
b657705d | 51 | extern uint8_t suricata_ctl_flags; |
7142fdb7 | 52 | extern int max_pending_packets; |
53acf089 | 53 | |
b753ecce | 54 | //static int pcap_max_read_packets = 0; |
9c7f5afa VJ |
55 | |
56 | typedef struct PcapFileGlobalVars_ { | |
57 | pcap_t *pcap_handle; | |
57f71f7e | 58 | void (*Decoder)(ThreadVars *, DecodeThreadVars *, Packet *, u_int8_t *, u_int16_t, PacketQueue *); |
a4fe9718 | 59 | int datalink; |
ba46c16a | 60 | struct bpf_program filter; |
b90ebc1c | 61 | uint64_t cnt; /** packet counter */ |
9c7f5afa VJ |
62 | } PcapFileGlobalVars; |
63 | ||
4e7df60b | 64 | /** max packets < 65536 */ |
b753ecce | 65 | //#define PCAP_FILE_MAX_PKTS 256 |
4e7df60b | 66 | |
9c7f5afa VJ |
67 | typedef struct PcapFileThreadVars_ |
68 | { | |
69 | /* counters */ | |
fa5939ca BR |
70 | uint32_t pkts; |
71 | uint64_t bytes; | |
9c7f5afa VJ |
72 | |
73 | ThreadVars *tv; | |
b753ecce | 74 | TmSlot *slot; |
4e7df60b | 75 | |
67cea099 VJ |
76 | /** callback result -- set if one of the thread module failed. */ |
77 | int cb_result; | |
78 | ||
4e7df60b | 79 | uint8_t done; |
b753ecce | 80 | uint32_t errs; |
9c7f5afa VJ |
81 | } PcapFileThreadVars; |
82 | ||
e0aacac4 | 83 | static PcapFileGlobalVars pcap_g; |
9c7f5afa | 84 | |
b753ecce VJ |
85 | TmEcode ReceivePcapFileLoop(ThreadVars *, void *, void *); |
86 | ||
40b8afdd | 87 | TmEcode ReceivePcapFileThreadInit(ThreadVars *, void *, void **); |
9c7f5afa | 88 | void ReceivePcapFileThreadExitStats(ThreadVars *, void *); |
40b8afdd | 89 | TmEcode ReceivePcapFileThreadDeinit(ThreadVars *, void *); |
9c7f5afa | 90 | |
4e7df60b | 91 | TmEcode DecodePcapFile(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); |
40b8afdd | 92 | TmEcode DecodePcapFileThreadInit(ThreadVars *, void *, void **); |
9c7f5afa VJ |
93 | |
94 | void TmModuleReceivePcapFileRegister (void) { | |
e0aacac4 VJ |
95 | memset(&pcap_g, 0x00, sizeof(pcap_g)); |
96 | ||
9c7f5afa | 97 | tmm_modules[TMM_RECEIVEPCAPFILE].name = "ReceivePcapFile"; |
a3910884 | 98 | tmm_modules[TMM_RECEIVEPCAPFILE].ThreadInit = ReceivePcapFileThreadInit; |
b753ecce VJ |
99 | tmm_modules[TMM_RECEIVEPCAPFILE].Func = NULL; |
100 | tmm_modules[TMM_RECEIVEPCAPFILE].PktAcqLoop = ReceivePcapFileLoop; | |
a3910884 VJ |
101 | tmm_modules[TMM_RECEIVEPCAPFILE].ThreadExitPrintStats = ReceivePcapFileThreadExitStats; |
102 | tmm_modules[TMM_RECEIVEPCAPFILE].ThreadDeinit = NULL; | |
9c7f5afa | 103 | tmm_modules[TMM_RECEIVEPCAPFILE].RegisterTests = NULL; |
070ed778 | 104 | tmm_modules[TMM_RECEIVEPCAPFILE].cap_flags = 0; |
3f1c4efc | 105 | tmm_modules[TMM_RECEIVEPCAPFILE].flags = TM_FLAG_RECEIVE_TM; |
9c7f5afa VJ |
106 | } |
107 | ||
108 | void TmModuleDecodePcapFileRegister (void) { | |
109 | tmm_modules[TMM_DECODEPCAPFILE].name = "DecodePcapFile"; | |
a3910884 | 110 | tmm_modules[TMM_DECODEPCAPFILE].ThreadInit = DecodePcapFileThreadInit; |
9c7f5afa | 111 | tmm_modules[TMM_DECODEPCAPFILE].Func = DecodePcapFile; |
a3910884 VJ |
112 | tmm_modules[TMM_DECODEPCAPFILE].ThreadExitPrintStats = NULL; |
113 | tmm_modules[TMM_DECODEPCAPFILE].ThreadDeinit = NULL; | |
9c7f5afa | 114 | tmm_modules[TMM_DECODEPCAPFILE].RegisterTests = NULL; |
070ed778 | 115 | tmm_modules[TMM_DECODEPCAPFILE].cap_flags = 0; |
bc6cf438 | 116 | tmm_modules[TMM_DECODEPCAPFILE].flags = TM_FLAG_DECODE_TM; |
9c7f5afa VJ |
117 | } |
118 | ||
b753ecce | 119 | void PcapFileCallbackLoop(char *user, struct pcap_pkthdr *h, u_char *pkt) { |
29d51a61 | 120 | SCEnter(); |
c5e15213 | 121 | |
9c7f5afa | 122 | PcapFileThreadVars *ptv = (PcapFileThreadVars *)user; |
b753ecce | 123 | Packet *p = PacketGetFromQueueOrAlloc(); |
9c7f5afa | 124 | |
b753ecce | 125 | if (unlikely(p == NULL)) { |
e3fc53ec | 126 | SCReturn; |
4e7df60b | 127 | } |
41e9dba2 | 128 | PACKET_PROFILING_TMM_START(p, TMM_RECEIVEPCAPFILE); |
9c7f5afa VJ |
129 | |
130 | p->ts.tv_sec = h->ts.tv_sec; | |
131 | p->ts.tv_usec = h->ts.tv_usec; | |
9ececacd | 132 | SCLogDebug("p->ts.tv_sec %"PRIuMAX"", (uintmax_t)p->ts.tv_sec); |
a4fe9718 | 133 | p->datalink = pcap_g.datalink; |
820b0ded | 134 | p->pcap_cnt = ++pcap_g.cnt; |
9c7f5afa VJ |
135 | |
136 | ptv->pkts++; | |
137 | ptv->bytes += h->caplen; | |
138 | ||
88559901 EL |
139 | if (unlikely(PacketCopyData(p, pkt, h->caplen))) { |
140 | TmqhOutputPacketpool(ptv->tv, p); | |
41e9dba2 | 141 | PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE); |
dd038c19 | 142 | SCReturn; |
88559901 | 143 | } |
41e9dba2 | 144 | PACKET_PROFILING_TMM_END(p, TMM_RECEIVEPCAPFILE); |
c5e15213 | 145 | |
67cea099 VJ |
146 | if (TmThreadsSlotProcessPkt(ptv->tv, ptv->slot, p) != TM_ECODE_OK) { |
147 | pcap_breakloop(pcap_g.pcap_handle); | |
148 | ptv->cb_result = TM_ECODE_FAILED; | |
149 | } | |
4e7df60b | 150 | |
c5e15213 | 151 | SCReturn; |
9c7f5afa VJ |
152 | } |
153 | ||
c5e15213 | 154 | /** |
b753ecce | 155 | * \brief Main PCAP file reading Loop function |
c5e15213 | 156 | */ |
b753ecce | 157 | TmEcode ReceivePcapFileLoop(ThreadVars *tv, void *data, void *slot) { |
53acf089 | 158 | uint16_t packet_q_len = 0; |
c5e15213 | 159 | PcapFileThreadVars *ptv = (PcapFileThreadVars *)data; |
b753ecce VJ |
160 | TmSlot *s = (TmSlot *)slot; |
161 | ptv->slot = s->slot_next; | |
67cea099 | 162 | ptv->cb_result = TM_ECODE_OK; |
b753ecce | 163 | int r; |
4e7df60b | 164 | |
b753ecce | 165 | SCEnter(); |
1d74797b | 166 | |
b753ecce VJ |
167 | while (1) { |
168 | if (suricata_ctl_flags & SURICATA_STOP || | |
169 | suricata_ctl_flags & SURICATA_KILL) | |
170 | { | |
de1d002e | 171 | SCReturnInt(TM_ECODE_OK); |
e3fc53ec | 172 | } |
1d74797b | 173 | |
b753ecce VJ |
174 | /* make sure we have at least one packet in the packet pool, to prevent |
175 | * us from alloc'ing packets at line rate */ | |
176 | do { | |
177 | packet_q_len = PacketPoolSize(); | |
178 | if (unlikely(packet_q_len == 0)) { | |
179 | PacketPoolWait(); | |
180 | } | |
181 | } while (packet_q_len == 0); | |
182 | ||
183 | /* Right now we just support reading packets one at a time. */ | |
184 | r = pcap_dispatch(pcap_g.pcap_handle, (int)packet_q_len, | |
185 | (pcap_handler)PcapFileCallbackLoop, (u_char *)ptv); | |
67cea099 | 186 | if (unlikely(r == -1)) { |
b753ecce VJ |
187 | SCLogError(SC_ERR_PCAP_DISPATCH, "error code %" PRId32 " %s", |
188 | r, pcap_geterr(pcap_g.pcap_handle)); | |
189 | ||
190 | /* in the error state we just kill the engine */ | |
191 | EngineKill(); | |
192 | SCReturnInt(TM_ECODE_FAILED); | |
193 | } else if (unlikely(r == 0)) { | |
194 | SCLogInfo("pcap file end of file reached (pcap err code %" PRId32 ")", r); | |
195 | ||
196 | EngineStop(); | |
197 | break; | |
67cea099 VJ |
198 | } else if (ptv->cb_result == TM_ECODE_FAILED) { |
199 | SCLogError(SC_ERR_PCAP_DISPATCH, "Pcap callback PcapFileCallbackLoop failed"); | |
200 | EngineKill(); | |
201 | SCReturnInt(TM_ECODE_FAILED); | |
4e7df60b | 202 | } |
d68f182e | 203 | SCPerfSyncCountersIfSignalled(tv, 0); |
4e7df60b VJ |
204 | } |
205 | ||
29d51a61 | 206 | SCReturnInt(TM_ECODE_OK); |
9c7f5afa VJ |
207 | } |
208 | ||
40b8afdd | 209 | TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, void *initdata, void **data) { |
29d51a61 | 210 | SCEnter(); |
e0aacac4 | 211 | char *tmpbpfstring = NULL; |
9c7f5afa | 212 | if (initdata == NULL) { |
29d51a61 PR |
213 | SCLogError(SC_ERR_INVALID_ARGUMENT, "error: initdata == NULL"); |
214 | SCReturnInt(TM_ECODE_FAILED); | |
9c7f5afa VJ |
215 | } |
216 | ||
e0aacac4 VJ |
217 | SCLogInfo("reading pcap file %s", (char *)initdata); |
218 | ||
25a3a5c6 | 219 | PcapFileThreadVars *ptv = SCMalloc(sizeof(PcapFileThreadVars)); |
9f4fae5b | 220 | if (ptv == NULL) |
29d51a61 | 221 | SCReturnInt(TM_ECODE_FAILED); |
9c7f5afa VJ |
222 | memset(ptv, 0, sizeof(PcapFileThreadVars)); |
223 | ||
8c3d0c05 | 224 | char errbuf[PCAP_ERRBUF_SIZE] = ""; |
9c7f5afa VJ |
225 | pcap_g.pcap_handle = pcap_open_offline((char *)initdata, errbuf); |
226 | if (pcap_g.pcap_handle == NULL) { | |
52936818 | 227 | SCLogError(SC_ERR_FOPEN, "%s\n", errbuf); |
25a3a5c6 | 228 | SCFree(ptv); |
52936818 | 229 | exit(EXIT_FAILURE); |
9c7f5afa VJ |
230 | } |
231 | ||
ba46c16a | 232 | if (ConfGet("bpf-filter", &tmpbpfstring) != 1) { |
e0aacac4 | 233 | SCLogDebug("could not get bpf or none specified"); |
ba46c16a | 234 | } else { |
e0aacac4 | 235 | SCLogInfo("using bpf-filter \"%s\"", tmpbpfstring); |
ba46c16a WM |
236 | |
237 | if(pcap_compile(pcap_g.pcap_handle,&pcap_g.filter,tmpbpfstring,1,0) < 0) { | |
238 | SCLogError(SC_ERR_BPF,"bpf compilation error %s",pcap_geterr(pcap_g.pcap_handle)); | |
25a3a5c6 | 239 | SCFree(ptv); |
e0aacac4 | 240 | return TM_ECODE_FAILED; |
ba46c16a WM |
241 | } |
242 | ||
243 | if(pcap_setfilter(pcap_g.pcap_handle,&pcap_g.filter) < 0) { | |
244 | SCLogError(SC_ERR_BPF,"could not set bpf filter %s",pcap_geterr(pcap_g.pcap_handle)); | |
25a3a5c6 | 245 | SCFree(ptv); |
e0aacac4 | 246 | return TM_ECODE_FAILED; |
ba46c16a WM |
247 | } |
248 | } | |
249 | ||
a4fe9718 | 250 | pcap_g.datalink = pcap_datalink(pcap_g.pcap_handle); |
e0aacac4 VJ |
251 | SCLogDebug("datalink %" PRId32 "", pcap_g.datalink); |
252 | ||
a4fe9718 | 253 | switch(pcap_g.datalink) { |
dec11038 BS |
254 | case LINKTYPE_LINUX_SLL: |
255 | pcap_g.Decoder = DecodeSll; | |
256 | break; | |
257 | case LINKTYPE_ETHERNET: | |
258 | pcap_g.Decoder = DecodeEthernet; | |
259 | break; | |
260 | case LINKTYPE_PPP: | |
261 | pcap_g.Decoder = DecodePPP; | |
262 | break; | |
8a643213 WM |
263 | case LINKTYPE_RAW: |
264 | pcap_g.Decoder = DecodeRaw; | |
265 | break; | |
266 | ||
dec11038 | 267 | default: |
25a3a5c6 PR |
268 | SCLogError(SC_ERR_UNIMPLEMENTED, "datalink type %" PRId32 " not " |
269 | "(yet) supported in module PcapFile.\n", pcap_g.datalink); | |
270 | SCFree(ptv); | |
29d51a61 | 271 | SCReturnInt(TM_ECODE_FAILED); |
9c7f5afa VJ |
272 | } |
273 | ||
274 | ptv->tv = tv; | |
275 | *data = (void *)ptv; | |
29d51a61 | 276 | SCReturnInt(TM_ECODE_OK); |
9c7f5afa VJ |
277 | } |
278 | ||
279 | void ReceivePcapFileThreadExitStats(ThreadVars *tv, void *data) { | |
29d51a61 | 280 | SCEnter(); |
9c7f5afa VJ |
281 | PcapFileThreadVars *ptv = (PcapFileThreadVars *)data; |
282 | ||
28e15be5 | 283 | SCLogInfo("Pcap-file module read %" PRIu32 " packets, %" PRIu64 " bytes", ptv->pkts, ptv->bytes); |
9c7f5afa VJ |
284 | return; |
285 | } | |
286 | ||
40b8afdd | 287 | TmEcode ReceivePcapFileThreadDeinit(ThreadVars *tv, void *data) { |
29d51a61 PR |
288 | SCEnter(); |
289 | SCReturnInt(TM_ECODE_OK); | |
9c7f5afa VJ |
290 | } |
291 | ||
5133098b AS |
292 | double prev_signaled_ts = 0; |
293 | ||
4e7df60b | 294 | TmEcode DecodePcapFile(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) |
d0e70309 | 295 | { |
29d51a61 | 296 | SCEnter(); |
244f5d54 AS |
297 | DecodeThreadVars *dtv = (DecodeThreadVars *)data; |
298 | ||
57f71f7e | 299 | /* update counters */ |
ceb7e495 | 300 | SCPerfCounterIncr(dtv->counter_pkts, tv->sc_perf_pca); |
8beef4a9 AS |
301 | SCPerfCounterIncr(dtv->counter_pkts_per_sec, tv->sc_perf_pca); |
302 | ||
dd038c19 | 303 | SCPerfCounterAddUI64(dtv->counter_bytes, tv->sc_perf_pca, GET_PKT_LEN(p)); |
4cacb1e9 | 304 | #if 0 |
dd038c19 | 305 | SCPerfCounterAddDouble(dtv->counter_bytes_per_sec, tv->sc_perf_pca, GET_PKT_LEN(p)); |
8beef4a9 | 306 | SCPerfCounterAddDouble(dtv->counter_mbit_per_sec, tv->sc_perf_pca, |
dd038c19 | 307 | (GET_PKT_LEN(p) * 8)/1000000.0 ); |
4cacb1e9 | 308 | #endif |
dd038c19 EL |
309 | SCPerfCounterAddUI64(dtv->counter_avg_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p)); |
310 | SCPerfCounterSetUI64(dtv->counter_max_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p)); | |
d0e70309 | 311 | |
5133098b | 312 | double curr_ts = p->ts.tv_sec + p->ts.tv_usec / 1000.0; |
0150e66e | 313 | if (curr_ts < prev_signaled_ts || (curr_ts - prev_signaled_ts) > 60.0) { |
5133098b AS |
314 | prev_signaled_ts = curr_ts; |
315 | FlowWakeupFlowManagerThread(); | |
316 | } | |
317 | ||
6943a7eb VJ |
318 | /* update the engine time representation based on the timestamp |
319 | * of the packet. */ | |
320 | TimeSet(&p->ts); | |
321 | ||
9c7f5afa | 322 | /* call the decoder */ |
dd038c19 | 323 | pcap_g.Decoder(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq); |
244f5d54 | 324 | |
29d51a61 | 325 | SCReturnInt(TM_ECODE_OK); |
9c7f5afa VJ |
326 | } |
327 | ||
40b8afdd | 328 | TmEcode DecodePcapFileThreadInit(ThreadVars *tv, void *initdata, void **data) |
d0e70309 | 329 | { |
29d51a61 | 330 | SCEnter(); |
244f5d54 | 331 | DecodeThreadVars *dtv = NULL; |
8cc525c9 | 332 | dtv = DecodeThreadVarsAlloc(); |
244f5d54 | 333 | |
8cc525c9 | 334 | if (dtv == NULL) |
29d51a61 | 335 | SCReturnInt(TM_ECODE_FAILED); |
244f5d54 | 336 | |
8beef4a9 | 337 | DecodeRegisterPerfCounters(dtv, tv); |
244f5d54 AS |
338 | |
339 | *data = (void *)dtv; | |
ceb7e495 | 340 | |
29d51a61 | 341 | SCReturnInt(TM_ECODE_OK); |
d0e70309 AS |
342 | } |
343 | ||
9c7f5afa VJ |
344 | /* eof */ |
345 |