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 <arpa/inet.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
32 #include <sys/socket.h>
33 #endif
34 
35 #include "daq_module_api.h"
36 
37 #define DAQ_TRACE_VERSION 1
38 
39 #define DAQ_TRACE_FILENAME "inline-out.txt"
40 
41 #define SET_ERROR(modinst, ...)    daq_base_api.set_errbuf(modinst, __VA_ARGS__)
42 
43 #define CHECK_SUBAPI(ctxt, fname) \
44     (ctxt->subapi.fname.func != NULL)
45 
46 #define CALL_SUBAPI_NOARGS(ctxt, fname) \
47     ctxt->subapi.fname.func(ctxt->subapi.fname.context)
48 
49 #define CALL_SUBAPI(ctxt, fname, ...) \
50     ctxt->subapi.fname.func(ctxt->subapi.fname.context, __VA_ARGS__)
51 
52 typedef struct
53 {
54     DAQ_ModuleInstance_h modinst;
55     DAQ_InstanceAPI_t subapi;
56 
57     FILE *outfile;
58     char *filename;
59 
60     DAQ_Stats_t stats;
61 } TraceContext;
62 
63 static DAQ_VariableDesc_t trace_variable_descriptions[] = {
64     { "file", "Filename to write text traces to (default: " DAQ_TRACE_FILENAME ")", DAQ_VAR_DESC_REQUIRES_ARGUMENT },
65 };
66 
67 static DAQ_BaseAPI_t daq_base_api;
68 
69 //-------------------------------------------------------------------------
70 
hexdump(FILE * fp,const uint8_t * data,unsigned int len,const char * prefix)71 static void hexdump(FILE *fp, const uint8_t *data, unsigned int len, const char *prefix)
72 {
73     unsigned int i;
74     for (i = 0; i < len; i++)
75     {
76         if (i % 16 == 0)
77             fprintf(fp, "\n%s", prefix ? prefix : "");
78         else if (i % 2 == 0)
79             fprintf(fp, " ");
80         fprintf(fp, "%02x", data[i]);
81     }
82     fprintf(fp, "\n");
83 }
84 
trace_daq_module_load(const DAQ_BaseAPI_t * base_api)85 static int trace_daq_module_load(const DAQ_BaseAPI_t *base_api)
86 {
87     if (base_api->api_version != DAQ_BASE_API_VERSION || base_api->api_size != sizeof(DAQ_BaseAPI_t))
88         return DAQ_ERROR;
89 
90     daq_base_api = *base_api;
91 
92     return DAQ_SUCCESS;
93 }
94 
trace_daq_module_unload(void)95 static int trace_daq_module_unload(void)
96 {
97     memset(&daq_base_api, 0, sizeof(daq_base_api));
98     return DAQ_SUCCESS;
99 }
100 
trace_daq_get_variable_descs(const DAQ_VariableDesc_t ** var_desc_table)101 static int trace_daq_get_variable_descs(const DAQ_VariableDesc_t **var_desc_table)
102 {
103     *var_desc_table = trace_variable_descriptions;
104 
105     return sizeof(trace_variable_descriptions) / sizeof(DAQ_VariableDesc_t);
106 }
107 
trace_daq_instantiate(const DAQ_ModuleConfig_h modcfg,DAQ_ModuleInstance_h modinst,void ** ctxt_ptr)108 static int trace_daq_instantiate(const DAQ_ModuleConfig_h modcfg, DAQ_ModuleInstance_h modinst, void **ctxt_ptr)
109 {
110     // Simple multi-instance sanity check
111     unsigned total_instances = daq_base_api.config_get_total_instances(modcfg);
112     unsigned instance_id = daq_base_api.config_get_instance_id(modcfg);
113     if (total_instances > 1 && instance_id == 0)
114     {
115         SET_ERROR(modinst, "%s: Instance ID required for multi-instance (%u instances expected)", __func__, total_instances);
116         return DAQ_ERROR_INVAL;
117     }
118 
119     TraceContext *tc = calloc(1, sizeof(TraceContext));
120     if (!tc)
121     {
122         SET_ERROR(modinst, "%s: Couldn't allocate memory for the DAQ context", __func__);
123         return DAQ_ERROR_NOMEM;
124     }
125     tc->modinst = modinst;
126 
127     if (daq_base_api.resolve_subapi(modinst, &tc->subapi) != DAQ_SUCCESS)
128     {
129         SET_ERROR(modinst, "%s: Couldn't resolve subapi. No submodule configured?", __func__);
130         free(tc);
131         return DAQ_ERROR_INVAL;
132     }
133 
134     const char *filename = DAQ_TRACE_FILENAME;
135     const char *varKey, *varValue;
136     daq_base_api.config_first_variable(modcfg, &varKey, &varValue);
137     while (varKey)
138     {
139         if (!strcmp(varKey, "file"))
140             filename = varValue;
141         daq_base_api.config_next_variable(modcfg, &varKey, &varValue);
142     }
143 
144     // Mangle the output filename with a prefix in the multi-instance scenario
145     char prefix[32];
146     if (instance_id > 0)
147     {
148         // For now, only support mangling base filenames (no directory path allowed)
149         if (strchr(filename, '/'))
150         {
151             SET_ERROR(modinst, "%s: Invalid filename for multi-instance: %s", __func__, filename);
152             free(tc);
153             return DAQ_ERROR_INVAL;
154         }
155 
156         snprintf(prefix, sizeof(prefix), "%u_", instance_id);
157     }
158     else
159         prefix[0] = '\0';
160 
161     size_t len = strlen(filename) + strlen(prefix) + 1;
162     tc->filename = malloc(len);
163     if (!tc->filename)
164     {
165         SET_ERROR(modinst, "%s: Couldn't allocate memory for the text output filename", __func__);
166         free(tc);
167         return DAQ_ERROR_NOMEM;
168     }
169     snprintf(tc->filename, len, "%s%s", prefix, filename);
170 
171     *ctxt_ptr = tc;
172 
173     return DAQ_SUCCESS;
174 }
175 
trace_daq_destroy(void * handle)176 static void trace_daq_destroy(void *handle)
177 {
178     TraceContext *tc = (TraceContext *) handle;
179 
180     if (tc->outfile)
181         fclose(tc->outfile);
182     free(tc->filename);
183     free(tc);
184 }
185 
trace_daq_inject(void * handle,DAQ_MsgType type,const void * hdr,const uint8_t * data,uint32_t data_len)186 static int trace_daq_inject(void *handle, DAQ_MsgType type, const void *hdr, const uint8_t *data, uint32_t data_len)
187 {
188     TraceContext *tc = (TraceContext*) handle;
189 
190     if (type == DAQ_MSG_TYPE_PACKET)
191     {
192         const DAQ_PktHdr_t *pkthdr = (const DAQ_PktHdr_t *) hdr;
193         fprintf(tc->outfile, "I: %lu.%lu(%u)\n", (unsigned long) pkthdr->ts.tv_sec,
194                (unsigned long) pkthdr->ts.tv_usec, data_len);
195         hexdump(tc->outfile, data, data_len, "    ");
196         fprintf(tc->outfile, "\n");
197     }
198 
199     if (CHECK_SUBAPI(tc, inject))
200     {
201         int rval = CALL_SUBAPI(tc, inject, type, hdr, data, data_len);
202         if (rval != DAQ_SUCCESS)
203             return rval;
204     }
205 
206     tc->stats.packets_injected++;
207     return DAQ_SUCCESS;
208 }
209 
trace_daq_inject_relative(void * handle,const DAQ_Msg_t * msg,const uint8_t * data,uint32_t data_len,int reverse)210 static int trace_daq_inject_relative(void *handle, const DAQ_Msg_t *msg, const uint8_t *data, uint32_t data_len, int reverse)
211 {
212     TraceContext *tc = (TraceContext*) handle;
213     const DAQ_PktHdr_t *hdr = (const DAQ_PktHdr_t *) msg->hdr;
214 
215     fprintf(tc->outfile, "%cI: %lu.%lu(%u): %u\n", reverse ? 'R' : 'F',
216             (unsigned long) hdr->ts.tv_sec, (unsigned long) hdr->ts.tv_usec, msg->data_len, data_len);
217     hexdump(tc->outfile, data, data_len, "    ");
218     fprintf(tc->outfile, "\n");
219 
220     if (CHECK_SUBAPI(tc, inject_relative))
221     {
222         int rval = CALL_SUBAPI(tc, inject_relative, msg, data, data_len, reverse);
223         if (rval != DAQ_SUCCESS)
224             return rval;
225     }
226 
227     tc->stats.packets_injected++;
228     return DAQ_SUCCESS;
229 }
230 
trace_daq_start(void * handle)231 static int trace_daq_start(void* handle)
232 {
233     TraceContext *tc = (TraceContext*) handle;
234 
235     int rval = CALL_SUBAPI_NOARGS(tc, start);
236     if (rval != DAQ_SUCCESS)
237         return rval;
238 
239     const char* filename = tc->filename ? tc->filename : DAQ_TRACE_FILENAME;
240     tc->outfile = fopen(filename, "w");
241     if (!tc->outfile)
242     {
243         CALL_SUBAPI_NOARGS(tc, stop);
244         SET_ERROR(tc->modinst, "can't open text output file");
245         return DAQ_ERROR;
246     }
247 
248     return DAQ_SUCCESS;
249 }
250 
trace_daq_stop(void * handle)251 static int trace_daq_stop (void* handle)
252 {
253     TraceContext *tc = (TraceContext*) handle;
254     int rval = CALL_SUBAPI_NOARGS(tc, stop);
255 
256     if (rval != DAQ_SUCCESS)
257         return rval;
258 
259     if (tc->outfile)
260     {
261         fclose(tc->outfile);
262         tc->outfile = NULL;
263     }
264 
265     return DAQ_SUCCESS;
266 }
267 
print_msg(TraceContext * tc,const DAQ_Msg_t * msg)268 static void print_msg(TraceContext* tc, const DAQ_Msg_t *msg)
269 {
270     if (msg->type == DAQ_MSG_TYPE_PACKET)
271     {
272         const DAQ_PktHdr_t *hdr = (const DAQ_PktHdr_t *) msg->hdr;
273         fprintf(tc->outfile, "%lu.%lu(%u)", (unsigned long) hdr->ts.tv_sec,
274                     (unsigned long) hdr->ts.tv_usec, msg->data_len);
275     }
276 }
277 
trace_daq_ioctl(void * handle,DAQ_IoctlCmd cmd,void * arg,size_t arglen)278 static int trace_daq_ioctl(void *handle, DAQ_IoctlCmd cmd, void *arg, size_t arglen)
279 {
280     TraceContext* tc = (TraceContext*) handle;
281 
282     switch (cmd)
283     {
284         case DIOCTL_GET_DEVICE_INDEX:
285         {
286             if (arglen != sizeof(DIOCTL_QueryDeviceIndex))
287                 return DAQ_ERROR_INVAL;
288             DIOCTL_QueryDeviceIndex *qdi = (DIOCTL_QueryDeviceIndex *) arg;
289             if (!qdi->device)
290                 return DAQ_ERROR_INVAL;
291             fprintf(tc->outfile, "IOCTL: QueryDeviceIndex: '%s'\n", qdi->device);
292             break;
293         }
294         case DIOCTL_SET_FLOW_OPAQUE:
295         {
296             if (arglen != sizeof(DIOCTL_SetFlowOpaque))
297                 return DAQ_ERROR_INVAL;
298             DIOCTL_SetFlowOpaque *sfo = (DIOCTL_SetFlowOpaque *) arg;
299             if (!sfo->msg)
300                 return DAQ_ERROR_INVAL;
301             fprintf(tc->outfile, "IOCTL: SetFlowOpaque: ");
302             print_msg(tc, sfo->msg);
303             fprintf(tc->outfile, " Value: %u\n", sfo->value);
304             break;
305         }
306         case DIOCTL_SET_FLOW_HA_STATE:
307         {
308             if (arglen != sizeof(DIOCTL_FlowHAState))
309                 return DAQ_ERROR_INVAL;
310             DIOCTL_FlowHAState *fhs = (DIOCTL_FlowHAState *) arg;
311             if (!fhs->msg || (!fhs->data && fhs->length != 0))
312                 return DAQ_ERROR_INVAL;
313             fprintf(tc->outfile, "IOCTL: SetFlowHAState: ");
314             print_msg(tc, fhs->msg);
315             fprintf(tc->outfile, " (%u)\n", fhs->length);
316             hexdump(tc->outfile, fhs->data, fhs->length, "    ");
317             break;
318         }
319         case DIOCTL_GET_FLOW_HA_STATE:
320         {
321             if (arglen != sizeof(DIOCTL_FlowHAState))
322                 return DAQ_ERROR_INVAL;
323             DIOCTL_FlowHAState *fhs = (DIOCTL_FlowHAState *) arg;
324             if (!fhs->msg)
325                 return DAQ_ERROR_INVAL;
326             fprintf(tc->outfile, "IOCTL: GetFlowHAState: ");
327             print_msg(tc, fhs->msg);
328             fprintf(tc->outfile, "\n");
329             break;
330         }
331         case DIOCTL_SET_FLOW_QOS_ID:
332         {
333             if (arglen != sizeof(DIOCTL_SetFlowQosID))
334                 return DAQ_ERROR_INVAL;
335             DIOCTL_SetFlowQosID *sfq = (DIOCTL_SetFlowQosID *) arg;
336             if (!sfq->msg)
337                 return DAQ_ERROR_INVAL;
338             fprintf(tc->outfile, "IOCTL: SetFlowQosID: ");
339             print_msg(tc, sfq->msg);
340             fprintf(tc->outfile, "%u/0x%x\n", (unsigned) (sfq->qos_id & 0xFFFFFFFF),
341                     (unsigned) (sfq->qos_id >> 32));
342             break;
343         }
344         case DIOCTL_SET_PACKET_TRACE_DATA:
345         {
346             if (arglen != sizeof(DIOCTL_SetPacketTraceData))
347                 return DAQ_ERROR_INVAL;
348             DIOCTL_SetPacketTraceData *sptd = (DIOCTL_SetPacketTraceData *) arg;
349             if (!sptd->msg || (!sptd->trace_data && sptd->trace_data_len != 0))
350                 return DAQ_ERROR_INVAL;
351             fprintf(tc->outfile, "IOCTL: SetPacketTraceData: ");
352             print_msg(tc, sptd->msg);
353             fprintf(tc->outfile, " VR: %hhu (%u):\n", sptd->verdict_reason, sptd->trace_data_len);
354             fprintf(tc->outfile, "    %.*s\n", sptd->trace_data_len, sptd->trace_data);
355             break;
356         }
357         case DIOCTL_SET_PACKET_VERDICT_REASON:
358         {
359             if (arglen != sizeof(DIOCTL_SetPacketVerdictReason))
360                 return DAQ_ERROR_INVAL;
361             DIOCTL_SetPacketVerdictReason *spvr = (DIOCTL_SetPacketVerdictReason *) arg;
362             if (!spvr->msg)
363                 return DAQ_ERROR_INVAL;
364             fprintf(tc->outfile, "IOCTL: SetPacketVerdictReason: ");
365             print_msg(tc, spvr->msg);
366             fprintf(tc->outfile, " VR: %hhu\n", spvr->verdict_reason);
367             break;
368         }
369         case DIOCTL_SET_FLOW_PRESERVE:
370         {
371             if (arglen != sizeof(DAQ_Msg_h))
372                 return DAQ_ERROR_INVAL;
373             DAQ_Msg_h msg = (DAQ_Msg_h) arg;
374             if (!msg)
375                 return DAQ_ERROR_INVAL;
376             fprintf(tc->outfile, "IOCTL: SetFlowPreserve: ");
377             print_msg(tc, msg);
378             fprintf(tc->outfile, "\n");
379             break;
380         }
381         case DIOCTL_GET_FLOW_TCP_SCRUBBED_SYN:
382         case DIOCTL_GET_FLOW_TCP_SCRUBBED_SYN_ACK:
383         {
384             if (arglen != sizeof(DIOCTL_GetFlowScrubbedTcp))
385                 return DAQ_ERROR_INVAL;
386             DIOCTL_GetFlowScrubbedTcp *gpst = (DIOCTL_GetFlowScrubbedTcp *) arg;
387             if (!gpst->msg)
388                 return DAQ_ERROR_INVAL;
389             fprintf(tc->outfile, "IOCTL: %s: ", (cmd == DIOCTL_GET_FLOW_TCP_SCRUBBED_SYN) ?
390                     "GetFlowTcpScrubbedSyn" : "GetFlowTcpScrubbedSynAck");
391             print_msg(tc, gpst->msg);
392             fprintf(tc->outfile, "\n");
393             break;
394         }
395         case DIOCTL_CREATE_EXPECTED_FLOW:
396         {
397             if (arglen != sizeof(DIOCTL_CreateExpectedFlow))
398                 return DAQ_ERROR_INVAL;
399             DIOCTL_CreateExpectedFlow *cef = (DIOCTL_CreateExpectedFlow *) arg;
400             if (!cef->ctrl_msg || cef->ctrl_msg->type != DAQ_MSG_TYPE_PACKET)
401                 return DAQ_ERROR_INVAL;
402             fprintf(tc->outfile, "IOCTL: CreateExpectedFlow: ");
403             print_msg(tc, cef->ctrl_msg);
404             fprintf(tc->outfile, ":\n");
405 
406             DAQ_EFlow_Key_t *key = (DAQ_EFlow_Key_t *) &cef->key;
407             char src_addr_str[INET6_ADDRSTRLEN], dst_addr_str[INET6_ADDRSTRLEN];
408             if (key->src_af == AF_INET)
409                 inet_ntop(AF_INET, &key->sa.src_ip4, src_addr_str, sizeof(src_addr_str));
410             else
411                 inet_ntop(AF_INET6, &key->sa.src_ip6, src_addr_str, sizeof(src_addr_str));
412             if (key->dst_af == AF_INET)
413                 inet_ntop(AF_INET, &key->da.dst_ip4, dst_addr_str, sizeof(dst_addr_str));
414             else
415                 inet_ntop(AF_INET6, &key->da.dst_ip6, dst_addr_str, sizeof(dst_addr_str));
416             fprintf(tc->outfile, "    %s:%hu -> %s:%hu (%hhu)\n", src_addr_str, key->src_port,
417                     dst_addr_str, key->dst_port, key->protocol);
418             fprintf(tc->outfile, "    %hu %hu %hu %hu 0x%X %u\n", key->address_space_id, key->tunnel_type,
419                     key->vlan_id, key->vlan_cnots, cef->flags, cef->timeout_ms);
420             break;
421         }
422         case DIOCTL_DIRECT_INJECT_PAYLOAD:
423         {
424             if (arglen != sizeof(DIOCTL_DirectInjectPayload))
425                 return DAQ_ERROR_INVAL;
426             DIOCTL_DirectInjectPayload *dip = (DIOCTL_DirectInjectPayload *) arg;
427             fprintf(tc->outfile, "IOCTL: DirectInjectPayload: ");
428             print_msg(tc, dip->msg);
429             fprintf(tc->outfile, " (%hhu segments)%s\n", dip->num_segments, dip->reverse ? " (reverse)" : "");
430             for (int i = 0; i < dip->num_segments; i++)
431             {
432                 const DAQ_DIPayloadSegment *segment = dip->segments[i];
433                 fprintf(tc->outfile, "  Segment %d (%u)\n", i, segment->length);
434                 hexdump(tc->outfile, segment->data, segment->length, "    ");
435             }
436             break;
437         }
438         case DIOCTL_DIRECT_INJECT_RESET:
439         {
440             if (arglen != sizeof(DIOCTL_DirectInjectReset))
441                 return DAQ_ERROR_INVAL;
442             DIOCTL_DirectInjectReset *dir = (DIOCTL_DirectInjectReset *) arg;
443             fprintf(tc->outfile, "IOCTL: DirectInjectReset: ");
444             print_msg(tc, dir->msg);
445             if (dir->direction == DAQ_DIR_BOTH)
446                 fprintf(tc->outfile, " (both)");
447             else if (dir->direction == DAQ_DIR_REVERSE)
448                 fprintf(tc->outfile, " (reverse)");
449             fprintf(tc->outfile, "\n");
450             break;
451         }
452 
453         default:
454             fprintf(tc->outfile, "IOCTL: %d (%zu)\n", cmd, arglen);
455             hexdump(tc->outfile, arg, arglen, "    ");
456             break;
457     }
458 
459     if (CHECK_SUBAPI(tc, ioctl))
460         return CALL_SUBAPI(tc, ioctl, cmd, arg, arglen);
461 
462     return DAQ_SUCCESS;
463 }
464 
trace_daq_get_stats(void * handle,DAQ_Stats_t * stats)465 static int trace_daq_get_stats(void* handle, DAQ_Stats_t* stats)
466 {
467     TraceContext *tc = (TraceContext*) handle;
468     int rval = DAQ_SUCCESS;
469 
470     if (CHECK_SUBAPI(tc, get_stats))
471     {
472         rval = CALL_SUBAPI(tc, get_stats, stats);
473         /* Use our own concept of verdict and injected packet stats */
474         for (int i = 0; i < MAX_DAQ_VERDICT; i++)
475             stats->verdicts[i] = tc->stats.verdicts[i];
476         stats->packets_injected = tc->stats.packets_injected;
477     }
478     else
479         *stats = tc->stats;
480 
481     return rval;
482 }
483 
trace_daq_reset_stats(void * handle)484 static void trace_daq_reset_stats(void* handle)
485 {
486     TraceContext *tc = (TraceContext*) handle;
487     if (CHECK_SUBAPI(tc, reset_stats))
488         CALL_SUBAPI_NOARGS(tc, reset_stats);
489     memset(&tc->stats, 0, sizeof(tc->stats));
490 }
491 
trace_daq_get_capabilities(void * handle)492 static uint32_t trace_daq_get_capabilities(void* handle)
493 {
494     TraceContext *tc = (TraceContext*) handle;
495     uint32_t caps = CHECK_SUBAPI(tc, get_capabilities) ? CALL_SUBAPI_NOARGS(tc, get_capabilities) : 0;
496     caps |= DAQ_CAPA_BLOCK | DAQ_CAPA_REPLACE | DAQ_CAPA_INJECT;
497     return caps;
498 }
499 
500 // We don't have access to daq_verdict_string() because we're not linking
501 // against LibDAQ, so pack our own copy.
502 static const char *daq_verdict_strings[MAX_DAQ_VERDICT] = {
503     "Pass",         // DAQ_VERDICT_PASS
504     "Block",        // DAQ_VERDICT_BLOCK
505     "Replace",      // DAQ_VERDICT_REPLACE
506     "Whitelist",    // DAQ_VERDICT_WHITELIST
507     "Blacklist",    // DAQ_VERDICT_BLACKLIST
508     "Ignore"        // DAQ_VERDICT_IGNORE
509 };
510 
trace_daq_msg_finalize(void * handle,const DAQ_Msg_t * msg,DAQ_Verdict verdict)511 static int trace_daq_msg_finalize(void *handle, const DAQ_Msg_t *msg, DAQ_Verdict verdict)
512 {
513     TraceContext *tc = (TraceContext *) handle;
514 
515     tc->stats.verdicts[verdict]++;
516     if (msg->type == DAQ_MSG_TYPE_PACKET)
517     {
518         DAQ_PktHdr_t *hdr = (DAQ_PktHdr_t *) msg->hdr;
519         const uint8_t *data = msg->data;
520 
521         fprintf(tc->outfile, "PV: %lu.%lu(%u): %s\n", (unsigned long) hdr->ts.tv_sec,
522                 (unsigned long) hdr->ts.tv_usec, msg->data_len, daq_verdict_strings[verdict]);
523         if (verdict == DAQ_VERDICT_REPLACE)
524             hexdump(tc->outfile, data, msg->data_len, "    ");
525     }
526 
527     return CALL_SUBAPI(tc, msg_finalize, msg, verdict);
528 }
529 
530 //-------------------------------------------------------------------------
531 
532 #ifdef BUILDING_SO
533 DAQ_SO_PUBLIC DAQ_ModuleAPI_t DAQ_MODULE_DATA =
534 #else
535 DAQ_ModuleAPI_t trace_daq_module_data =
536 #endif
537 {
538     /* .api_version = */ DAQ_MODULE_API_VERSION,
539     /* .api_size = */ sizeof(DAQ_ModuleAPI_t),
540     /* .module_version = */ DAQ_TRACE_VERSION,
541     /* .name = */ "trace",
542     /* .type = */ DAQ_TYPE_WRAPPER | DAQ_TYPE_INLINE_CAPABLE,
543     /* .load = */ trace_daq_module_load,
544     /* .unload = */ trace_daq_module_unload,
545     /* .get_variable_descs = */ trace_daq_get_variable_descs,
546     /* .instantiate = */ trace_daq_instantiate,
547     /* .destroy = */ trace_daq_destroy,
548     /* .set_filter = */ NULL,
549     /* .start = */ trace_daq_start,
550     /* .inject = */ trace_daq_inject,
551     /* .inject_relative = */ trace_daq_inject_relative,
552     /* .interrupt = */ NULL,
553     /* .stop = */ trace_daq_stop,
554     /* .ioctl = */ trace_daq_ioctl,
555     /* .get_stats = */ trace_daq_get_stats,
556     /* .reset_stats = */ trace_daq_reset_stats,
557     /* .get_snaplen = */ NULL,
558     /* .get_capabilities = */ trace_daq_get_capabilities,
559     /* .get_datalink_type = */ NULL,
560     /* .config_load = */ NULL,
561     /* .config_swap = */ NULL,
562     /* .config_free = */ NULL,
563     /* .msg_receive = */ NULL,
564     /* .msg_finalize = */ trace_daq_msg_finalize,
565     /* .get_msg_pool_info = */ NULL,
566 };
567 
568