res_hep/res_hep_pjsip: Add a HEPv3 capture agent module and a logger for PJSIP

This patch adds the following:
(1) A new module, res_hep, which implements a generic packet capture agent for
the Homer Encapsulation Protocol (HEP) version 3. Note that this code is based
on a patch provided by Alexandr Dubovikov; I basically just wrapped it up,
added configuration via the configuration framework, and threw in a
taskprocessor.
(2) A new module, res_hep_pjsip, which forwards all SIP message traffic that
passes through the res_pjsip stack over to res_hep for encapsulation and
transmission to a HEPv3 capture server.

Much thanks to Alexandr for his Asterisk patch for this code and for a *lot*
of patience waiting for me to port it to 12/trunk. Due to some dithering on
my part, this has taken the better part of a year to port forward (I still
blame CDRs for the delay).

ASTERISK-23557 #close

Review: https://reviewboard.asterisk.org/r/3207/
........

Merged revisions 411534 from http://svn.asterisk.org/svn/asterisk/branches/12


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@411556 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Matthew Jordan
2014-03-28 18:32:50 +00:00
parent a35ce3924b
commit ef0c9fe4d8
6 changed files with 917 additions and 10 deletions

23
CHANGES
View File

@@ -89,21 +89,11 @@ Debugging
without configuration changes. These dynamic logger channels will only without configuration changes. These dynamic logger channels will only
exist until the next restart of asterisk. exist until the next restart of asterisk.
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 12.1.0 to Asterisk 12.2.0 ------------
------------------------------------------------------------------------------
ARI
------------------
* The live recording object on recording events now contains a target_uri
field which contains the URI of what is being recorded.
Core Core
------------------ ------------------
* Exposed sorcery-based configuration files like pjsip.conf to dialplans via * Exposed sorcery-based configuration files like pjsip.conf to dialplans via
the new AST_SORCERY diaplan function. the new AST_SORCERY diaplan function.
=======
ARI ARI
------------------ ------------------
* The live recording object on recording events now contains a target_uri * The live recording object on recording events now contains a target_uri
@@ -138,6 +128,19 @@ RealTime
* A new set of Alembic scripts has been added for CDR tables. This will create * A new set of Alembic scripts has been added for CDR tables. This will create
a 'cdr' table with the default schema that Asterisk expects. a 'cdr' table with the default schema that Asterisk expects.
res_hep
------------------
* A new module, res_hep, has been added, that acts as a generic packet
capture agent for the Homer Encapsulation Protocol (HEP) version 3.
It can be configured via hep.conf. Other modules can use res_hep to send
message traffic to a HEP capture server.
res_hep_pjsip
------------------
* A new module, res_hep_pjsip, has been added that will forward PJSIP
message traffic to a HEP capture server. See res_hep for more
information.
res_pjsip res_pjsip
------------------ ------------------
* transport and endpoint ToS options (tos, tos_audio, and tos_video) may now * transport and endpoint ToS options (tos, tos_audio, and tos_video) may now

16
configs/hep.conf.sample Normal file
View File

@@ -0,0 +1,16 @@
;
; res_hep Module configuration for Asterisk
;
; All settings are currently set in the general section.
[general]
enabled = yes ; Enable/disable forwarding of packets to a
; HEP server. Default is "yes".
capture_address = 192.168.1.1:9061 ; The address of the HEP capture server.
capture_password = foo ; If specified, the authorization passsword
; for the HEP server. If not specified, no
; authorization password will be sent.
capture_id = 1234 ; A unique integer identifier for this
; server. This ID will be embedded sent
; with each packet from this server.

111
include/asterisk/res_hep.h Normal file
View File

@@ -0,0 +1,111 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2014, Digium, Inc.
*
* Alexandr Dubovikov <alexandr.dubovikov@sipcapture.org>
* Matt Jordan <mjordan@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief Routines for integration with Homer using HEPv3
*
* \author Alexandr Dubovikov <alexandr.dubovikov@sipcapture.org>
* \author Matt Jordan <mjordan@digium.com>
*
*/
#ifndef _ASTERISK_RES_HEPV3_H
#define _ASTERISK_RES_HEPV3_H
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
#include "asterisk/netsock2.h"
/*! \brief HEPv3 Packet Capture Types */
enum hepv3_capture_type {
HEPV3_CAPTURE_TYPE_SIP = 0x01,
HEPV3_CAPTURE_TYPE_H323 = 0x02,
HEPV3_CAPTURE_TYPE_SDP = 0x03,
HEPV3_CAPTURE_TYPE_RTP = 0x04,
HEPV3_CAPTURE_TYPE_RTCP = 0x05,
HEPV3_CAPTURE_TYPE_MGCP = 0x06,
HEPV3_CAPTURE_TYPE_MEGACO = 0x07,
HEPV3_CAPTURE_TYPE_M2UA = 0x08,
HEPV3_CAPTURE_TYPE_M3UA = 0x09,
HEPV3_CAPTURE_TYPE_IAX = 0x10,
};
/*! \brief HEPv3 Capture Info */
struct hepv3_capture_info {
/*! The source address of the packet */
struct ast_sockaddr src_addr;
/*! The destination address of the packet */
struct ast_sockaddr dst_addr;
/*! The time the packet was captured */
struct timeval capture_time;
/*! The actual payload */
void *payload;
/*! Some UUID for the packet */
char *uuid;
/*! The \ref hepv3_capture_type packet type captured */
enum hepv3_capture_type capture_type;
/*! The size of the payload */
size_t len;
/*! If non-zero, the payload accompanying this capture info will be compressed */
unsigned int zipped:1;
};
/*!
* \brief Create a \ref hepv3_capture_info object
*
* This returned object is an ao2 reference counted object.
*
* Any attribute in the returned \ref hepv3_capture_info that is a
* pointer should point to something that is allocated on the heap,
* as it will be free'd when the \ref hepv3_capture_info object is
* reclaimed.
*
* \param payload The payload to send to the HEP capture node
* \param len Length of \ref payload
*
* \retval A \ref hepv3_capture_info ref counted object on success
* \retval NULL on error
*/
struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t len);
/*!
* \brief Send a generic packet capture to HEPv3
*
* \param capture_info Information describing the packet. This
* should be a reference counted object, created via
* \ref hepv3_create_capture_info.
*
* Once this function is called, it assumes ownership of the
* \ref capture_info object and steals the reference of the
* object. Regardless of success or failure, the calling function
* should assumed that this function will own the object.
*
* \retval 0 on success
* \retval -1 on error
*/
int hepv3_send_packet(struct hepv3_capture_info *capture_info);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* _ASTERISK_RES_HEPV3_H */

626
res/res_hep.c Normal file
View File

@@ -0,0 +1,626 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2014, Digium, Inc.
*
* Alexandr Dubovikov <alexandr.dubovikov@sipcapture.org>
* Matt Jordan <mjordan@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief Routines for integration with Homer using HEPv3
*
* \author Alexandr Dubovikov <alexandr.dubovikov@sipcapture.org>
* \author Matt Jordan <mjordan@digium.com>
*
*/
/*!
* \li \ref res_hep.c uses the configuration file \ref hep.conf
* \addtogroup configuration_file Configuration Files
*/
/*!
* \page hep.conf hep.conf
* \verbinclude hep.conf.sample
*/
/*** MODULEINFO
<defaultenabled>no</defaultenabled>
<support_level>extended</support_level>
***/
/*** DOCUMENTATION
<configInfo name="res_hep" language="en_US">
<synopsis>Resource for integration with Homer using HEPv3</synopsis>
<configFile name="hep.conf">
<configObject name="general">
<synopsis>General settings.</synopsis>
<description><para>
The <emphasis>general</emphasis> settings section contains information
to configure Asterisk as a Homer capture agent.
</para>
</description>
<configOption name="enabled" default="yes">
<synopsis>Enable or disable packet capturing.</synopsis>
<description>
<enumlist>
<enum name="no" />
<enum name="yes" />
</enumlist>
</description>
</configOption>
<configOption name="capture_address" default="192.168.1.1:9061">
<synopsis>The address and port of the Homer server to send packets to.</synopsis>
</configOption>
<configOption name="capture_password">
<synopsis>If set, the authentication password to send to Homer.</synopsis>
</configOption>
<configOption name="capture_id" default="0">
<synopsis>The ID for this capture agent.</synopsis>
</configOption>
</configObject>
</configFile>
</configInfo>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/astobj2.h"
#include "asterisk/config_options.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/res_hep.h"
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip6.h>
#define DEFAULT_HEP_SERVER ""
/*! Generic vendor ID. Used for HEPv3 standard packets */
#define GENERIC_VENDOR_ID 0x0000
/*! Asterisk vendor ID. Used for custom data to send to a capture node */
#define ASTERISK_VENDOR_ID 0x0004
/*! Chunk types from the HEPv3 Spec */
enum hepv3_chunk_types {
/*! THE IP PROTOCOL FAMILY */
CHUNK_TYPE_IP_PROTOCOL_FAMILY = 0X0001,
/*! THE IP PROTOCOL ID (UDP, TCP, ETC.) */
CHUNK_TYPE_IP_PROTOCOL_ID = 0X0002,
/*! IF IPV4, THE SOURCE ADDRESS */
CHUNK_TYPE_IPV4_SRC_ADDR = 0X0003,
/*! IF IPV4, THE DESTINATION ADDRESS */
CHUNK_TYPE_IPV4_DST_ADDR = 0X0004,
/*! IF IPV6, THE SOURCE ADDRESS */
CHUNK_TYPE_IPV6_SRC_ADDR = 0X0005,
/*! IF IPV6, THE DESTINATION ADDRESS */
CHUNK_TYPE_IPV6_DST_ADDR = 0X0006,
/*! THE SOURCE PORT */
CHUNK_TYPE_SRC_PORT = 0X0007,
/*! THE DESTINATION PORT */
CHUNK_TYPE_DST_PORT = 0X0008,
/*! THE CAPTURE TIME (SECONDS) */
CHUNK_TYPE_TIMESTAMP_SEC = 0X0009,
/*! THE CAPTURE TIME (MICROSECONDS) */
CHUNK_TYPE_TIMESTAMP_USEC = 0X000A,
/*! THE PROTOCOL PACKET TYPE. SEE /REF HEPV3_CAPTURE_TYPE */
CHUNK_TYPE_PROTOCOL_TYPE = 0X000B,
/*! OUR CAPTURE AGENT ID */
CHUNK_TYPE_CAPTURE_AGENT_ID = 0X000C,
/*! A KEEP ALIVE TIMER */
CHUNK_TYPE_KEEP_ALIVE_TIMER = 0X000D,
/*! THE \REF CAPTURE_PASSWORD IF DEFINED */
CHUNK_TYPE_AUTH_KEY = 0X000E,
/*! THE ONE AND ONLY PAYLOAD */
CHUNK_TYPE_PAYLOAD = 0X000F,
/*! THE ONE AND ONLY (ZIPPED) PAYLOAD */
CHUNK_TYPE_PAYLOAD_ZIP = 0X0010,
/*! THE UUID FOR THIS PACKET */
CHUNK_TYPE_UUID = 0X0011,
};
#define INITIALIZE_GENERIC_HEP_IDS(hep_chunk, type) do { \
(hep_chunk)->vendor_id = htons(GENERIC_VENDOR_ID); \
(hep_chunk)->type_id = htons((type)); \
} while (0)
#define INITIALIZE_GENERIC_HEP_IDS_VAR(hep_chunk, type, len) do { \
INITIALIZE_GENERIC_HEP_IDS((hep_chunk), (type)); \
(hep_chunk)->length = htons(sizeof(*(hep_chunk)) + len); \
} while (0)
#define INITIALIZE_GENERIC_HEP_CHUNK(hep_item, type) do { \
INITIALIZE_GENERIC_HEP_IDS(&(hep_item)->chunk, (type)); \
(hep_item)->chunk.length = htons(sizeof(*(hep_item))); \
} while (0)
#define INITIALIZE_GENERIC_HEP_CHUNK_DATA(hep_item, type, value) do { \
INITIALIZE_GENERIC_HEP_CHUNK((hep_item), (type)); \
(hep_item)->data = (value); \
} while (0)
/*
* HEPv3 Types.
* Note that the content in these is stored in network byte order.
*/
struct hep_chunk {
u_int16_t vendor_id;
u_int16_t type_id;
u_int16_t length;
} __attribute__((packed));
struct hep_chunk_uint8 {
struct hep_chunk chunk;
u_int8_t data;
} __attribute__((packed));
struct hep_chunk_uint16 {
struct hep_chunk chunk;
u_int16_t data;
} __attribute__((packed));
struct hep_chunk_uint32 {
struct hep_chunk chunk;
u_int32_t data;
} __attribute__((packed));
struct hep_chunk_ip4 {
struct hep_chunk chunk;
struct in_addr data;
} __attribute__((packed));
struct hep_chunk_ip6 {
struct hep_chunk chunk;
struct in6_addr data;
} __attribute__((packed));
struct hep_ctrl {
char id[4];
u_int16_t length;
} __attribute__((packed));
/* HEP structures */
struct hep_generic {
struct hep_ctrl header;
struct hep_chunk_uint8 ip_family;
struct hep_chunk_uint8 ip_proto;
struct hep_chunk_uint16 src_port;
struct hep_chunk_uint16 dst_port;
struct hep_chunk_uint32 time_sec;
struct hep_chunk_uint32 time_usec;
struct hep_chunk_uint8 proto_t;
struct hep_chunk_uint32 capt_id;
} __attribute__((packed));
/*! \brief Global configuration for the module */
struct hepv3_global_config {
unsigned int enabled; /*!< Whether or not sending is enabled */
unsigned int capture_id; /*!< Capture ID for this agent */
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(capture_address); /*!< Address to send to */
AST_STRING_FIELD(capture_password); /*!< Password for Homer server */
);
};
/*! \brief The actual module config */
struct module_config {
struct hepv3_global_config *general; /*!< The general config settings */
};
/*! \brief Run-time data derived from \ref hepv3_global_config */
struct hepv3_runtime_data {
struct ast_sockaddr remote_addr; /*!< The address to send to */
int sockfd; /*!< The socket file descriptor */
};
static struct aco_type global_option = {
.type = ACO_GLOBAL,
.name = "general",
.item_offset = offsetof(struct module_config, general),
.category_match = ACO_WHITELIST,
.category = "^general$",
};
struct aco_type *global_options[] = ACO_TYPES(&global_option);
struct aco_file hepv3_conf = {
.filename = "hep.conf",
.types = ACO_TYPES(&global_option),
};
/*! \brief The module configuration container */
static AO2_GLOBAL_OBJ_STATIC(global_config);
/*! \brief Current module data */
static AO2_GLOBAL_OBJ_STATIC(global_data);
static struct ast_taskprocessor *hep_queue_tp;
static void *module_config_alloc(void);
static void hepv3_config_post_apply(void);
/*! \brief Register information about the configs being processed by this module */
CONFIG_INFO_STANDARD(cfg_info, global_config, module_config_alloc,
.files = ACO_FILES(&hepv3_conf),
.post_apply_config = hepv3_config_post_apply,
);
static void hepv3_config_dtor(void *obj)
{
struct hepv3_global_config *config = obj;
ast_string_field_free_memory(config);
}
/*! \brief HEPv3 configuration object allocation */
static void *hepv3_config_alloc(void)
{
struct hepv3_global_config *config;
config = ao2_alloc(sizeof(*config), hepv3_config_dtor);
if (!config || ast_string_field_init(config, 32)) {
return NULL;
}
return config;
}
/*! \brief Configuration object destructor */
static void module_config_dtor(void *obj)
{
struct module_config *config = obj;
if (config->general) {
ao2_ref(config->general, -1);
}
}
/*! \brief Module config constructor */
static void *module_config_alloc(void)
{
struct module_config *config;
config = ao2_alloc(sizeof(*config), module_config_dtor);
if (!config) {
return NULL;
}
config->general = hepv3_config_alloc();
if (!config->general) {
ao2_ref(config, -1);
config = NULL;
}
return config;
}
/*! \brief HEPv3 run-time data destructor */
static void hepv3_data_dtor(void *obj)
{
struct hepv3_runtime_data *data = obj;
if (data->sockfd > -1) {
close(data->sockfd);
data->sockfd = -1;
}
}
/*! \brief Allocate the HEPv3 run-time data */
static struct hepv3_runtime_data *hepv3_data_alloc(struct hepv3_global_config *config)
{
struct hepv3_runtime_data *data;
data = ao2_alloc(sizeof(*data), hepv3_data_dtor);
if (!data) {
return NULL;
}
if (!ast_sockaddr_parse(&data->remote_addr, config->capture_address, PARSE_PORT_REQUIRE)) {
ast_log(AST_LOG_WARNING, "Failed to create address from %s\n", config->capture_address);
ao2_ref(data, -1);
return NULL;
}
data->sockfd = socket(ast_sockaddr_is_ipv6(&data->remote_addr) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
if (data->sockfd < 0) {
ast_log(AST_LOG_WARNING, "Failed to create socket for address %s: %s\n",
config->capture_address, strerror(errno));
ao2_ref(data, -1);
return NULL;
}
return data;
}
/*! \brief Destructor for a \ref hepv3_capture_info object */
static void capture_info_dtor(void *obj)
{
struct hepv3_capture_info *info = obj;
ast_free(info->uuid);
ast_free(info->payload);
}
struct hepv3_capture_info *hepv3_create_capture_info(const void *payload, size_t len)
{
struct hepv3_capture_info *info;
info = ao2_alloc(sizeof(*info), capture_info_dtor);
if (!info) {
return NULL;
}
info->payload = ast_malloc(len);
if (!info->payload) {
ao2_ref(info, -1);
return NULL;
}
memcpy(info->payload, payload, len);
info->len = len;
return info;
}
/*! \brief Callback function for the \ref hep_queue_tp taskprocessor */
static int hep_queue_cb(void *data)
{
RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
RAII_VAR(struct hepv3_runtime_data *, hepv3_data, ao2_global_obj_ref(global_data), ao2_cleanup);
RAII_VAR(struct hepv3_capture_info *, capture_info, data, ao2_cleanup);
struct hep_generic hg_pkt;
unsigned int packet_len = 0, sock_buffer_len;
struct hep_chunk_ip4 ipv4_src, ipv4_dst;
struct hep_chunk_ip6 ipv6_src, ipv6_dst;
struct hep_chunk auth_key, payload, uuid;
void *sock_buffer;
int res;
if (!capture_info || !config || !hepv3_data) {
return 0;
}
if (ast_sockaddr_is_ipv4(&capture_info->src_addr) != ast_sockaddr_is_ipv4(&capture_info->dst_addr)) {
ast_log(AST_LOG_NOTICE, "Unable to send packet: Address Family mismatch between source/destination\n");
return -1;
}
packet_len = sizeof(hg_pkt);
/* Build HEPv3 header, capture info, and calculate the total packet size */
memcpy(hg_pkt.header.id, "\x48\x45\x50\x33", 4);
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_proto, CHUNK_TYPE_IP_PROTOCOL_ID, 0x11);
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.src_port, CHUNK_TYPE_SRC_PORT, htons(ast_sockaddr_port(&capture_info->src_addr)));
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.dst_port, CHUNK_TYPE_DST_PORT, htons(ast_sockaddr_port(&capture_info->dst_addr)));
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.time_sec, CHUNK_TYPE_TIMESTAMP_SEC, htonl(capture_info->capture_time.tv_sec));
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.time_usec, CHUNK_TYPE_TIMESTAMP_USEC, htonl(capture_info->capture_time.tv_usec));
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.proto_t, CHUNK_TYPE_PROTOCOL_TYPE, capture_info->capture_type);
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.capt_id, CHUNK_TYPE_CAPTURE_AGENT_ID, htonl(config->general->capture_id));
if (ast_sockaddr_is_ipv4(&capture_info->src_addr)) {
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_family,
CHUNK_TYPE_IP_PROTOCOL_FAMILY, AF_INET);
INITIALIZE_GENERIC_HEP_CHUNK(&ipv4_src, CHUNK_TYPE_IPV4_SRC_ADDR);
inet_pton(AF_INET, ast_sockaddr_stringify_addr(&capture_info->src_addr), &ipv4_src.data);
INITIALIZE_GENERIC_HEP_CHUNK(&ipv4_dst, CHUNK_TYPE_IPV4_DST_ADDR);
inet_pton(AF_INET, ast_sockaddr_stringify_addr(&capture_info->dst_addr), &ipv4_dst.data);
packet_len += (sizeof(ipv4_src) + sizeof(ipv4_dst));
} else {
INITIALIZE_GENERIC_HEP_CHUNK_DATA(&hg_pkt.ip_family,
CHUNK_TYPE_IP_PROTOCOL_FAMILY, AF_INET6);
INITIALIZE_GENERIC_HEP_CHUNK(&ipv6_src, CHUNK_TYPE_IPV6_SRC_ADDR);
inet_pton(AF_INET6, ast_sockaddr_stringify_addr(&capture_info->src_addr), &ipv6_src.data);
INITIALIZE_GENERIC_HEP_CHUNK(&ipv6_dst, CHUNK_TYPE_IPV6_DST_ADDR);
inet_pton(AF_INET6, ast_sockaddr_stringify_addr(&capture_info->dst_addr), &ipv6_dst.data);
packet_len += (sizeof(ipv6_src) + sizeof(ipv6_dst));
}
if (!ast_strlen_zero(config->general->capture_password)) {
INITIALIZE_GENERIC_HEP_IDS_VAR(&auth_key, CHUNK_TYPE_AUTH_KEY, strlen(config->general->capture_password));
packet_len += (sizeof(auth_key) + strlen(config->general->capture_password));
}
INITIALIZE_GENERIC_HEP_IDS_VAR(&uuid, CHUNK_TYPE_UUID, strlen(capture_info->uuid));
packet_len += (sizeof(uuid) + strlen(capture_info->uuid));
INITIALIZE_GENERIC_HEP_IDS_VAR(&payload,
capture_info->zipped ? CHUNK_TYPE_PAYLOAD_ZIP : CHUNK_TYPE_PAYLOAD, capture_info->len);
packet_len += (sizeof(payload) + capture_info->len);
hg_pkt.header.length = htons(packet_len);
/* Build the buffer to send */
sock_buffer = ast_malloc(packet_len);
if (!sock_buffer) {
return -1;
}
/* Copy in the header */
memcpy(sock_buffer, &hg_pkt, sizeof(hg_pkt));
sock_buffer_len = sizeof(hg_pkt);
/* Addresses */
if (ast_sockaddr_is_ipv4(&capture_info->src_addr)) {
memcpy(sock_buffer + sock_buffer_len, &ipv4_src, sizeof(ipv4_src));
sock_buffer_len += sizeof(ipv4_src);
memcpy(sock_buffer + sock_buffer_len, &ipv4_dst, sizeof(ipv4_dst));
sock_buffer_len += sizeof(ipv4_dst);
} else {
memcpy(sock_buffer + sock_buffer_len, &ipv6_src, sizeof(ipv6_src));
sock_buffer_len += sizeof(ipv6_src);
memcpy(sock_buffer + sock_buffer_len, &ipv6_dst, sizeof(ipv6_dst));
sock_buffer_len += sizeof(ipv6_dst);
}
/* Auth Key */
if (!ast_strlen_zero(config->general->capture_password)) {
memcpy(sock_buffer + sock_buffer_len, &auth_key, sizeof(auth_key));
sock_buffer_len += sizeof(auth_key);
memcpy(sock_buffer + sock_buffer_len, config->general->capture_password, strlen(config->general->capture_password));
sock_buffer_len += strlen(config->general->capture_password);
}
/* UUID */
memcpy(sock_buffer + sock_buffer_len, &uuid, sizeof(uuid));
sock_buffer_len += sizeof(uuid);
memcpy(sock_buffer + sock_buffer_len, capture_info->uuid, strlen(capture_info->uuid));
sock_buffer_len += strlen(capture_info->uuid);
/* Packet! */
memcpy(sock_buffer + sock_buffer_len, &payload, sizeof(payload));
sock_buffer_len += sizeof(payload);
memcpy(sock_buffer + sock_buffer_len, capture_info->payload, capture_info->len);
sock_buffer_len += capture_info->len;
ast_assert(sock_buffer_len == packet_len);
res = ast_sendto(hepv3_data->sockfd, sock_buffer, sock_buffer_len, 0, &hepv3_data->remote_addr);
if (res < 0) {
ast_log(AST_LOG_ERROR, "Error [%d] while sending packet to HEPv3 server: %s\n",
errno, strerror(errno));
} else if (res != sock_buffer_len) {
ast_log(AST_LOG_WARNING, "Failed to send complete packet to HEPv3 server: %d of %u sent\n",
res, sock_buffer_len);
res = -1;
}
ast_free(sock_buffer);
return res;
}
int hepv3_send_packet(struct hepv3_capture_info *capture_info)
{
RAII_VAR(struct module_config *, config, ao2_global_obj_ref(global_config), ao2_cleanup);
int res;
if (!config->general->enabled) {
return 0;
}
res = ast_taskprocessor_push(hep_queue_tp, hep_queue_cb, capture_info);
if (res == -1) {
ao2_ref(capture_info, -1);
}
return res;
}
/*!
* \brief Post-apply callback for the config framework.
*
* This will create the run-time information from the supplied
* configuration.
*/
static void hepv3_config_post_apply(void)
{
RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(global_config), ao2_cleanup);
struct hepv3_runtime_data *data;
data = hepv3_data_alloc(mod_cfg->general);
if (!data) {
return;
}
ao2_global_obj_replace_unref(global_data, data);
}
/*!
* \brief Reload the module
*/
static int reload_module(void)
{
if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
return -1;
}
return 0;
}
/*!
* \brief Unload the module
*/
static int unload_module(void)
{
hep_queue_tp = ast_taskprocessor_unreference(hep_queue_tp);
ao2_global_obj_release(global_config);
ao2_global_obj_release(global_data);
aco_info_destroy(&cfg_info);
return 0;
}
/*!
* \brief Load the module
*/
static int load_module(void)
{
if (aco_info_init(&cfg_info)) {
goto error;
}
hep_queue_tp = ast_taskprocessor_get("hep_queue_tp", TPS_REF_DEFAULT);
if (!hep_queue_tp) {
goto error;
}
aco_option_register(&cfg_info, "enabled", ACO_EXACT, global_options, "yes", OPT_BOOL_T, 1, FLDSET(struct hepv3_global_config, enabled));
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));
aco_option_register(&cfg_info, "capture_password", ACO_EXACT, global_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct hepv3_global_config, capture_password));
aco_option_register(&cfg_info, "capture_id", ACO_EXACT, global_options, "0", OPT_UINT_T, 0, STRFLDSET(struct hepv3_global_config, capture_id));
if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
goto error;
}
return AST_MODULE_LOAD_SUCCESS;
error:
aco_info_destroy(&cfg_info);
return AST_MODULE_LOAD_DECLINE;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HEPv3 API",
.load = load_module,
.unload = unload_module,
.reload = reload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
);

7
res/res_hep.exports.in Normal file
View File

@@ -0,0 +1,7 @@
{
global:
LINKER_SYMBOL_PREFIX*hepv3_send_packet;
LINKER_SYMBOL_PREFIX*hepv3_create_capture_info;
local:
*;
};

144
res/res_hep_pjsip.c Normal file
View File

@@ -0,0 +1,144 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2014, Digium, Inc.
*
* Matt Jordan <mjordan@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief PJSIP logging with Homer
*
* \author Matt Jordan <mjordan@digium.com>
*
*/
/*** MODULEINFO
<depend>pjproject</depend>
<depend>res_pjsip</depend>
<depend>res_hep</depend>
<defaultenabled>no</defaultenabled>
<support_level>extended</support_level>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <pjsip.h>
#include "asterisk/res_pjsip.h"
#include "asterisk/res_hep.h"
#include "asterisk/module.h"
#include "asterisk/netsock2.h"
static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
{
char local_buf[256];
char remote_buf[256];
char *uuid;
struct hepv3_capture_info *capture_info;
pjsip_cid_hdr *cid_hdr;
capture_info = hepv3_create_capture_info(tdata->buf.start, (size_t)(tdata->buf.cur - tdata->buf.start));
if (!capture_info) {
return PJ_SUCCESS;
}
pj_sockaddr_print(&tdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
pj_sockaddr_print(&tdata->tp_info.dst_addr, remote_buf, sizeof(remote_buf), 3);
cid_hdr = PJSIP_MSG_CID_HDR(tdata->msg);
uuid = ast_malloc(pj_strlen(&cid_hdr->id) + 1);
if (!uuid) {
ao2_ref(capture_info, -1);
return PJ_SUCCESS;
}
ast_copy_pj_str(uuid, &cid_hdr->id, pj_strlen(&cid_hdr->id) + 1);
ast_sockaddr_parse(&capture_info->src_addr, local_buf, PARSE_PORT_REQUIRE);
ast_sockaddr_parse(&capture_info->dst_addr, remote_buf, PARSE_PORT_REQUIRE);
capture_info->capture_time = ast_tvnow();
capture_info->capture_type = HEPV3_CAPTURE_TYPE_SIP;
capture_info->uuid = uuid;
capture_info->zipped = 0;
hepv3_send_packet(capture_info);
return PJ_SUCCESS;
}
static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
{
char local_buf[256];
char remote_buf[256];
char *uuid;
struct hepv3_capture_info *capture_info;
capture_info = hepv3_create_capture_info(&rdata->pkt_info.packet, rdata->pkt_info.len);
if (!capture_info) {
return PJ_SUCCESS;
}
pj_sockaddr_print(&rdata->tp_info.transport->local_addr, local_buf, sizeof(local_buf), 3);
pj_sockaddr_print(&rdata->pkt_info.src_addr, remote_buf, sizeof(remote_buf), 3);
uuid = ast_malloc(pj_strlen(&rdata->msg_info.cid->id) + 1);
if (!uuid) {
ao2_ref(capture_info, -1);
return PJ_SUCCESS;
}
ast_copy_pj_str(uuid, &rdata->msg_info.cid->id, pj_strlen(&rdata->msg_info.cid->id) + 1);
ast_sockaddr_parse(&capture_info->src_addr, remote_buf, PARSE_PORT_REQUIRE);
ast_sockaddr_parse(&capture_info->dst_addr, local_buf, PARSE_PORT_REQUIRE);
capture_info->capture_time.tv_sec = rdata->pkt_info.timestamp.sec;
capture_info->capture_time.tv_usec = rdata->pkt_info.timestamp.msec * 1000;
capture_info->capture_type = HEPV3_CAPTURE_TYPE_SIP;
capture_info->uuid = uuid;
capture_info->zipped = 0;
hepv3_send_packet(capture_info);
return PJ_FALSE;
}
static pjsip_module logging_module = {
.name = { "HEPv3 Logging Module", 20 },
.priority = 0,
.on_rx_request = logging_on_rx_msg,
.on_rx_response = logging_on_rx_msg,
.on_tx_request = logging_on_tx_msg,
.on_tx_response = logging_on_tx_msg,
};
static int load_module(void)
{
ast_sip_register_service(&logging_module);
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
ast_sip_unregister_service(&logging_module);
return 0;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP HEPv3 Logger",
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_DEFAULT,
);