1 /****************************************************************************
2  *
3  * Copyright (C) 2007-2013 Sourcefire, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License Version 2 as
7  * published by the Free Software Foundation.  You may not use, modify or
8  * distribute this program under any other version of the GNU General
9  * Public License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  *
20  ****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <arpa/inet.h>
26 #include <pcap.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined __DragonFly__
31 #include <sys/socket.h>
32 #endif
33 
34 #include "daq.h"
35 #include "daq_api.h"
36 
37 #define DAQ_MOD_VERSION 4
38 
39 #define DAQ_NAME "dump"
40 #define DAQ_TYPE (DAQ_TYPE_FILE_CAPABLE | DAQ_TYPE_INTF_CAPABLE | \
41                   DAQ_TYPE_INLINE_CAPABLE | DAQ_TYPE_MULTI_INSTANCE)
42 
43 #define DAQ_DUMP_PCAP_FILE "inline-out.pcap"
44 #define DAQ_DUMP_TEXT_FILE "inline-out.txt"
45 
46 typedef enum {
47     DUMP_OUTPUT_NONE = 0x0,
48     DUMP_OUTPUT_PCAP = 0x1,
49     DUMP_OUTPUT_TEXT = 0x2,
50     DUMP_OUTPUT_BOTH = 0x3
51 } DumpOutputType;
52 
53 typedef struct {
54     // delegate most stuff to daq_pcap
55     DAQ_Module_t* module;
56     void* handle;
57 
58     // but write all output packets here
59     pcap_dumper_t* dump;
60     char* pcap_filename;
61 
62     // and write other textual output here
63     FILE *text_out;
64     char* text_filename;
65 
66     DumpOutputType output_type;
67 
68     // by linking in with these
69     DAQ_Analysis_Func_t callback;
70     void* user;
71 
72     DAQ_Stats_t stats;
73 } DumpImpl;
74 
75 static int dump_daq_stop(void*);
76 
daq_dump_get_vars(DumpImpl * impl,DAQ_Config_t * cfg,char * errBuf,size_t errMax)77 static int daq_dump_get_vars (
78     DumpImpl* impl, DAQ_Config_t* cfg, char* errBuf, size_t errMax
79 ) {
80     const char* s = NULL;
81     DAQ_Dict* entry;
82 
83     for ( entry = cfg->values; entry; entry = entry->next)
84     {
85         if ( !strcmp(entry->key, "load-mode") )
86         {
87             s = entry->value;
88         }
89         else if ( !strcmp(entry->key, "file") )
90         {
91             impl->pcap_filename = strdup(entry->value);
92         }
93         else if ( !strcmp(entry->key, "text-file") )
94         {
95             impl->text_filename = strdup(entry->value);
96         }
97         else if ( !strcmp(entry->key, "output") )
98         {
99             if ( !strcmp(entry->value, "none") )
100                 impl->output_type = DUMP_OUTPUT_NONE;
101             else if ( !strcmp(entry->value, "pcap") )
102                 impl->output_type = DUMP_OUTPUT_PCAP;
103             else if ( !strcmp(entry->value, "text") )
104                 impl->output_type = DUMP_OUTPUT_TEXT;
105             else if ( !strcmp(entry->value, "both") )
106                 impl->output_type = DUMP_OUTPUT_BOTH;
107             else
108             {
109                 snprintf(errBuf, errMax, "invalid output type (%s)", entry->value);
110             }
111         }
112     }
113     if ( !s )
114         return 1;
115 
116     if ( !strcasecmp(s, "read-file") )
117     {
118         cfg->mode = DAQ_MODE_READ_FILE;
119         return 1;
120     }
121     else if ( !strcasecmp(s, "passive") )
122     {
123         cfg->mode = DAQ_MODE_PASSIVE;
124         return 1;
125     }
126     else if ( !strcasecmp(s, "inline") )
127     {
128         cfg->mode = DAQ_MODE_INLINE;
129         return 1;
130     }
131     snprintf(errBuf, errMax, "invalid load-mode (%s)", s);
132     return 0;
133 }
134 
hexdump(FILE * fp,const uint8_t * data,unsigned int len,const char * prefix)135 static void hexdump(FILE *fp, const uint8_t *data, unsigned int len, const char *prefix)
136 {
137     unsigned int i;
138     for (i = 0; i < len; i++)
139     {
140         if (i % 16 == 0)
141             fprintf(fp, "\n%s", prefix ? prefix : "");
142         else if (i % 2 == 0)
143             fprintf(fp, " ");
144         fprintf(fp, "%02x", data[i]);
145     }
146     fprintf(fp, "\n");
147 }
148 
149 //-------------------------------------------------------------------------
150 // constructor / destructor
151 
dump_daq_initialize(const DAQ_Config_t * cfg,void ** handle,char * errBuf,size_t errMax)152 static int dump_daq_initialize (
153     const DAQ_Config_t* cfg, void** handle, char* errBuf, size_t errMax)
154 {
155     DumpImpl* impl;
156     impl = calloc(1, sizeof(*impl));
157     DAQ_Module_t* mod = (DAQ_Module_t*)cfg->extra;
158     DAQ_Config_t sub_cfg = *cfg;
159     int err;
160 
161     if ( !impl )
162     {
163         snprintf(errBuf, errMax,
164             "%s: Couldn't allocate memory for the DAQ context",
165             __func__);
166         return DAQ_ERROR_NOMEM;
167     }
168     if ( !mod || !(mod->type & DAQ_TYPE_FILE_CAPABLE) )
169     {
170         snprintf(errBuf, errMax, "%s: no file capable daq provided", __func__);
171         free(impl);
172         return DAQ_ERROR;
173     }
174 
175     impl->output_type = DUMP_OUTPUT_PCAP;
176     if ( !daq_dump_get_vars(impl, &sub_cfg, errBuf, errMax) )
177     {
178         free(impl);
179         return DAQ_ERROR;
180     }
181     err = mod->initialize(&sub_cfg, &impl->handle, errBuf, errMax);
182 
183     if ( err )
184     {
185         free(impl);
186         return err;
187     }
188     impl->module = mod;
189     *handle = impl;
190 
191     return DAQ_SUCCESS;
192 }
193 
dump_daq_shutdown(void * handle)194 static void dump_daq_shutdown (void* handle)
195 {
196     DumpImpl* impl = (DumpImpl*)handle;
197     impl->module->shutdown(impl->handle);
198     if ( impl->pcap_filename )
199         free(impl->pcap_filename);
200     if ( impl->text_filename )
201         free(impl->text_filename);
202     free(impl);
203 }
204 
205 //-------------------------------------------------------------------------
206 // packet processing functions:
207 // forward all but blocks, retries and blacklists:
208 static const int s_fwd[MAX_DAQ_VERDICT] = { 1, 0, 1, 1, 0, 1, 0 };
209 // We don't have access to daq_verdict_string() because we're not linking
210 // against LibDAQ, so pack our own copy.
211 static const char *daq_verdict_strings[MAX_DAQ_VERDICT] = {
212     "Pass",         // DAQ_VERDICT_PASS
213     "Block",        // DAQ_VERDICT_BLOCK
214     "Replace",      // DAQ_VERDICT_REPLACE
215     "Whitelist",    // DAQ_VERDICT_WHITELIST
216     "Blacklist",    // DAQ_VERDICT_BLACKLIST
217     "Ignore",       // DAQ_VERDICT_IGNORE
218     "Retry"         // DAQ_VERDICT_RETRY
219 };
220 
daq_dump_capture(void * user,const DAQ_PktHdr_t * hdr,const uint8_t * pkt)221 static DAQ_Verdict daq_dump_capture (
222     void* user, const DAQ_PktHdr_t* hdr, const uint8_t* pkt)
223 {
224     DumpImpl* impl = (DumpImpl*)user;
225     DAQ_Verdict verdict = impl->callback(impl->user, hdr, pkt);
226 
227     if ( verdict >= MAX_DAQ_VERDICT )
228         verdict = DAQ_VERDICT_BLOCK;
229 
230     impl->stats.verdicts[verdict]++;
231 
232     if ( impl->dump && s_fwd[verdict] )
233         pcap_dump((u_char*)impl->dump, (struct pcap_pkthdr*)hdr, pkt);
234 
235     if (impl->text_out)
236     {
237         fprintf(impl->text_out, "PV: %lu.%lu(%u): %s\n", (unsigned long) hdr->ts.tv_sec,
238                 (unsigned long) hdr->ts.tv_usec, hdr->caplen, daq_verdict_strings[verdict]);
239         if (verdict == DAQ_VERDICT_REPLACE)
240             hexdump(impl->text_out, pkt, hdr->caplen, "    ");
241     }
242 
243     return verdict;
244 }
245 
dump_daq_acquire(void * handle,int cnt,DAQ_Analysis_Func_t callback,DAQ_Meta_Func_t metaback,void * user)246 static int dump_daq_acquire (
247     void* handle, int cnt, DAQ_Analysis_Func_t callback, DAQ_Meta_Func_t metaback, void* user)
248 {
249     DumpImpl* impl = (DumpImpl*)handle;
250     impl->callback = callback;
251     impl->user = user;
252     return impl->module->acquire(impl->handle, cnt, daq_dump_capture, metaback, impl);
253 }
254 
dump_daq_inject(void * handle,const DAQ_PktHdr_t * hdr,const uint8_t * data,uint32_t len,int reverse)255 static int dump_daq_inject (
256     void* handle, const DAQ_PktHdr_t* hdr, const uint8_t* data, uint32_t len,
257     int reverse)
258 {
259     DumpImpl* impl = (DumpImpl*)handle;
260 
261     if (impl->text_out)
262     {
263         fprintf(impl->text_out, "%cI: %lu.%lu(%u): %u\n", reverse ? 'R' : 'F',
264                 (unsigned long) hdr->ts.tv_sec, (unsigned long) hdr->ts.tv_usec, hdr->caplen, len);
265         hexdump(impl->text_out, data, len, "    ");
266         fprintf(impl->text_out, "\n");
267     }
268 
269     if (impl->dump)
270     {
271         // copy the original header to get the same
272         // timestamps but overwrite the lengths
273         DAQ_PktHdr_t h = *hdr;
274         h.pktlen = h.caplen = len;
275         pcap_dump((u_char*)impl->dump, (struct pcap_pkthdr*)&h, data);
276         if ( ferror(pcap_dump_file(impl->dump)) )
277         {
278             impl->module->set_errbuf(impl->handle, "inject can't write to dump file");
279             return DAQ_ERROR;
280         }
281     }
282 
283     impl->stats.packets_injected++;
284     return DAQ_SUCCESS;
285 }
286 
287 //-------------------------------------------------------------------------
288 
dump_daq_start(void * handle)289 static int dump_daq_start (void* handle)
290 {
291     DumpImpl* impl = (DumpImpl*)handle;
292     int dlt;
293     int snap;
294 
295     int ret = impl->module->start(impl->handle);
296 
297     if ( ret )
298         return ret;
299 
300     dlt = impl->module->get_datalink_type(impl->handle);
301     snap = impl->module->get_snaplen(impl->handle);
302 
303     if ( impl->output_type & DUMP_OUTPUT_PCAP )
304     {
305         const char* pcap_filename = impl->pcap_filename ? impl->pcap_filename : DAQ_DUMP_PCAP_FILE;
306         pcap_t* pcap;
307 
308         pcap = pcap_open_dead(dlt, snap);
309         impl->dump = pcap ? pcap_dump_open(pcap, pcap_filename) : NULL;
310         if ( !impl->dump )
311         {
312             impl->module->stop(impl->handle);
313             impl->module->set_errbuf(impl->handle, "can't open dump file");
314             return DAQ_ERROR;
315         }
316         pcap_close(pcap);
317     }
318 
319     if ( impl->output_type & DUMP_OUTPUT_TEXT )
320     {
321         const char* text_filename = impl->text_filename ? impl->text_filename : DAQ_DUMP_TEXT_FILE;
322 
323         impl->text_out = fopen(text_filename, "w");
324         if ( !impl->text_out )
325         {
326             impl->module->stop(impl->handle);
327             impl->module->set_errbuf(impl->handle, "can't open text output file");
328             return DAQ_ERROR;
329         }
330     }
331 
332     return DAQ_SUCCESS;
333 }
334 
dump_daq_stop(void * handle)335 static int dump_daq_stop (void* handle)
336 {
337     DumpImpl* impl = (DumpImpl*)handle;
338     int err = impl->module->stop(impl->handle);
339 
340     if ( err )
341         return err;
342 
343     if ( impl->dump )
344     {
345         pcap_dump_close(impl->dump);
346         impl->dump = NULL;
347     }
348 
349     if ( impl->text_out )
350     {
351         fclose(impl->text_out);
352         impl->text_out = NULL;
353     }
354 
355     return DAQ_SUCCESS;
356 }
357 
358 //-------------------------------------------------------------------------
359 // these methods are delegated to the pcap daq
360 
dump_daq_set_filter(void * handle,const char * filter)361 static int dump_daq_set_filter (void* handle, const char* filter)
362 {
363     DumpImpl* impl = (DumpImpl*)handle;
364     return impl->module->set_filter(impl->handle, filter);
365 }
366 
dump_daq_breakloop(void * handle)367 static int dump_daq_breakloop (void* handle)
368 {
369     DumpImpl* impl = (DumpImpl*)handle;
370     return impl->module->breakloop(impl->handle);
371 }
372 
dump_daq_check_status(void * handle)373 static DAQ_State dump_daq_check_status (void* handle)
374 {
375     DumpImpl* impl = (DumpImpl*)handle;
376     return impl->module->check_status(impl->handle);
377 }
378 
dump_daq_get_stats(void * handle,DAQ_Stats_t * stats)379 static int dump_daq_get_stats (void* handle, DAQ_Stats_t* stats)
380 {
381     DumpImpl* impl = (DumpImpl*)handle;
382     int ret = impl->module->get_stats(impl->handle, stats);
383     int i;
384 
385     for ( i = 0; i < MAX_DAQ_VERDICT; i++ )
386         stats->verdicts[i] = impl->stats.verdicts[i];
387 
388     stats->packets_injected = impl->stats.packets_injected;
389     return ret;
390 }
391 
dump_daq_reset_stats(void * handle)392 static void dump_daq_reset_stats (void* handle)
393 {
394     DumpImpl* impl = (DumpImpl*)handle;
395     impl->module->reset_stats(impl->handle);
396     memset(&impl->stats, 0, sizeof(impl->stats));
397 }
398 
dump_daq_get_snaplen(void * handle)399 static int dump_daq_get_snaplen (void* handle)
400 {
401     DumpImpl* impl = (DumpImpl*)handle;
402     return impl->module->get_snaplen(impl->handle);
403 }
404 
dump_daq_get_capabilities(void * handle)405 static uint32_t dump_daq_get_capabilities (void* handle)
406 {
407     DumpImpl* impl = (DumpImpl*)handle;
408     uint32_t caps = impl->module->get_capabilities(impl->handle);
409     caps |= DAQ_CAPA_BLOCK | DAQ_CAPA_REPLACE | DAQ_CAPA_INJECT;
410     return caps;
411 }
412 
dump_daq_get_datalink_type(void * handle)413 static int dump_daq_get_datalink_type (void *handle)
414 {
415     DumpImpl* impl = (DumpImpl*)handle;
416     return impl->module->get_datalink_type(impl->handle);
417 }
418 
dump_daq_get_errbuf(void * handle)419 static const char* dump_daq_get_errbuf (void* handle)
420 {
421     DumpImpl* impl = (DumpImpl*)handle;
422     return impl->module->get_errbuf(impl->handle);
423 }
424 
dump_daq_set_errbuf(void * handle,const char * s)425 static void dump_daq_set_errbuf (void* handle, const char* s)
426 {
427     DumpImpl* impl = (DumpImpl*)handle;
428     impl->module->set_errbuf(impl->handle, s ? s : "");
429 }
430 
dump_daq_get_device_index(void * handle,const char * device)431 static int dump_daq_get_device_index(void* handle, const char* device)
432 {
433     DumpImpl* impl = (DumpImpl*)handle;
434     return impl->module->get_device_index(impl->handle, device);
435 }
436 
dump_daq_modify_flow(void * handle,const DAQ_PktHdr_t * hdr,const DAQ_ModFlow_t * modify)437 static int dump_daq_modify_flow(void *handle, const DAQ_PktHdr_t *hdr, const DAQ_ModFlow_t *modify)
438 {
439     DumpImpl* impl = (DumpImpl*)handle;
440 
441     if (impl->text_out)
442     {
443         fprintf(impl->text_out, "MF: %lu.%lu(%u): %d %u \n", (unsigned long) hdr->ts.tv_sec,
444                 (unsigned long) hdr->ts.tv_usec, hdr->caplen, modify->type, modify->length);
445         hexdump(impl->text_out, modify->value, modify->length, "    ");
446     }
447     return DAQ_SUCCESS;
448 }
449 
dump_daq_dp_add_dc(void * handle,const DAQ_PktHdr_t * hdr,DAQ_DP_key_t * dp_key,const uint8_t * packet_data,DAQ_Data_Channel_Params_t * params)450 static int dump_daq_dp_add_dc(void *handle, const DAQ_PktHdr_t *hdr, DAQ_DP_key_t *dp_key,
451                                 const uint8_t *packet_data, DAQ_Data_Channel_Params_t *params)
452 {
453     DumpImpl* impl = (DumpImpl*)handle;
454 
455     if (impl->text_out)
456     {
457         char src_addr_str[INET6_ADDRSTRLEN], dst_addr_str[INET6_ADDRSTRLEN];
458 
459         fprintf(impl->text_out, "DP: %lu.%lu(%u):\n", (unsigned long) hdr->ts.tv_sec,
460                 (unsigned long) hdr->ts.tv_usec, hdr->caplen);
461         if (dp_key->src_af == AF_INET)
462             inet_ntop(AF_INET, &dp_key->sa.src_ip4, src_addr_str, sizeof(src_addr_str));
463         else
464             inet_ntop(AF_INET6, &dp_key->sa.src_ip6, src_addr_str, sizeof(src_addr_str));
465         if (dp_key->dst_af == AF_INET)
466             inet_ntop(AF_INET, &dp_key->da.dst_ip4, dst_addr_str, sizeof(dst_addr_str));
467         else
468             inet_ntop(AF_INET6, &dp_key->da.dst_ip6, dst_addr_str, sizeof(dst_addr_str));
469         fprintf(impl->text_out, "    %s:%hu -> %s:%hu (%hhu)\n", src_addr_str, dp_key->src_port,
470                 dst_addr_str, dp_key->dst_port, dp_key->protocol);
471         fprintf(impl->text_out, "    %hu %hu %hu %hu 0x%X %u\n", dp_key->address_space_id, dp_key->tunnel_type,
472                 dp_key->vlan_id, dp_key->vlan_cnots, params ? params->flags : 0, params ? params->timeout_ms : 0);
473     }
474     return DAQ_SUCCESS;
475 }
476 
477 //-------------------------------------------------------------------------
478 
479 #ifdef BUILDING_SO
480 DAQ_SO_PUBLIC DAQ_Module_t DAQ_MODULE_DATA =
481 #else
482 DAQ_Module_t dump_daq_module_data =
483 #endif
484 {
485     .api_version = DAQ_API_VERSION,
486     .module_version = DAQ_MOD_VERSION,
487     .name = DAQ_NAME,
488     .type = DAQ_TYPE,
489     .initialize = dump_daq_initialize,
490     .set_filter = dump_daq_set_filter,
491     .start = dump_daq_start,
492     .acquire = dump_daq_acquire,
493     .inject = dump_daq_inject,
494     .breakloop = dump_daq_breakloop,
495     .stop = dump_daq_stop,
496     .shutdown = dump_daq_shutdown,
497     .check_status = dump_daq_check_status,
498     .get_stats = dump_daq_get_stats,
499     .reset_stats = dump_daq_reset_stats,
500     .get_snaplen = dump_daq_get_snaplen,
501     .get_capabilities = dump_daq_get_capabilities,
502     .get_datalink_type = dump_daq_get_datalink_type,
503     .get_errbuf = dump_daq_get_errbuf,
504     .set_errbuf = dump_daq_set_errbuf,
505     .get_device_index = dump_daq_get_device_index,
506     .modify_flow = dump_daq_modify_flow,
507     .hup_prep = NULL,
508     .hup_apply = NULL,
509     .hup_post = NULL,
510     .dp_add_dc = dump_daq_dp_add_dc,
511     .query_flow = NULL,
512 };
513 
514