|
1 | 1 | /* |
2 | | - * Copyright (c) 2023, Peter Haag |
| 2 | + * Copyright (c) 2023-2026, Peter Haag |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
|
31 | 31 | #include "packet_pcap.h" |
32 | 32 |
|
33 | 33 | #include <errno.h> |
34 | | -#include <fcntl.h> |
35 | | -#include <pthread.h> |
36 | | -#include <signal.h> |
37 | | -#include <stdarg.h> |
38 | | -#include <stdatomic.h> |
39 | 34 | #include <stdint.h> |
40 | | -#include <stdio.h> |
41 | 35 | #include <stdlib.h> |
42 | 36 | #include <string.h> |
43 | | -#include <sys/param.h> |
44 | | -#include <sys/time.h> |
45 | | -#include <sys/types.h> |
46 | | -#include <time.h> |
47 | | -#include <unistd.h> |
48 | 37 |
|
49 | | -#include "packet_pcap.h" |
50 | | -#include "pcap_gzip.h" |
51 | | -#include "pcapdump.h" |
52 | | -#include "pcaproc.h" |
53 | | -#include "queue.h" |
54 | 38 | #include "util.h" |
55 | 39 |
|
56 | | -static void CloseSocket(packetParam_t *param); |
57 | | - |
58 | | -static int setup_pcap_filter(packetParam_t *param, char *filter); |
59 | | - |
60 | | -static void ReportStat(packetParam_t *param); |
61 | | - |
62 | | -static inline void PcapDump(packetBuffer_t *packetBuffer, struct pcap_pkthdr *hdr, const u_char *sp); |
63 | | - |
64 | | -static struct pcap_stat last_stat = {0}; |
65 | | -static proc_stat_t proc_stat = {0}; |
66 | | - |
67 | | -#define TO_MS 100 |
68 | | - |
69 | 40 | /* |
70 | | - * Functions |
| 41 | + * Batch helper functions for pcap file processing |
71 | 42 | */ |
72 | 43 |
|
73 | | -static void CloseSocket(packetParam_t *param) { pcap_close(param->pcap_dev); } // End of CloseSocket |
74 | | - |
75 | | -// batch helpers |
76 | | - |
77 | | -void *payload_handle(PktBatch_t *batch, size_t idx) { |
78 | | - // return aligned payload for index idx |
79 | | - return batch->payload_slab + (ptrdiff_t)idx * (ptrdiff_t)batch->payload_size; |
80 | | -} // End of payload_handle |
81 | | - |
82 | 44 | PktBatch_t *batch_alloc(size_t cap, uint32_t snaplen) { |
83 | 45 | PktBatch_t *batch = calloc(1, sizeof(PktBatch_t) + cap * sizeof(PacketRef)); |
84 | 46 | if (!batch) { |
@@ -122,281 +84,3 @@ void batch_free(PktBatch_t *batch) { |
122 | 84 |
|
123 | 85 | free(batch); |
124 | 86 | } // End of batch_free |
125 | | - |
126 | | -// live device |
127 | | -int setup_pcap_live(packetParam_t *param, char *device, char *filter, unsigned snaplen, size_t buffsize) { |
128 | | - pcap_t *p; |
129 | | - char errbuf[PCAP_ERRBUF_SIZE]; |
130 | | - |
131 | | - errbuf[0] = '\0'; |
132 | | - |
133 | | - /* |
134 | | - * If device is NULL, that means the user did not specify one and is |
135 | | - * leaving it up libpcap to find one. |
136 | | - */ |
137 | | - if (device == NULL) { |
138 | | - pcap_if_t *alldevsp = NULL; |
139 | | - if (pcap_findalldevs(&alldevsp, errbuf) == -1) { |
140 | | - LogError("pcap_findalldevs() error: %s in %s line %d", errbuf, __FILE__, __LINE__); |
141 | | - return -1; |
142 | | - } |
143 | | - if (alldevsp == NULL) { |
144 | | - LogError("Couldn't find default device"); |
145 | | - return -1; |
146 | | - } |
147 | | - device = alldevsp[0].name; |
148 | | - LogError("Listen on %s", device); |
149 | | - } |
150 | | - |
151 | | - /* |
152 | | - * Open the packet capturing device with the following values: |
153 | | - * |
154 | | - * SNAPLEN: User defined or 1600 bytes |
155 | | - * PROMISC: on |
156 | | - * The interface needs to be in promiscuous mode to capture all |
157 | | - * network traffic on the localnet. |
158 | | - * TO_MS: 100ms |
159 | | - * this value specifies how long to wait, if a packet arrives |
160 | | - * until the application may request it. longer time have the advantage |
161 | | - * in processing multiple packets - less interrupts but more packet loss |
162 | | - */ |
163 | | - p = pcap_create(device, errbuf); |
164 | | - if (!p) { |
165 | | - LogError("pcap_create() failed on %s: %s", device, errbuf); |
166 | | - return -1; |
167 | | - } |
168 | | - |
169 | | - if (pcap_set_snaplen(p, snaplen)) { |
170 | | - LogError("pcap_set_snaplen() failed: %s", pcap_geterr(p)); |
171 | | - pcap_close(p); |
172 | | - return -1; |
173 | | - } |
174 | | - |
175 | | - if (pcap_set_promisc(p, PROMISC)) { |
176 | | - LogError("pcap_set_promisc() failed: %s", pcap_geterr(p)); |
177 | | - pcap_close(p); |
178 | | - return -1; |
179 | | - } |
180 | | - |
181 | | - if (pcap_set_timeout(p, TO_MS)) { |
182 | | - LogError("pcap_set_timeout() failed: %s", pcap_geterr(p)); |
183 | | - pcap_close(p); |
184 | | - return -1; |
185 | | - } |
186 | | - |
187 | | - if (pcap_set_buffer_size(p, buffsize) < 0) { |
188 | | - LogError("pcap_set_buffer_size() failed: %s", pcap_geterr(p)); |
189 | | - pcap_close(p); |
190 | | - return -1; |
191 | | - } |
192 | | - |
193 | | - if (pcap_activate(p)) { |
194 | | - LogError("pcap_activate() failed: %s", pcap_geterr(p)); |
195 | | - pcap_close(p); |
196 | | - return -1; |
197 | | - } |
198 | | - |
199 | | - param->snaplen = snaplen; |
200 | | - param->linktype = pcap_datalink(p); |
201 | | - param->pcap_dev = p; |
202 | | - |
203 | | - if (filter && !setup_pcap_filter(param, filter)) { |
204 | | - pcap_close(param->pcap_dev); |
205 | | - return -1; |
206 | | - } |
207 | | - |
208 | | - return 0; |
209 | | - |
210 | | -} // End of setup_pcap_live |
211 | | - |
212 | | -static int setup_pcap_filter(packetParam_t *param, char *filter) { |
213 | | - struct bpf_program filter_code; |
214 | | - |
215 | | - if (pcap_compile(param->pcap_dev, &filter_code, filter, 1, PCAP_NETMASK_UNKNOWN)) { |
216 | | - LogError("pcap_compile() failed: %s", pcap_geterr(param->pcap_dev)); |
217 | | - return 0; |
218 | | - } |
219 | | - |
220 | | - if (pcap_setfilter(param->pcap_dev, &filter_code)) { |
221 | | - LogError("pcap_setfilter() failed: %s", pcap_geterr(param->pcap_dev)); |
222 | | - return 0; |
223 | | - } |
224 | | - |
225 | | - return 1; |
226 | | - |
227 | | -} // End of setup_pcap_filter |
228 | | - |
229 | | -static void ReportStat(packetParam_t *param) { |
230 | | - struct pcap_stat pstat; |
231 | | - |
232 | | - if (param->live) { |
233 | | - memset((void *)&pstat, 0, sizeof(struct pcap_stat)); |
234 | | - if (pcap_stats(param->pcap_dev, &pstat) < 0) { |
235 | | - LogError("pcap_stats() failed: %s", pcap_geterr(param->pcap_dev)); |
236 | | - } else { |
237 | | - LogInfo("Stat: received: %d, dropped by OS/Buffer: %d, dropped by interface/driver: %d", pstat.ps_recv - last_stat.ps_recv, |
238 | | - pstat.ps_drop - last_stat.ps_drop, pstat.ps_ifdrop - last_stat.ps_ifdrop); |
239 | | - last_stat = pstat; |
240 | | - } |
241 | | - } |
242 | | - |
243 | | - LogInfo("Processed: %u, decode errors: %u, short caplen: %u, unknown: %u", param->proc_stat.packets - proc_stat.packets, |
244 | | - param->proc_stat.decoding_errors - proc_stat.decoding_errors, param->proc_stat.short_snap - proc_stat.short_snap, |
245 | | - param->proc_stat.unknown - proc_stat.unknown); |
246 | | - |
247 | | - proc_stat = param->proc_stat; |
248 | | - |
249 | | -} // End of ReportStat |
250 | | - |
251 | | -static inline void PcapDump(packetBuffer_t *packetBuffer, struct pcap_pkthdr *hdr, const u_char *sp) { |
252 | | - // caller checks for enough space in buffer |
253 | | - struct pcap_sf_pkthdr sf_hdr; |
254 | | - sf_hdr.ts.tv_sec = hdr->ts.tv_sec; |
255 | | - sf_hdr.ts.tv_usec = hdr->ts.tv_usec; |
256 | | - sf_hdr.caplen = hdr->caplen; |
257 | | - sf_hdr.len = hdr->len; |
258 | | - |
259 | | - void *p = packetBuffer->buffer + packetBuffer->bufferSize; |
260 | | - memcpy(p, (void *)&sf_hdr, sizeof(sf_hdr)); |
261 | | - p += sizeof(struct pcap_sf_pkthdr); |
262 | | - |
263 | | - memcpy(p, (void *)sp, hdr->caplen); |
264 | | - packetBuffer->bufferSize += (sizeof(struct pcap_sf_pkthdr) + hdr->caplen); |
265 | | - |
266 | | -} // End of PcapDump |
267 | | - |
268 | | -void __attribute__((noreturn)) * pcap_packet_thread(void *args) { |
269 | | - packetParam_t *packetParam = (packetParam_t *)args; |
270 | | - |
271 | | - Init_NodeAllocator(); |
272 | | - time_t t_win = packetParam->t_win; |
273 | | - time_t now = 0; |
274 | | - if (packetParam->live) { |
275 | | - // start time is now for live capture |
276 | | - now = time(NULL); |
277 | | - } else { |
278 | | - struct pcap_pkthdr *hdr; |
279 | | - const u_char *data; |
280 | | - // start time is time of 1st packet for file reading |
281 | | - long pos = ftell(pcap_file(packetParam->pcap_dev)); |
282 | | - if (pcap_next_ex(packetParam->pcap_dev, &hdr, &data) == 1) { |
283 | | - now = hdr->ts.tv_sec; |
284 | | - } |
285 | | - // reset file to 1st packet |
286 | | - fseek(pcap_file(packetParam->pcap_dev), pos, SEEK_SET); |
287 | | - } |
288 | | - time_t t_start = now - (now % t_win); |
289 | | - |
290 | | - int done = atomic_load_explicit(packetParam->done, memory_order_relaxed); |
291 | | - int DoPacketDump = packetParam->bufferQueue != NULL; |
292 | | - |
293 | | - packetBuffer_t *packetBuffer = NULL; |
294 | | - if (DoPacketDump) { |
295 | | - packetBuffer = queue_pop(packetParam->bufferQueue); |
296 | | - if (packetBuffer == QUEUE_CLOSED) done = 1; |
297 | | - } |
298 | | - |
299 | | - while (!done) { |
300 | | - struct pcap_pkthdr *hdr; |
301 | | - const u_char *data; |
302 | | - |
303 | | - int ret = pcap_next_ex(packetParam->pcap_dev, &hdr, &data); |
304 | | - time_t t_packet = 0; |
305 | | - switch (ret) { |
306 | | - case 1: { |
307 | | - // packet read ok |
308 | | - dbg_printf("pcap_next_ex() next packet\n"); |
309 | | - t_packet = hdr->ts.tv_sec; |
310 | | - if ((t_packet - t_start) >= t_win) { |
311 | | - if (DoPacketDump) { |
312 | | - // Rote dump file - close old - open new |
313 | | - dbg_printf("packet_thread() flush file - buffer: %zu\n", packetBuffer->bufferSize); |
314 | | - packetBuffer->timeStamp = t_start; |
315 | | - queue_push(packetParam->flushQueue, packetBuffer); |
316 | | - packetBuffer = queue_pop(packetParam->bufferQueue); |
317 | | - if (packetBuffer == QUEUE_CLOSED) { |
318 | | - done = 1; |
319 | | - continue; |
320 | | - } |
321 | | - } |
322 | | - // Rotate flow file |
323 | | - ReportStat(packetParam); |
324 | | - Push_SyncNode(packetParam->NodeList, t_start); |
325 | | - t_start = t_packet - (t_packet % t_win); |
326 | | - } |
327 | | - |
328 | | - int ok = ProcessPacket(packetParam, hdr, data); |
329 | | - size_t size = sizeof(struct pcap_sf_pkthdr) + hdr->caplen; |
330 | | - if (DoPacketDump && ok) { |
331 | | - if ((packetBuffer->bufferSize + size) > BUFFSIZE) { |
332 | | - packetBuffer->timeStamp = 0; |
333 | | - dbg_printf("packet_thread() flush buffer - size %zu\n", packetBuffer->bufferSize); |
334 | | - queue_push(packetParam->flushQueue, packetBuffer); |
335 | | - packetBuffer = queue_pop(packetParam->bufferQueue); |
336 | | - if (packetBuffer == QUEUE_CLOSED) { |
337 | | - done = 1; |
338 | | - continue; |
339 | | - } |
340 | | - } |
341 | | - PcapDump(packetBuffer, hdr, data); |
342 | | - } |
343 | | - } break; |
344 | | - case 0: { |
345 | | - // live capture idle cycle |
346 | | - dbg_printf("pcap_next_ex() read live - timeout\n"); |
347 | | - struct timeval tv; |
348 | | - gettimeofday(&tv, NULL); |
349 | | - t_packet = tv.tv_sec; |
350 | | - if ((t_packet - t_start) >= t_win) { /* rotate file */ |
351 | | - if (DoPacketDump) { |
352 | | - // Rote dump file - close old - open new |
353 | | - packetBuffer->timeStamp = t_start; |
354 | | - queue_push(packetParam->flushQueue, packetBuffer); |
355 | | - packetBuffer = queue_pop(packetParam->bufferQueue); |
356 | | - if (packetBuffer == QUEUE_CLOSED) { |
357 | | - done = 1; |
358 | | - continue; |
359 | | - } |
360 | | - } |
361 | | - ReportStat(packetParam); |
362 | | - Push_SyncNode(packetParam->NodeList, t_start); |
363 | | - t_start = t_packet - (t_packet % t_win); |
364 | | - } |
365 | | - CacheCheck(packetParam->NodeList, t_start); |
366 | | - } break; |
367 | | - case -1: |
368 | | - // signal error reading the packet |
369 | | - dbg_printf("pcap_next_ex() read live - error\n"); |
370 | | - LogError("pcap_next_ex() read error: '%s'", pcap_geterr(packetParam->pcap_dev)); |
371 | | - done = 1; |
372 | | - break; |
373 | | - case -2: // End of packet file |
374 | | - // signal parent, job is done |
375 | | - dbg_printf("pcap_next_ex() read live - eof\n"); |
376 | | - done = 1; |
377 | | - break; |
378 | | - default: |
379 | | - pcap_breakloop(packetParam->pcap_dev); |
380 | | - LogError("Unexpected pcap_next_ex() return value: %i", ret); |
381 | | - done = 1; |
382 | | - } |
383 | | - done = done || atomic_load_explicit(packetParam->done, memory_order_relaxed); |
384 | | - } |
385 | | - |
386 | | - dbg_printf("Done capture loop - signal close\n"); |
387 | | - if (DoPacketDump && packetBuffer != QUEUE_CLOSED) { |
388 | | - packetBuffer->timeStamp = t_start; |
389 | | - queue_push(packetParam->flushQueue, packetBuffer); |
390 | | - queue_close(packetParam->flushQueue); |
391 | | - } |
392 | | - |
393 | | - CloseSocket(packetParam); |
394 | | - ReportStat(packetParam); |
395 | | - packetParam->t_win = t_start; |
396 | | - |
397 | | - // Tell parent we are gone |
398 | | - pthread_kill(packetParam->parent, SIGUSR1); |
399 | | - pthread_exit("leave pcap_loop()"); |
400 | | - /* NOTREACHED */ |
401 | | - |
402 | | -} /* End of packet_thread */ |
0 commit comments