28 #include <netinet/in.h>
32 #include "asterisk/res_pjsip.h"
42 uint32_t magic_number;
64 } __attribute__((__packed__));
84 uint32_t ip6_un1_flow;
91 struct in6_addr ip6_src;
133 static
void pjsip_logger_session_destroy(
void *obj)
166 static int apply_method_filter(
const struct pjsip_logger_session *session,
const pjsip_method *method)
176 for (i = 0; i < size; ++i) {
178 if (pjsip_method_cmp(&candidate->
method, method) == 0) {
188 static inline int pjsip_log_test_filter(
const struct pjsip_logger_session *session,
const char *address,
int port,
const pjsip_method *method)
200 if (apply_method_filter(session, method)) {
206 if (ast_strlen_zero(address) || !session->
matches) {
222 static void pjsip_logger_write_to_pcap(
struct pjsip_logger_session *session,
const char *msg,
size_t msg_len,
223 pj_sockaddr *source, pj_sockaddr *destination)
227 .ts_sec = now.tv_sec,
228 .ts_usec = now.tv_usec,
238 .ip6_ctlun.ip6_un2_vfc = 0x60,
240 void *pcap_ip_header;
241 size_t pcap_ip_header_len;
258 if ((source && source->addr.sa_family == pj_AF_INET()) ||
259 (destination && destination->addr.sa_family == pj_AF_INET())) {
260 pcap_ethernet_header.
type = htons(0x0800);
261 pcap_ip_header = &pcap_ipv4_header;
262 pcap_ip_header_len =
sizeof(
struct pcap_ipv4_header);
264 memcpy(&pcap_ipv4_header.
ip_src, pj_sockaddr_get_addr(source), pj_sockaddr_get_addr_len(source));
267 memcpy(&pcap_ipv4_header.
ip_dst, pj_sockaddr_get_addr(destination), pj_sockaddr_get_addr_len(destination));
269 pcap_ipv4_header.
ip_len = htons(
sizeof(
struct pcap_udp_header) +
sizeof(
struct pcap_ipv4_header) + msg_len);
272 pcap_ethernet_header.
type = htons(0x86DD);
273 pcap_ip_header = &pcap_ipv6_header;
274 pcap_ip_header_len =
sizeof(
struct pcap_ipv6_header);
276 memcpy(&pcap_ipv6_header.ip6_src, pj_sockaddr_get_addr(source), pj_sockaddr_get_addr_len(source));
279 memcpy(&pcap_ipv6_header.
ip6_dst, pj_sockaddr_get_addr(destination), pj_sockaddr_get_addr_len(destination));
281 pcap_ipv6_header.ip6_ctlun.ip6_un1.ip6_un1_plen = htons(
sizeof(
struct pcap_udp_header) + msg_len);
282 pcap_ipv6_header.ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_UDP;
291 if (fwrite(&pcap_record_header,
sizeof(
struct pcap_record_header), 1, session->
pcap_file) != 1) {
292 ast_log(LOG_WARNING,
"Writing PCAP header failed: %s\n", strerror(errno));
294 if (fwrite(&pcap_ethernet_header,
sizeof(
struct pcap_ethernet_header), 1, session->
pcap_file) != 1) {
295 ast_log(LOG_WARNING,
"Writing ethernet header to pcap failed: %s\n", strerror(errno));
297 if (fwrite(pcap_ip_header, pcap_ip_header_len, 1, session->
pcap_file) != 1) {
298 ast_log(LOG_WARNING,
"Writing IP header to pcap failed: %s\n", strerror(errno));
301 ast_log(LOG_WARNING,
"Writing UDP header to pcap failed: %s\n", strerror(errno));
303 if (fwrite(msg, msg_len, 1, session->
pcap_file) != 1) {
304 ast_log(LOG_WARNING,
"Writing UDP payload to pcap failed: %s\n", strerror(errno));
310 static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
312 char buffer[AST_SOCKADDR_BUFLEN];
314 ao2_rdlock(default_logger);
315 if (!pjsip_log_test_filter(default_logger, tdata->tp_info.dst_name, tdata->tp_info.dst_port, &tdata->msg->line.req.method)) {
316 ao2_unlock(default_logger);
319 ao2_unlock(default_logger);
322 ast_verbose(
"<--- Transmitting SIP %s (%d bytes) to %s:%s --->\n%.*s\n",
323 tdata->msg->type == PJSIP_REQUEST_MSG ?
"request" :
"response",
324 (
int) (tdata->buf.cur - tdata->buf.start),
325 tdata->tp_info.transport->type_name,
326 pj_sockaddr_print(&tdata->tp_info.dst_addr, buffer,
sizeof(buffer), 3),
327 (
int) (tdata->buf.end - tdata->buf.start), tdata->buf.start);
331 pjsip_logger_write_to_pcap(default_logger, tdata->buf.start, (
int) (tdata->buf.cur - tdata->buf.start),
332 NULL, &tdata->tp_info.dst_addr);
338 static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
340 char buffer[AST_SOCKADDR_BUFLEN];
342 if (!rdata->msg_info.msg) {
346 ao2_rdlock(default_logger);
347 if (!pjsip_log_test_filter(default_logger, rdata->pkt_info.src_name, rdata->pkt_info.src_port, &rdata->msg_info.msg->line.req.method)) {
348 ao2_unlock(default_logger);
351 ao2_unlock(default_logger);
354 ast_verbose(
"<--- Received SIP %s (%d bytes) from %s:%s --->\n%s\n",
355 rdata->msg_info.msg->type == PJSIP_REQUEST_MSG ?
"request" :
"response",
357 rdata->tp_info.transport->type_name,
358 pj_sockaddr_print(&rdata->pkt_info.src_addr, buffer,
sizeof(buffer), 3),
359 rdata->pkt_info.packet);
363 pjsip_logger_write_to_pcap(default_logger, rdata->pkt_info.packet, rdata->msg_info.len,
364 &rdata->pkt_info.src_addr, NULL);
370 static pjsip_module logging_module = {
371 .name = {
"Logging Module", 14 },
373 .on_rx_request = logging_on_rx_msg,
374 .on_rx_response = logging_on_rx_msg,
375 .on_tx_request = logging_on_tx_msg,
376 .on_tx_response = logging_on_tx_msg,
379 static char *pjsip_enable_logger_all(
int fd)
381 ao2_wrlock(default_logger);
384 ao2_unlock(default_logger);
387 ast_cli(fd,
"PJSIP Logging enabled\n");
393 static char *pjsip_enable_logger_host(
int fd,
const char *arg,
unsigned int add_host)
395 const char *host = arg;
400 ao2_wrlock(default_logger);
408 default_logger->
matches = NULL;
411 mask = strrchr(host,
'/');
414 ao2_unlock(default_logger);
415 return CLI_SHOWUSAGE;
421 if (!default_logger->
matches || error) {
423 ast_cli(fd,
"Failed to add address '%s' for logging\n", host);
425 ao2_unlock(default_logger);
429 ao2_unlock(default_logger);
440 size_t method_bytes = strlen(method);
448 memcpy(info->
name, method, method_bytes + 1);
449 pj_strset(&info->pj_name, info->
name, method_bytes);
450 pjsip_method_init_np(&info->
method, &info->pj_name);
458 return pjsip_method_cmp(&element->
method, &candidate->
method) == 0
463 static int method_logging_info_sort_cmp(
const void *a,
const void *b)
467 return strcasecmp((*m_a)->name, (*m_b)->name);
471 static char *pjsip_enable_logger_method(
int fd,
const char *arg,
int add_method)
476 method = method_logging_info_alloc(arg);
481 ao2_wrlock(default_logger);
491 ast_cli(fd,
"Method '%s' is already enabled\n", method->
name);
492 ao2_unlock(default_logger);
498 ast_log(LOG_ERROR,
"Cannot register logger method '%s'. Unable to append.\n", method->
name);
499 ao2_unlock(default_logger);
513 (
int) method->pj_name.slen, method->pj_name.ptr);
516 ast_cli(fd,
"PJSIP Logging Enabled for SIP Methods: %s\n",
ast_str_buffer(str));
520 ao2_unlock(default_logger);
525 static char *pjsip_disable_logger(
int fd)
527 ao2_wrlock(default_logger);
545 default_logger->
matches = NULL;
547 ao2_unlock(default_logger);
550 ast_cli(fd,
"PJSIP Logging disabled\n");
556 static char *pjsip_set_logger_verbose(
int fd,
const char *arg)
558 ao2_wrlock(default_logger);
560 ao2_unlock(default_logger);
562 ast_cli(fd,
"PJSIP Logging to verbose has been %s\n",
ast_true(arg) ?
"enabled" :
"disabled");
567 static char *pjsip_set_logger_pcap(
int fd,
const char *arg)
570 .magic_number = 0xa1b2c3d4,
577 ao2_wrlock(default_logger);
585 default_logger->
pcap_file = fopen(arg,
"wb");
587 ao2_unlock(default_logger);
588 ast_cli(fd,
"Failed to open file '%s' for pcap writing\n", arg);
591 fwrite(&pcap_header, 1,
sizeof(
struct pcap_header), default_logger->
pcap_file);
594 ao2_unlock(default_logger);
596 ast_cli(fd,
"PJSIP logging to pcap file '%s'\n", arg);
601 enum pjsip_logger_mask {
602 AST_PJSIP_LOGGER_UNSET = 0,
603 AST_PJSIP_LOGGER_NONE = (1 << 0),
604 AST_PJSIP_LOGGER_HOST = (1 << 1),
605 AST_PJSIP_LOGGER_METHOD = (1 << 2),
606 AST_PJSIP_LOGGER_VERBOSE = (1 << 3),
607 AST_PJSIP_LOGGER_PCAP = (1 << 4),
608 AST_PJSIP_LOGGER_ALL = (1 << 5),
611 static enum pjsip_logger_mask logger_cli_settings = AST_PJSIP_LOGGER_UNSET;
612 static enum pjsip_logger_mask logger_config_settings = AST_PJSIP_LOGGER_UNSET;
616 static const char *
const method_choices[] = {
617 "INVITE",
"CANCEL",
"ACK",
618 "BYE",
"REGISTER",
"OPTION",
619 "SUBSCRIBE",
"NOTIFY",
"PUBLISH",
626 if (cmd == CLI_INIT) {
627 e->
command =
"pjsip set logger {on|off|host|add|method|methodadd|verbose|pcap}";
629 "Usage: pjsip set logger {on|off|host <name/subnet>|add <name/subnet>|method <method>|methodadd <method>|verbose <on/off>|pcap <filename>}\n"
630 " Enables or disabling logging of SIP packets\n"
631 " read on ports bound to PJSIP transports either\n"
632 " globally or enables logging for an individual\n"
633 " host or particular SIP method(s).\n"
634 " Messages can be filtered by SIP request methods\n"
635 " INVITE, CANCEL, ACK, BYE, REGISTER, OPTION\n"
636 " SUBSCRIBE, NOTIFY, PUBLISH, INFO, and MESSAGE\n";
638 }
else if (cmd == CLI_GENERATE) {
639 if (a->argc && !strncasecmp(a->argv[e->
args - 1],
"method", 6)) {
645 what = a->argv[e->
args - 1];
647 if (a->argc == e->
args) {
648 if (!strcasecmp(what,
"on")) {
649 logger_cli_settings |= AST_PJSIP_LOGGER_ALL;
650 return pjsip_enable_logger_all(a->fd);
651 }
else if (!strcasecmp(what,
"off")) {
652 logger_cli_settings = AST_PJSIP_LOGGER_NONE;
653 return pjsip_disable_logger(a->fd);
655 }
else if (a->argc == e->
args + 1) {
656 if (!strcasecmp(what,
"host")) {
657 logger_cli_settings |= AST_PJSIP_LOGGER_HOST;
658 return pjsip_enable_logger_host(a->fd, a->argv[e->
args], 0);
659 }
else if (!strcasecmp(what,
"add")) {
660 logger_cli_settings |= AST_PJSIP_LOGGER_HOST;
661 return pjsip_enable_logger_host(a->fd, a->argv[e->
args], 1);
662 }
else if (!strcasecmp(what,
"method")) {
663 logger_cli_settings |= AST_PJSIP_LOGGER_METHOD;
664 return pjsip_enable_logger_method(a->fd, a->argv[e->
args], 0);
665 }
else if (!strcasecmp(what,
"methodadd")) {
666 logger_cli_settings |= AST_PJSIP_LOGGER_METHOD;
667 return pjsip_enable_logger_method(a->fd, a->argv[e->
args], 1);
668 }
else if (!strcasecmp(what,
"verbose")) {
669 logger_cli_settings |= AST_PJSIP_LOGGER_VERBOSE;
670 return pjsip_set_logger_verbose(a->fd, a->argv[e->
args]);
671 }
else if (!strcasecmp(what,
"pcap")) {
672 logger_cli_settings |= AST_PJSIP_LOGGER_PCAP;
673 return pjsip_set_logger_pcap(a->fd, a->argv[e->
args]);
677 return CLI_SHOWUSAGE;
681 AST_CLI_DEFINE(pjsip_set_logger,
"Enable/Disable PJSIP Logger Output")
684 static void check_debug(
void)
693 if (logger_cli_settings == AST_PJSIP_LOGGER_NONE || logger_cli_settings == AST_PJSIP_LOGGER_UNSET) {
695 pjsip_disable_logger(-1);
697 ast_debug(3,
"Leaving logger enabled since logging settings overridden using CLI\n");
699 logger_config_settings = AST_PJSIP_LOGGER_NONE;
705 if (logger_cli_settings != AST_PJSIP_LOGGER_UNSET) {
708 ast_debug(3,
"Leaving logger alone since logging has been overridden using CLI\n");
712 if (!(logger_config_settings & AST_PJSIP_LOGGER_ALL)) {
715 logger_config_settings |= AST_PJSIP_LOGGER_ALL;
716 pjsip_enable_logger_all(-1);
722 logger_config_settings = AST_PJSIP_LOGGER_HOST;
723 if (pjsip_enable_logger_host(-1,
debug, 0) != CLI_SUCCESS) {
724 ast_log(LOG_WARNING,
"Could not resolve host %s for debug "
729 static void global_reloaded(
const char *object_type)
738 static int load_module(
void)
741 ast_log(LOG_WARNING,
"Unable to add global observer\n");
745 default_logger = pjsip_logger_session_alloc();
746 if (!default_logger) {
748 ast_sip_get_sorcery(),
"global", &global_observer);
749 ast_log(LOG_WARNING,
"Unable to create default logger\n");
755 ast_sip_register_service(&logging_module);
761 static int unload_module(
void)
764 ast_sip_unregister_service(&logging_module);
767 ast_sip_get_sorcery(),
"global", &global_observer);
769 ao2_cleanup(default_logger);
770 default_logger = NULL;
775 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"PJSIP Packet Logger",
776 .support_level = AST_MODULE_SUPPORT_CORE,
778 .unload = unload_module,
780 .requires =
"res_pjsip",
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Asterisk main include file. File version handling, generic pbx functions.
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
char pcap_filename[PATH_MAX]
Filename used for the pcap file.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
unsigned int log_to_verbose
Whether to log to verbose or not.
struct pjsip_logger_session::@466 log_methods
Vector of SIP methods to log.
descriptor for a cli entry.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
static int debug
Global debug status.
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
enum ast_acl_sense ast_apply_ha(const struct ast_ha *ha, const struct ast_sockaddr *addr)
Apply a set of rules to a given IP address.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Socket address structure.
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
int args
This gets set in ast_cli_register()
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
unsigned int log_to_pcap
Whether to log to pcap or not.
internal representation of ACL entries In principle user applications would have no need for this...
char name[]
The PJSIP method structure used for comparisons.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Access Control of various sorts.
int ast_sockaddr_resolve_first_af(struct ast_sockaddr *addr, const char *name, int flag, int family)
Return the first entry from ast_sockaddr_resolve filtered by address family.
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_VECTOR(name, type)
Define a vector structure.
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Add an observer to a specific object type.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Support for dynamic strings.
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Interface for a sorcery object type observer.
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
#define ast_calloc(num, len)
A wrapper for calloc()
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
Vector container support.
void(* loaded)(const char *object_type)
Callback for when an object type is loaded/reloaded.
void ast_free_ha(struct ast_ha *ha)
Free a list of HAs.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
Remove an observer from a specific object type.
struct ast_ha * ast_append_ha_with_port(const char *sense, const char *stuff, struct ast_ha *path, int *error)
Add a new rule with optional port to a list of HAs.
FILE * pcap_file
The pcap file itself.
unsigned int enabled
Whether the session is enabled or not.
Standard Command Line Interface.
pjsip_method method
A PJSIP string for the method.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
unsigned int log_all_traffic
Whether the session is logging all traffic or not.
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
struct ast_ha * matches
Explicit addresses or ranges being logged.
#define AST_VECTOR_CALLBACK(vec, callback, default_value,...)
Execute a callback on every element in a vector returning the first matched.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.