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