res_hep/res_hep_pjsip: Add a HEPv3 capture agent module and a logger for PJSIP
[asterisk/asterisk.git] / res / res_hep.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2014, Digium, Inc.
5  *
6  * Alexandr Dubovikov <alexandr.dubovikov@sipcapture.org>
7  * Matt Jordan <mjordan@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*!
21  * \file
22  * \brief Routines for integration with Homer using HEPv3
23  *
24  * \author Alexandr Dubovikov <alexandr.dubovikov@sipcapture.org>
25  * \author Matt Jordan <mjordan@digium.com>
26  *
27  */
28
29 /*!
30  * \li \ref res_hep.c uses the configuration file \ref hep.conf
31  * \addtogroup configuration_file Configuration Files
32  */
33
34 /*!
35  * \page hep.conf hep.conf
36  * \verbinclude hep.conf.sample
37  */
38
39 /*** MODULEINFO
40         <defaultenabled>no</defaultenabled>
41         <support_level>extended</support_level>
42  ***/
43
44 /*** DOCUMENTATION
45         <configInfo name="res_hep" language="en_US">
46                 <synopsis>Resource for integration with Homer using HEPv3</synopsis>
47                 <configFile name="hep.conf">
48                         <configObject name="general">
49                                 <synopsis>General settings.</synopsis>
50                                 <description><para>
51                                         The <emphasis>general</emphasis> settings section contains information
52                                         to configure Asterisk as a Homer capture agent.
53                                         </para>
54                                 </description>
55                                 <configOption name="enabled" default="yes">
56                                         <synopsis>Enable or disable packet capturing.</synopsis>
57                                         <description>
58                                                 <enumlist>
59                                                         <enum name="no" />
60                                                         <enum name="yes" />
61                                                 </enumlist>
62                                         </description>
63                                 </configOption>
64                                 <configOption name="capture_address" default="192.168.1.1:9061">
65                                         <synopsis>The address and port of the Homer server to send packets to.</synopsis>
66                                 </configOption>
67                                 <configOption name="capture_password">
68                                         <synopsis>If set, the authentication password to send to Homer.</synopsis>
69                                 </configOption>
70                                 <configOption name="capture_id" default="0">
71                                         <synopsis>The ID for this capture agent.</synopsis>
72                                 </configOption>
73                         </configObject>
74                 </configFile>
75         </configInfo>
76 ***/
77
78 #include "asterisk.h"
79
80 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
81
82 #include "asterisk/module.h"
83 #include "asterisk/astobj2.h"
84 #include "asterisk/config_options.h"
85 #include "asterisk/taskprocessor.h"
86 #include "asterisk/res_hep.h"
87
88 #include <netinet/ip.h>
89 #include <netinet/tcp.h>
90 #include <netinet/udp.h>
91 #include <netinet/ip6.h>
92
93 #define DEFAULT_HEP_SERVER ""
94
95 /*! Generic vendor ID. Used for HEPv3 standard packets */
96 #define GENERIC_VENDOR_ID 0x0000
97
98 /*! Asterisk vendor ID. Used for custom data to send to a capture node */
99 #define ASTERISK_VENDOR_ID 0x0004
100
101 /*! Chunk types from the HEPv3 Spec */
102 enum hepv3_chunk_types {
103
104         /*! THE IP PROTOCOL FAMILY */
105         CHUNK_TYPE_IP_PROTOCOL_FAMILY = 0X0001,
106
107         /*! THE IP PROTOCOL ID (UDP, TCP, ETC.) */
108         CHUNK_TYPE_IP_PROTOCOL_ID = 0X0002,
109
110         /*! IF IPV4, THE SOURCE ADDRESS */
111         CHUNK_TYPE_IPV4_SRC_ADDR = 0X0003,
112
113         /*! IF IPV4, THE DESTINATION ADDRESS */
114         CHUNK_TYPE_IPV4_DST_ADDR = 0X0004,
115
116         /*! IF IPV6, THE SOURCE ADDRESS */
117         CHUNK_TYPE_IPV6_SRC_ADDR = 0X0005,
118
119         /*! IF IPV6, THE DESTINATION ADDRESS */
120         CHUNK_TYPE_IPV6_DST_ADDR = 0X0006,
121
122         /*! THE SOURCE PORT */
123         CHUNK_TYPE_SRC_PORT = 0X0007,
124
125         /*! THE DESTINATION PORT */
126         CHUNK_TYPE_DST_PORT = 0X0008,
127
128         /*! THE CAPTURE TIME (SECONDS) */
129         CHUNK_TYPE_TIMESTAMP_SEC = 0X0009,
130
131         /*! THE CAPTURE TIME (MICROSECONDS) */
132         CHUNK_TYPE_TIMESTAMP_USEC = 0X000A,
133
134         /*! THE PROTOCOL PACKET TYPE. SEE /REF HEPV3_CAPTURE_TYPE */
135         CHUNK_TYPE_PROTOCOL_TYPE = 0X000B,
136
137         /*! OUR CAPTURE AGENT ID */
138         CHUNK_TYPE_CAPTURE_AGENT_ID = 0X000C,
139
140         /*! A KEEP ALIVE TIMER */
141         CHUNK_TYPE_KEEP_ALIVE_TIMER = 0X000D,
142
143         /*! THE \REF CAPTURE_PASSWORD IF DEFINED */
144         CHUNK_TYPE_AUTH_KEY = 0X000E,
145
146         /*! THE ONE AND ONLY PAYLOAD */
147         CHUNK_TYPE_PAYLOAD = 0X000F,
148
149         /*! THE ONE AND ONLY (ZIPPED) PAYLOAD */
150         CHUNK_TYPE_PAYLOAD_ZIP = 0X0010,
151
152         /*! THE UUID FOR THIS PACKET */
153         CHUNK_TYPE_UUID = 0X0011,
154 };
155
156 #define INITIALIZE_GENERIC_HEP_IDS(hep_chunk, type) do { \
157         (hep_chunk)->vendor_id = htons(GENERIC_VENDOR_ID); \
158         (hep_chunk)->type_id = htons((type)); \
159         } while (0)
160
161 #define INITIALIZE_GENERIC_HEP_IDS_VAR(hep_chunk, type, len) do { \
162         INITIALIZE_GENERIC_HEP_IDS((hep_chunk), (type)); \
163         (hep_chunk)->length = htons(sizeof(*(hep_chunk)) + len); \
164         } while (0)
165
166 #define INITIALIZE_GENERIC_HEP_CHUNK(hep_item, type) do { \
167         INITIALIZE_GENERIC_HEP_IDS(&(hep_item)->chunk, (type)); \
168         (hep_item)->chunk.length = htons(sizeof(*(hep_item))); \
169         } while (0)
170
171 #define INITIALIZE_GENERIC_HEP_CHUNK_DATA(hep_item, type, value) do { \
172         INITIALIZE_GENERIC_HEP_CHUNK((hep_item), (type)); \
173         (hep_item)->data = (value); \
174         } while (0)
175
176 /*
177  * HEPv3 Types.
178  * Note that the content in these is stored in network byte order.
179  */
180
181 struct hep_chunk {
182         u_int16_t vendor_id;
183         u_int16_t type_id;
184         u_int16_t length;
185 } __attribute__((packed));
186
187 struct hep_chunk_uint8 {
188         struct hep_chunk chunk;
189         u_int8_t data;
190 } __attribute__((packed));
191
192 struct hep_chunk_uint16 {
193         struct hep_chunk chunk;
194         u_int16_t data;
195 } __attribute__((packed));
196
197 struct hep_chunk_uint32 {
198         struct hep_chunk chunk;
199         u_int32_t data;
200 } __attribute__((packed));
201
202 struct hep_chunk_ip4 {
203         struct hep_chunk chunk;
204         struct in_addr data;
205 } __attribute__((packed));
206
207 struct hep_chunk_ip6 {
208         struct hep_chunk chunk;
209         struct in6_addr data;
210 } __attribute__((packed));
211
212 struct hep_ctrl {
213         char id[4];
214         u_int16_t length;
215 } __attribute__((packed));
216
217 /* HEP structures */
218
219 struct hep_generic {
220         struct hep_ctrl         header;
221         struct hep_chunk_uint8  ip_family;
222         struct hep_chunk_uint8  ip_proto;
223         struct hep_chunk_uint16 src_port;
224         struct hep_chunk_uint16 dst_port;
225         struct hep_chunk_uint32 time_sec;
226         struct hep_chunk_uint32 time_usec;
227         struct hep_chunk_uint8  proto_t;
228         struct hep_chunk_uint32 capt_id;
229 } __attribute__((packed));
230
231 /*! \brief Global configuration for the module */
232 struct hepv3_global_config {
233         unsigned int enabled;                    /*!< Whether or not sending is enabled */
234         unsigned int capture_id;                 /*!< Capture ID for this agent */
235         AST_DECLARE_STRING_FIELDS(
236                 AST_STRING_FIELD(capture_address);   /*!< Address to send to */
237                 AST_STRING_FIELD(capture_password);  /*!< Password for Homer server */
238         );
239 };
240
241 /*! \brief The actual module config */
242 struct module_config {
243         struct hepv3_global_config *general; /*!< The general config settings */
244 };
245
246 /*! \brief Run-time data derived from \ref hepv3_global_config */
247 struct hepv3_runtime_data {
248         struct ast_sockaddr remote_addr;  /*!< The address to send to */
249         int sockfd;                       /*!< The socket file descriptor */
250 };
251
252 static struct aco_type global_option = {
253         .type = ACO_GLOBAL,
254         .name = "general",
255         .item_offset = offsetof(struct module_config, general),
256         .category_match = ACO_WHITELIST,
257         .category = "^general$",
258 };
259
260 struct aco_type *global_options[] = ACO_TYPES(&global_option);
261
262 struct aco_file hepv3_conf = {
263         .filename = "hep.conf",
264         .types = ACO_TYPES(&global_option),
265 };
266
267 /*! \brief The module configuration container */
268 static AO2_GLOBAL_OBJ_STATIC(global_config);
269
270 /*! \brief Current module data */
271 static AO2_GLOBAL_OBJ_STATIC(global_data);
272
273 static struct ast_taskprocessor *hep_queue_tp;
274
275 static void *module_config_alloc(void);
276 static void hepv3_config_post_apply(void);
277
278 /*! \brief Register information about the configs being processed by this module */
279 CONFIG_INFO_STANDARD(cfg_info, global_config, module_config_alloc,
280         .files = ACO_FILES(&hepv3_conf),
281         .post_apply_config = hepv3_config_post_apply,
282 );
283
284 static void hepv3_config_dtor(void *obj)
285 {
286         struct hepv3_global_config *config = obj;
287
288         ast_string_field_free_memory(config);
289 }
290
291 /*! \brief HEPv3 configuration object allocation */
292 static void *hepv3_config_alloc(void)
293 {
294         struct hepv3_global_config *config;
295
296         config = ao2_alloc(sizeof(*config), hepv3_config_dtor);
297         if (!config || ast_string_field_init(config, 32)) {
298                 return NULL;
299         }
300
301         return config;
302 }
303
304 /*! \brief Configuration object destructor */
305 static void module_config_dtor(void *obj)
306 {
307         struct module_config *config = obj;
308
309         if (config->general) {
310                 ao2_ref(config->general, -1);
311         }
312 }
313
314 /*! \brief Module config constructor */
315 static void *module_config_alloc(void)
316 {
317         struct module_config *config;
318
319         config = ao2_alloc(sizeof(*config), module_config_dtor);
320         if (!config) {
321                 return NULL;
322         }
323
324         config->general = hepv3_config_alloc();
325         if (!config->general) {
326                 ao2_ref(config, -1);
327                 config = NULL;
328         }
329
330         return config;
331 }
332
333 /*! \brief HEPv3 run-time data destructor */
334 static void hepv3_data_dtor(void *obj)
335 {
336         struct hepv3_runtime_data *data = obj;
337
338         if (data->sockfd > -1) {
339                 close(data->sockfd);
340                 data->sockfd = -1;
341         }
342 }
343
344 /*! \brief Allocate the HEPv3 run-time data */
345 static struct hepv3_runtime_data *hepv3_data_alloc(struct hepv3_global_config *config)
346 {
347         struct hepv3_runtime_data *data;
348
349         data = ao2_alloc(sizeof(*data), hepv3_data_dtor);
350         if (!data) {
351                 return NULL;
352         }
353
354         if (!ast_sockaddr_parse(&data->remote_addr, config->capture_address, PARSE_PORT_REQUIRE)) {
355                 ast_log(AST_LOG_WARNING, "Failed to create address from %s\n", config->capture_address);
356                 ao2_ref(data, -1);
357                 return NULL;
358         }
359
360         data->sockfd = socket(ast_sockaddr_is_ipv6(&data->remote_addr) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
361         if (data->sockfd < 0) {
362                 ast_log(AST_LOG_WARNING, "Failed to create socket for address %s: %s\n",
363                                 config->capture_address, strerror(errno));
364                 ao2_ref(data, -1);
365                 return NULL;
366         }
367
368         return data;
369 }
370
371 /*! \brief Destructor for a \ref hepv3_capture_info object */
372 static void capture_info_dtor(void *obj)
373 {
374         struct hepv3_capture_info *info = obj;
375
376         ast_free(info->uuid);
377         ast_free(info->payload);
378 }
379
380 struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t len)
381 {
382         struct hepv3_capture_info *info;
383
384         info = ao2_alloc(sizeof(*info), capture_info_dtor);
385         if (!info) {
386                 return NULL;
387         }
388
389         info->payload = ast_malloc(len);
390         if (!info->payload) {
391                 ao2_ref(info, -1);
392                 return NULL;
393         }
394         memcpy(info->payload, payload, len);
395         info->len = len;
396
397         return info;
398 }
399
400 /*! \brief Callback function for the \ref hep_queue_tp taskprocessor */
401 static int hep_queue_cb(void *data)
402 {
403         RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
404         RAII_VAR(struct hepv3_runtime_data *, hepv3_data, ao2_global_obj_ref(global_data), ao2_cleanup);
405         RAII_VAR(struct hepv3_capture_info *, capture_info, data, ao2_cleanup);
406         struct hep_generic hg_pkt;
407         unsigned int packet_len = 0, sock_buffer_len;
408         struct hep_chunk_ip4 ipv4_src, ipv4_dst;
409         struct hep_chunk_ip6 ipv6_src, ipv6_dst;
410         struct hep_chunk auth_key, payload, uuid;
411         void *sock_buffer;
412         int res;
413
414         if (!capture_info || !config || !hepv3_data) {
415                 return 0;
416         }
417
418         if (ast_sockaddr_is_ipv4(&capture_info->src_addr) != ast_sockaddr_is_ipv4(&capture_info->dst_addr)) {
419                 ast_log(AST_LOG_NOTICE, "Unable to send packet: Address Family mismatch between source/destination\n");
420                 return -1;
421         }
422
423         packet_len = sizeof(hg_pkt);
424
425         /* Build HEPv3 header, capture info, and calculate the total packet size */
426         memcpy(hg_pkt.header.id, "\x48\x45\x50\x33", 4);
427
428         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_proto, CHUNK_TYPE_IP_PROTOCOL_ID, 0x11);
429         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.src_port, CHUNK_TYPE_SRC_PORT, htons(ast_sockaddr_port(&capture_info->src_addr)));
430         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.dst_port, CHUNK_TYPE_DST_PORT, htons(ast_sockaddr_port(&capture_info->dst_addr)));
431         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.time_sec, CHUNK_TYPE_TIMESTAMP_SEC, htonl(capture_info->capture_time.tv_sec));
432         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.time_usec, CHUNK_TYPE_TIMESTAMP_USEC, htonl(capture_info->capture_time.tv_usec));
433         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.proto_t, CHUNK_TYPE_PROTOCOL_TYPE, capture_info->capture_type);
434         INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.capt_id, CHUNK_TYPE_CAPTURE_AGENT_ID, htonl(config->general->capture_id));
435
436         if (ast_sockaddr_is_ipv4(&capture_info->src_addr)) {
437                 INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_family,
438                         CHUNK_TYPE_IP_PROTOCOL_FAMILY, AF_INET);
439
440                 INITIALIZE_GENERIC_HEP_CHUNK(&ipv4_src, CHUNK_TYPE_IPV4_SRC_ADDR);
441                 inet_pton(AF_INET, ast_sockaddr_stringify_addr(&capture_info->src_addr), &ipv4_src.data);
442
443                 INITIALIZE_GENERIC_HEP_CHUNK(&ipv4_dst, CHUNK_TYPE_IPV4_DST_ADDR);
444                 inet_pton(AF_INET, ast_sockaddr_stringify_addr(&capture_info->dst_addr), &ipv4_dst.data);
445
446                 packet_len += (sizeof(ipv4_src) + sizeof(ipv4_dst));
447         } else {
448                 INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_family,
449                         CHUNK_TYPE_IP_PROTOCOL_FAMILY, AF_INET6);
450
451                 INITIALIZE_GENERIC_HEP_CHUNK(&ipv6_src, CHUNK_TYPE_IPV6_SRC_ADDR);
452                 inet_pton(AF_INET6, ast_sockaddr_stringify_addr(&capture_info->src_addr), &ipv6_src.data);
453
454                 INITIALIZE_GENERIC_HEP_CHUNK(&ipv6_dst, CHUNK_TYPE_IPV6_DST_ADDR);
455                 inet_pton(AF_INET6, ast_sockaddr_stringify_addr(&capture_info->dst_addr), &ipv6_dst.data);
456
457                 packet_len += (sizeof(ipv6_src) + sizeof(ipv6_dst));
458         }
459
460         if (!ast_strlen_zero(config->general->capture_password))  {
461                 INITIALIZE_GENERIC_HEP_IDS_VAR(&auth_key, CHUNK_TYPE_AUTH_KEY, strlen(config->general->capture_password));
462                 packet_len += (sizeof(auth_key) + strlen(config->general->capture_password));
463         }
464         INITIALIZE_GENERIC_HEP_IDS_VAR(&uuid, CHUNK_TYPE_UUID, strlen(capture_info->uuid));
465         packet_len += (sizeof(uuid) + strlen(capture_info->uuid));
466         INITIALIZE_GENERIC_HEP_IDS_VAR(&payload,
467                 capture_info->zipped ? CHUNK_TYPE_PAYLOAD_ZIP : CHUNK_TYPE_PAYLOAD, capture_info->len);
468         packet_len += (sizeof(payload) + capture_info->len);
469         hg_pkt.header.length = htons(packet_len);
470
471         /* Build the buffer to send */
472         sock_buffer = ast_malloc(packet_len);
473         if (!sock_buffer) {
474                 return -1;
475         }
476
477         /* Copy in the header */
478         memcpy(sock_buffer, &hg_pkt, sizeof(hg_pkt));
479         sock_buffer_len = sizeof(hg_pkt);
480
481         /* Addresses */
482         if (ast_sockaddr_is_ipv4(&capture_info->src_addr)) {
483                 memcpy(sock_buffer + sock_buffer_len, &ipv4_src, sizeof(ipv4_src));
484                 sock_buffer_len += sizeof(ipv4_src);
485                 memcpy(sock_buffer + sock_buffer_len, &ipv4_dst, sizeof(ipv4_dst));
486                 sock_buffer_len += sizeof(ipv4_dst);
487         } else {
488                 memcpy(sock_buffer + sock_buffer_len, &ipv6_src, sizeof(ipv6_src));
489                 sock_buffer_len += sizeof(ipv6_src);
490                 memcpy(sock_buffer + sock_buffer_len, &ipv6_dst, sizeof(ipv6_dst));
491                 sock_buffer_len += sizeof(ipv6_dst);
492         }
493
494         /* Auth Key */
495         if (!ast_strlen_zero(config->general->capture_password)) {
496                 memcpy(sock_buffer + sock_buffer_len, &auth_key, sizeof(auth_key));
497                 sock_buffer_len += sizeof(auth_key);
498                 memcpy(sock_buffer + sock_buffer_len, config->general->capture_password, strlen(config->general->capture_password));
499                 sock_buffer_len += strlen(config->general->capture_password);
500         }
501
502         /* UUID */
503         memcpy(sock_buffer + sock_buffer_len, &uuid, sizeof(uuid));
504         sock_buffer_len += sizeof(uuid);
505         memcpy(sock_buffer + sock_buffer_len, capture_info->uuid, strlen(capture_info->uuid));
506         sock_buffer_len += strlen(capture_info->uuid);
507
508         /* Packet! */
509         memcpy(sock_buffer + sock_buffer_len, &payload, sizeof(payload));
510         sock_buffer_len += sizeof(payload);
511         memcpy(sock_buffer + sock_buffer_len, capture_info->payload, capture_info->len);
512         sock_buffer_len += capture_info->len;
513
514         ast_assert(sock_buffer_len == packet_len);
515
516         res = ast_sendto(hepv3_data->sockfd, sock_buffer, sock_buffer_len, 0, &hepv3_data->remote_addr);
517         if (res < 0) {
518                 ast_log(AST_LOG_ERROR, "Error [%d] while sending packet to HEPv3 server: %s\n",
519                         errno, strerror(errno));
520         } else if (res != sock_buffer_len) {
521                 ast_log(AST_LOG_WARNING, "Failed to send complete packet to HEPv3 server: %d of %u sent\n",
522                         res, sock_buffer_len);
523                 res = -1;
524         }
525
526         ast_free(sock_buffer);
527         return res;
528 }
529
530 int hepv3_send_packet(struct hepv3_capture_info *capture_info)
531 {
532         RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
533         int res;
534
535         if (!config->general->enabled) {
536                 return 0;
537         }
538
539         res = ast_taskprocessor_push(hep_queue_tp, hep_queue_cb, capture_info);
540         if (res == -1) {
541                 ao2_ref(capture_info, -1);
542         }
543
544         return res;
545 }
546
547 /*!
548  * \brief Post-apply callback for the config framework.
549  *
550  * This will create the run-time information from the supplied
551  * configuration.
552 */
553 static void hepv3_config_post_apply(void)
554 {
555         RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(global_config), ao2_cleanup);
556         struct hepv3_runtime_data *data;
557
558         data = hepv3_data_alloc(mod_cfg->general);
559         if (!data) {
560                 return;
561         }
562
563         ao2_global_obj_replace_unref(global_data, data);
564 }
565
566 /*!
567  * \brief Reload the module
568  */
569 static int reload_module(void)
570 {
571         if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
572                 return -1;
573         }
574         return 0;
575 }
576
577 /*!
578  * \brief Unload the module
579  */
580 static int unload_module(void)
581 {
582         hep_queue_tp = ast_taskprocessor_unreference(hep_queue_tp);
583
584         ao2_global_obj_release(global_config);
585         ao2_global_obj_release(global_data);
586         aco_info_destroy(&cfg_info);
587
588         return 0;
589 }
590
591 /*!
592  * \brief Load the module
593  */
594 static int load_module(void)
595 {
596         if (aco_info_init(&cfg_info)) {
597                 goto error;
598         }
599
600         hep_queue_tp = ast_taskprocessor_get("hep_queue_tp", TPS_REF_DEFAULT);
601         if (!hep_queue_tp) {
602                 goto error;
603         }
604
605         aco_option_register(&cfg_info, "enabled", ACO_EXACT, global_options, "yes", OPT_BOOL_T, 1, FLDSET(struct hepv3_global_config, enabled));
606         aco_option_register(&cfg_info, "capture_address", ACO_EXACT, global_options, DEFAULT_HEP_SERVER, OPT_STRINGFIELD_T, 0, STRFLDSET(struct hepv3_global_config, capture_address));
607         aco_option_register(&cfg_info, "capture_password", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct hepv3_global_config, capture_password));
608         aco_option_register(&cfg_info, "capture_id", ACO_EXACT, global_options, "0", OPT_UINT_T, 0, STRFLDSET(struct hepv3_global_config, capture_id));
609
610         if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
611                 goto error;
612         }
613
614         return AST_MODULE_LOAD_SUCCESS;
615
616 error:
617         aco_info_destroy(&cfg_info);
618         return AST_MODULE_LOAD_DECLINE;
619 }
620
621 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HEPv3 API",
622         .load = load_module,
623         .unload = unload_module,
624         .reload = reload_module,
625         .load_pri = AST_MODPRI_APP_DEPEND,
626         );