1 /*
2 ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 ** Copyright (C) 2010-2013 Sourcefire, Inc.
4 ** Author: Michael R. Altizer <mialtize@cisco.com>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License Version 2 as
8 ** published by the Free Software Foundation.  You may not use, modify or
9 ** distribute this program under any other version of the GNU General
10 ** Public License.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software
19 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <pcap.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "daq_module_api.h"
32 
33 #define DAQ_DUMP_VERSION 5
34 
35 #define DEFAULT_TX_DUMP_FILE "inline-out.pcap"
36 #define DEFAULT_RX_DUMP_FILE "inline-in.pcap"
37 
38 #define SET_ERROR(modinst, ...)    daq_base_api.set_errbuf(modinst, __VA_ARGS__)
39 
40 #define CHECK_SUBAPI(ctxt, fname) \
41     (ctxt->subapi.fname.func != NULL)
42 
43 #define CALL_SUBAPI_NOARGS(ctxt, fname) \
44     ctxt->subapi.fname.func(ctxt->subapi.fname.context)
45 
46 #define CALL_SUBAPI(ctxt, fname, ...) \
47     ctxt->subapi.fname.func(ctxt->subapi.fname.context, __VA_ARGS__)
48 
49 typedef struct
50 {
51     DAQ_ModuleInstance_h modinst;
52     DAQ_InstanceAPI_t subapi;
53 
54     pcap_dumper_t *tx_dumper;
55     char *tx_filename;
56 
57     pcap_dumper_t *rx_dumper;
58     char *rx_filename;
59 
60     DAQ_Stats_t stats;
61 } DumpContext;
62 
63 static DAQ_VariableDesc_t dump_variable_descriptions[] = {
64     { "file", "PCAP filename to output transmitted packets to (default: " DEFAULT_TX_DUMP_FILE ")", DAQ_VAR_DESC_REQUIRES_ARGUMENT },
65     { "output", "Set to none to prevent output from being written to file (deprecated)", DAQ_VAR_DESC_REQUIRES_ARGUMENT },
66     { "dump-rx", "Also dump received packets to their own PCAP file (default: " DEFAULT_RX_DUMP_FILE ")", 0 }
67 };
68 
69 static DAQ_BaseAPI_t daq_base_api;
70 
71 //-------------------------------------------------------------------------
72 
dump_daq_module_load(const DAQ_BaseAPI_t * base_api)73 static int dump_daq_module_load(const DAQ_BaseAPI_t *base_api)
74 {
75     if (base_api->api_version != DAQ_BASE_API_VERSION || base_api->api_size != sizeof(DAQ_BaseAPI_t))
76         return DAQ_ERROR;
77 
78     daq_base_api = *base_api;
79 
80     return DAQ_SUCCESS;
81 }
82 
dump_daq_module_unload(void)83 static int dump_daq_module_unload(void)
84 {
85     memset(&daq_base_api, 0, sizeof(daq_base_api));
86     return DAQ_SUCCESS;
87 }
88 
dump_daq_get_variable_descs(const DAQ_VariableDesc_t ** var_desc_table)89 static int dump_daq_get_variable_descs(const DAQ_VariableDesc_t **var_desc_table)
90 {
91     *var_desc_table = dump_variable_descriptions;
92 
93     return sizeof(dump_variable_descriptions) / sizeof(DAQ_VariableDesc_t);
94 }
95 
dump_daq_instantiate(const DAQ_ModuleConfig_h modcfg,DAQ_ModuleInstance_h modinst,void ** ctxt_ptr)96 static int dump_daq_instantiate(const DAQ_ModuleConfig_h modcfg, DAQ_ModuleInstance_h modinst, void **ctxt_ptr)
97 {
98     // Simple multi-instance sanity check
99     unsigned total_instances = daq_base_api.config_get_total_instances(modcfg);
100     unsigned instance_id = daq_base_api.config_get_instance_id(modcfg);
101     if (total_instances > 1 && instance_id == 0)
102     {
103         SET_ERROR(modinst, "%s: Instance ID required for multi-instance (%u instances expected)", __func__, total_instances);
104         return DAQ_ERROR_INVAL;
105     }
106 
107     DumpContext *dc = calloc(1, sizeof(DumpContext));
108     if (!dc)
109     {
110         SET_ERROR(modinst, "%s: Couldn't allocate memory for the DAQ context", __func__);
111         return DAQ_ERROR_NOMEM;
112     }
113     dc->modinst = modinst;
114 
115     if (daq_base_api.resolve_subapi(modinst, &dc->subapi) != DAQ_SUCCESS)
116     {
117         SET_ERROR(modinst, "%s: Couldn't resolve subapi. No submodule configured?", __func__);
118         free(dc);
119         return DAQ_ERROR_INVAL;
120     }
121 
122     const char *tx_filename = DEFAULT_TX_DUMP_FILE;
123     const char *rx_filename = NULL;
124     const char *varKey, *varValue;
125     daq_base_api.config_first_variable(modcfg, &varKey, &varValue);
126     while (varKey)
127     {
128         if (!strcmp(varKey, "file"))
129             tx_filename = varValue;
130         else if (!strcmp(varKey, "dump-rx"))
131             rx_filename = varValue ? varValue : DEFAULT_RX_DUMP_FILE;
132         else if (!strcmp(varKey, "output"))
133         {
134             if (!strcmp(varValue, "none"))
135                 tx_filename = NULL;
136             else
137             {
138                 SET_ERROR(modinst, "%s: Invalid output type (%s)", __func__, varValue);
139                 free(dc);
140                 return DAQ_ERROR_INVAL;
141             }
142         }
143         daq_base_api.config_next_variable(modcfg, &varKey, &varValue);
144     }
145 
146     // Mangle the output filenames with a prefix in the multi-instance scenario
147     char prefix[32];
148     if (instance_id > 0)
149     {
150         // For now, only support mangling base filenames (no directory path allowed)
151         if (tx_filename && strchr(tx_filename, '/'))
152         {
153             SET_ERROR(modinst, "%s: Invalid TX PCAP filename for multi-instance: %s", __func__, tx_filename);
154             free(dc);
155             return DAQ_ERROR_INVAL;
156         }
157 
158         if (rx_filename && strchr(rx_filename, '/'))
159         {
160             SET_ERROR(modinst, "%s: Invalid RX PCAP filename for multi-instance: %s", __func__, rx_filename);
161             free(dc);
162             return DAQ_ERROR_INVAL;
163         }
164 
165         snprintf(prefix, sizeof(prefix), "%u_", instance_id);
166     }
167     else
168         prefix[0] = '\0';
169 
170     if (tx_filename)
171     {
172         size_t len = strlen(tx_filename) + strlen(prefix) + 1;
173         dc->tx_filename = malloc(len);
174         if (!dc->tx_filename)
175         {
176             SET_ERROR(modinst, "%s: Couldn't allocate memory for the TX PCAP filename", __func__);
177             free(dc);
178             return DAQ_ERROR_NOMEM;
179         }
180         snprintf(dc->tx_filename, len, "%s%s", prefix, tx_filename);
181     }
182 
183     if (rx_filename)
184     {
185         size_t len = strlen(rx_filename) + strlen(prefix) + 1;
186         dc->rx_filename = malloc(len);
187         if (!dc->rx_filename)
188         {
189             SET_ERROR(modinst, "%s: Couldn't allocate memory for the RX PCAP filename", __func__);
190             free(dc->tx_filename);
191             free(dc);
192             return DAQ_ERROR_NOMEM;
193         }
194         snprintf(dc->rx_filename, len, "%s%s", prefix, rx_filename);
195     }
196 
197     *ctxt_ptr = dc;
198 
199     return DAQ_SUCCESS;
200 }
201 
dump_daq_destroy(void * handle)202 static void dump_daq_destroy(void *handle)
203 {
204     DumpContext *dc = (DumpContext *) handle;
205 
206     if (dc->tx_dumper)
207         pcap_dump_close(dc->tx_dumper);
208     free(dc->tx_filename);
209     if (dc->rx_dumper)
210         pcap_dump_close(dc->rx_dumper);
211     free(dc->rx_filename);
212     free(dc);
213 }
214 
dump_daq_start(void * handle)215 static int dump_daq_start(void *handle)
216 {
217     DumpContext *dc = (DumpContext*) handle;
218 
219     int rval = CALL_SUBAPI_NOARGS(dc, start);
220     if (rval != DAQ_SUCCESS)
221         return rval;
222 
223     int dlt = CALL_SUBAPI_NOARGS(dc, get_datalink_type);
224     int snaplen = CALL_SUBAPI_NOARGS(dc, get_snaplen);
225 
226     if (dc->tx_filename)
227     {
228         pcap_t *pcap = pcap_open_dead(dlt, snaplen);
229         if (!pcap)
230         {
231             CALL_SUBAPI_NOARGS(dc, stop);
232             SET_ERROR(dc->modinst, "Could not create a dead PCAP handle!");
233             return DAQ_ERROR;
234         }
235         dc->tx_dumper = pcap_dump_open(pcap, dc->tx_filename);
236         if (!dc->tx_dumper)
237         {
238             CALL_SUBAPI_NOARGS(dc, stop);
239             SET_ERROR(dc->modinst, "Could not open PCAP %s for writing: %s", dc->tx_filename, pcap_geterr(pcap));
240             pcap_close(pcap);
241             return DAQ_ERROR;
242         }
243         pcap_close(pcap);
244     }
245 
246     if (dc->rx_filename)
247     {
248         pcap_t *pcap = pcap_open_dead(dlt, snaplen);
249         if (!pcap)
250         {
251             CALL_SUBAPI_NOARGS(dc, stop);
252             SET_ERROR(dc->modinst, "Could not create a dead PCAP handle!");
253             return DAQ_ERROR;
254         }
255         dc->rx_dumper = pcap_dump_open(pcap, dc->rx_filename);
256         if (!dc->rx_dumper)
257         {
258             CALL_SUBAPI_NOARGS(dc, stop);
259             SET_ERROR(dc->modinst, "Could not open PCAP %s for writing: %s", dc->rx_filename, pcap_geterr(pcap));
260             pcap_close(pcap);
261             return DAQ_ERROR;
262         }
263         pcap_close(pcap);
264     }
265 
266     return DAQ_SUCCESS;
267 }
268 
dump_daq_inject(void * handle,DAQ_MsgType type,const void * hdr,const uint8_t * data,uint32_t data_len)269 static int dump_daq_inject(void *handle, DAQ_MsgType type, const void *hdr, const uint8_t *data, uint32_t data_len)
270 {
271     DumpContext *dc = (DumpContext*) handle;
272 
273     if (dc->tx_dumper && type == DAQ_MSG_TYPE_PACKET)
274     {
275         const DAQ_PktHdr_t *pkthdr = (const DAQ_PktHdr_t *) hdr;
276         struct pcap_pkthdr pcap_hdr;
277 
278         pcap_hdr.ts.tv_sec = pkthdr->ts.tv_sec;
279         pcap_hdr.ts.tv_usec = pkthdr->ts.tv_usec;
280         pcap_hdr.caplen = data_len;
281         pcap_hdr.len = data_len;
282 
283         pcap_dump((u_char *) dc->tx_dumper, &pcap_hdr, data);
284     }
285 
286     if (CHECK_SUBAPI(dc, inject))
287     {
288         int rval = CALL_SUBAPI(dc, inject, type, hdr, data, data_len);
289         if (rval != DAQ_SUCCESS)
290             return rval;
291     }
292 
293     dc->stats.packets_injected++;
294     return DAQ_SUCCESS;
295 }
296 
dump_daq_inject_relative(void * handle,const DAQ_Msg_t * msg,const uint8_t * data,uint32_t data_len,int reverse)297 static int dump_daq_inject_relative(void *handle, const DAQ_Msg_t *msg, const uint8_t *data, uint32_t data_len, int reverse)
298 {
299     DumpContext *dc = (DumpContext*) handle;
300 
301     if (dc->tx_dumper && msg->type == DAQ_MSG_TYPE_PACKET)
302     {
303         const DAQ_PktHdr_t *pkthdr = (const DAQ_PktHdr_t *) msg->hdr;
304         struct pcap_pkthdr pcap_hdr;
305 
306         // Reuse the timestamp from the original packet for the injected packet
307         pcap_hdr.ts.tv_sec = pkthdr->ts.tv_sec;
308         pcap_hdr.ts.tv_usec = pkthdr->ts.tv_usec;
309         pcap_hdr.caplen = data_len;
310         pcap_hdr.len = data_len;
311 
312         pcap_dump((u_char *) dc->tx_dumper, &pcap_hdr, data);
313     }
314 
315     if (CHECK_SUBAPI(dc, inject_relative))
316     {
317         int rval = CALL_SUBAPI(dc, inject_relative, msg, data, data_len, reverse);
318         if (rval != DAQ_SUCCESS)
319             return rval;
320     }
321 
322     dc->stats.packets_injected++;
323     return DAQ_SUCCESS;
324 }
325 
dump_daq_stop(void * handle)326 static int dump_daq_stop(void *handle)
327 {
328     DumpContext *dc = (DumpContext*) handle;
329     int rval = CALL_SUBAPI_NOARGS(dc, stop);
330 
331     if (rval != DAQ_SUCCESS)
332         return rval;
333 
334     if (dc->tx_dumper)
335     {
336         pcap_dump_close(dc->tx_dumper);
337         dc->tx_dumper = NULL;
338     }
339 
340     if (dc->rx_dumper)
341     {
342         pcap_dump_close(dc->rx_dumper);
343         dc->rx_dumper = NULL;
344     }
345 
346     return DAQ_SUCCESS;
347 }
348 
dump_daq_get_stats(void * handle,DAQ_Stats_t * stats)349 static int dump_daq_get_stats(void *handle, DAQ_Stats_t *stats)
350 {
351     DumpContext *dc = (DumpContext*) handle;
352     int rval = DAQ_SUCCESS;
353 
354     if (CHECK_SUBAPI(dc, get_stats))
355     {
356         rval = CALL_SUBAPI(dc, get_stats, stats);
357         /* Use our own concept of verdict and injected packet stats */
358         for (int i = 0; i < MAX_DAQ_VERDICT; i++)
359             stats->verdicts[i] = dc->stats.verdicts[i];
360         stats->packets_injected = dc->stats.packets_injected;
361     }
362     else
363         *stats = dc->stats;
364 
365     return rval;
366 }
367 
dump_daq_reset_stats(void * handle)368 static void dump_daq_reset_stats(void *handle)
369 {
370     DumpContext *dc = (DumpContext*) handle;
371     if (CHECK_SUBAPI(dc, reset_stats))
372         CALL_SUBAPI_NOARGS(dc, reset_stats);
373     memset(&dc->stats, 0, sizeof(dc->stats));
374 }
375 
dump_daq_get_capabilities(void * handle)376 static uint32_t dump_daq_get_capabilities(void *handle)
377 {
378     DumpContext *dc = (DumpContext*) handle;
379     uint32_t caps = CHECK_SUBAPI(dc, get_capabilities) ? CALL_SUBAPI_NOARGS(dc, get_capabilities) : 0;
380     caps |= DAQ_CAPA_BLOCK | DAQ_CAPA_REPLACE | DAQ_CAPA_INJECT;
381     return caps;
382 }
383 
dump_daq_msg_receive(void * handle,const unsigned max_recv,const DAQ_Msg_t * msgs[],DAQ_RecvStatus * rstat)384 static unsigned dump_daq_msg_receive(void *handle, const unsigned max_recv, const DAQ_Msg_t *msgs[], DAQ_RecvStatus *rstat)
385 {
386     DumpContext *dc = (DumpContext*) handle;
387     unsigned num_receive = CALL_SUBAPI(dc, msg_receive, max_recv, msgs, rstat);
388 
389     if (dc->rx_dumper)
390     {
391         for (unsigned idx = 0; idx < num_receive; idx++)
392         {
393             const DAQ_Msg_t *msg = msgs[idx];
394 
395             if (msg->type != DAQ_MSG_TYPE_PACKET)
396                 continue;
397 
398             const DAQ_PktHdr_t *hdr = (const DAQ_PktHdr_t *) msg->hdr;
399             const uint8_t *data = msg->data;
400             struct pcap_pkthdr pcap_hdr;
401 
402             pcap_hdr.ts.tv_sec = hdr->ts.tv_sec;
403             pcap_hdr.ts.tv_usec = hdr->ts.tv_usec;
404             pcap_hdr.caplen = msg->data_len;
405             pcap_hdr.len = hdr->pktlen;
406             pcap_dump((u_char *) dc->rx_dumper, &pcap_hdr, data);
407         }
408     }
409 
410     return num_receive;
411 }
412 
dump_daq_msg_finalize(void * handle,const DAQ_Msg_t * msg,DAQ_Verdict verdict)413 static int dump_daq_msg_finalize(void *handle, const DAQ_Msg_t *msg, DAQ_Verdict verdict)
414 {
415     static const int s_fwd[MAX_DAQ_VERDICT] = { 1, 0, 1, 1, 0, 1 };
416     DumpContext *dc = (DumpContext *) handle;
417 
418     dc->stats.verdicts[verdict]++;
419     if (dc->tx_dumper && msg->type == DAQ_MSG_TYPE_PACKET && s_fwd[verdict])
420     {
421         const DAQ_PktHdr_t *hdr = (const DAQ_PktHdr_t *) msg->hdr;
422         const uint8_t *data = msg->data;
423         struct pcap_pkthdr pcap_hdr;
424 
425         pcap_hdr.ts.tv_sec = hdr->ts.tv_sec;
426         pcap_hdr.ts.tv_usec = hdr->ts.tv_usec;
427         pcap_hdr.caplen = msg->data_len;
428         pcap_hdr.len = hdr->pktlen;
429         pcap_dump((u_char *) dc->tx_dumper, &pcap_hdr, data);
430     }
431 
432     return CALL_SUBAPI(dc, msg_finalize, msg, verdict);
433 }
434 
435 //-------------------------------------------------------------------------
436 
437 #ifdef BUILDING_SO
438 DAQ_SO_PUBLIC DAQ_ModuleAPI_t DAQ_MODULE_DATA =
439 #else
440 DAQ_ModuleAPI_t dump_daq_module_data =
441 #endif
442 {
443     /* .api_version = */ DAQ_MODULE_API_VERSION,
444     /* .api_size = */ sizeof(DAQ_ModuleAPI_t),
445     /* .module_version = */ DAQ_DUMP_VERSION,
446     /* .name = */ "dump",
447     /* .type = */ DAQ_TYPE_WRAPPER | DAQ_TYPE_INLINE_CAPABLE,
448     /* .load = */ dump_daq_module_load,
449     /* .unload = */ dump_daq_module_unload,
450     /* .get_variable_descs = */ dump_daq_get_variable_descs,
451     /* .instantiate = */ dump_daq_instantiate,
452     /* .destroy = */ dump_daq_destroy,
453     /* .set_filter = */ NULL,
454     /* .start = */ dump_daq_start,
455     /* .inject = */ dump_daq_inject,
456     /* .inject_relative = */ dump_daq_inject_relative,
457     /* .interrupt = */ NULL,
458     /* .stop = */ dump_daq_stop,
459     /* .ioctl = */ NULL,
460     /* .get_stats = */ dump_daq_get_stats,
461     /* .reset_stats = */ dump_daq_reset_stats,
462     /* .get_snaplen = */ NULL,
463     /* .get_capabilities = */ dump_daq_get_capabilities,
464     /* .get_datalink_type = */ NULL,
465     /* .config_load = */ NULL,
466     /* .config_swap = */ NULL,
467     /* .config_free = */ NULL,
468     /* .msg_receive = */ dump_daq_msg_receive,
469     /* .msg_finalize = */ dump_daq_msg_finalize,
470     /* .get_msg_pool_info = */ NULL,
471 };
472 
473