Skip to content

Commit 99a4333

Browse files
InterLinked1seanbright
authored andcommitted
res_pjsip_logger: Add method-based logging option.
Expands the pjsip logger to support the ability to filter by SIP message method. This can make certain types of SIP debugging easier by only logging messages of particular method(s). ASTERISK-30146 #close Co-authored-by: Sean Bright <sean@seanbright.com> Change-Id: I9c8cbb6fc8686ef21190eb42e08bc9a9b147707f
1 parent 54c1fd9 commit 99a4333

File tree

2 files changed

+164
-8
lines changed

2 files changed

+164
-8
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Subject: res_pjsip_logger
2+
3+
SIP messages can now be filtered by SIP request method
4+
(INVITE, CANCEL, ACK, BYE, REGISTER, OPTION,
5+
SUBSCRIBE, NOTIFY, PUBLISH, INFO, and MESSAGE),
6+
allowing for more granular debugging to be done
7+
in the CLI. This applies to requests but not responses.

res/res_pjsip_logger.c

Lines changed: 157 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <pjsip.h>
3131

3232
#include "asterisk/res_pjsip.h"
33+
#include "asterisk/vector.h"
3334
#include "asterisk/module.h"
3435
#include "asterisk/logger.h"
3536
#include "asterisk/cli.h"
@@ -99,6 +100,12 @@ struct pcap_udp_header {
99100
uint16_t checksum; /*! \brief Packet checksum, left uncalculated for our purposes */
100101
};
101102

103+
struct method_logging_info {
104+
pj_str_t pj_name; /*! \brief A PJSIP string for the method */
105+
pjsip_method method; /*! \brief The PJSIP method structure used for comparisons */
106+
char name[]; /*! \brief The method name */
107+
};
108+
102109
/*! \brief PJSIP Logging Session */
103110
struct pjsip_logger_session {
104111
/*! \brief Explicit addresses or ranges being logged */
@@ -115,6 +122,8 @@ struct pjsip_logger_session {
115122
unsigned int log_to_verbose:1;
116123
/*! \brief Whether to log to pcap or not */
117124
unsigned int log_to_pcap:1;
125+
/*! \brief Vector of SIP methods to log */
126+
AST_VECTOR(, struct method_logging_info *) log_methods;
118127
};
119128

120129
/*! \brief The default logger session */
@@ -130,6 +139,9 @@ static void pjsip_logger_session_destroy(void *obj)
130139
}
131140

132141
ast_free_ha(session->matches);
142+
143+
AST_VECTOR_RESET(&session->log_methods, ast_free);
144+
AST_VECTOR_FREE(&session->log_methods);
133145
}
134146

135147
/*! \brief Allocator for logger session */
@@ -145,11 +157,35 @@ static struct pjsip_logger_session *pjsip_logger_session_alloc(void)
145157

146158
session->log_to_verbose = 1;
147159

160+
AST_VECTOR_INIT(&session->log_methods, 0);
161+
148162
return session;
149163
}
150164

151-
/*! \brief See if we pass debug IP filter */
152-
static inline int pjsip_log_test_addr(const struct pjsip_logger_session *session, const char *address, int port)
165+
/*! \note Must be called with the pjsip_logger_session lock held */
166+
static int apply_method_filter(const struct pjsip_logger_session *session, const pjsip_method *method)
167+
{
168+
size_t size = AST_VECTOR_SIZE(&session->log_methods);
169+
size_t i;
170+
171+
if (size == 0) {
172+
/* Nothing in the vector means everything matches */
173+
return 0;
174+
}
175+
176+
for (i = 0; i < size; ++i) {
177+
struct method_logging_info *candidate = AST_VECTOR_GET(&session->log_methods, i);
178+
if (pjsip_method_cmp(&candidate->method, method) == 0) {
179+
return 0;
180+
}
181+
}
182+
183+
/* Nothing matched */
184+
return 1;
185+
}
186+
187+
/*! \brief See if we pass debug filter */
188+
static inline int pjsip_log_test_filter(const struct pjsip_logger_session *session, const char *address, int port, const pjsip_method *method)
153189
{
154190
struct ast_sockaddr test_addr;
155191

@@ -161,9 +197,15 @@ static inline int pjsip_log_test_addr(const struct pjsip_logger_session *session
161197
return 1;
162198
}
163199

200+
if (apply_method_filter(session, method)) {
201+
/* The method filter didn't match anything, so reject. */
202+
return 0;
203+
}
204+
164205
/* A null address was passed in or no explicit matches. Just reject it. */
165206
if (ast_strlen_zero(address) || !session->matches) {
166-
return 0;
207+
/* If we matched on method and host is empty, accept, otherwise reject. */
208+
return AST_VECTOR_SIZE(&session->log_methods) > 0;
167209
}
168210

169211
ast_sockaddr_parse(&test_addr, address, PARSE_PORT_IGNORE);
@@ -270,7 +312,7 @@ static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
270312
char buffer[AST_SOCKADDR_BUFLEN];
271313

272314
ao2_rdlock(default_logger);
273-
if (!pjsip_log_test_addr(default_logger, tdata->tp_info.dst_name, tdata->tp_info.dst_port)) {
315+
if (!pjsip_log_test_filter(default_logger, tdata->tp_info.dst_name, tdata->tp_info.dst_port, &tdata->msg->line.req.method)) {
274316
ao2_unlock(default_logger);
275317
return PJ_SUCCESS;
276318
}
@@ -302,7 +344,7 @@ static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
302344
}
303345

304346
ao2_rdlock(default_logger);
305-
if (!pjsip_log_test_addr(default_logger, rdata->pkt_info.src_name, rdata->pkt_info.src_port)) {
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)) {
306348
ao2_unlock(default_logger);
307349
return PJ_FALSE;
308350
}
@@ -393,6 +435,93 @@ static char *pjsip_enable_logger_host(int fd, const char *arg, unsigned int add_
393435
return CLI_SUCCESS;
394436
}
395437

438+
static struct method_logging_info *method_logging_info_alloc(const char *method)
439+
{
440+
size_t method_bytes = strlen(method);
441+
struct method_logging_info *info;
442+
443+
info = ast_calloc(1, sizeof(struct method_logging_info) + method_bytes + 1);
444+
if (!info) {
445+
return NULL;
446+
}
447+
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);
451+
452+
return info;
453+
}
454+
455+
static int method_logging_info_cmp(const struct method_logging_info *element,
456+
const struct method_logging_info *candidate)
457+
{
458+
return pjsip_method_cmp(&element->method, &candidate->method) == 0
459+
? CMP_MATCH | CMP_STOP
460+
: 0;
461+
}
462+
463+
static int method_logging_info_sort_cmp(const void *a, const void *b)
464+
{
465+
const struct method_logging_info *const *m_a = a;
466+
const struct method_logging_info *const *m_b = b;
467+
return strcasecmp((*m_a)->name, (*m_b)->name);
468+
}
469+
470+
/*! \brief Add the current or an additional method to match for filtering */
471+
static char *pjsip_enable_logger_method(int fd, const char *arg, int add_method)
472+
{
473+
struct ast_str *str;
474+
struct method_logging_info *method;
475+
476+
method = method_logging_info_alloc(arg);
477+
if (!method) {
478+
return CLI_FAILURE;
479+
}
480+
481+
ao2_wrlock(default_logger);
482+
default_logger->enabled = 1;
483+
484+
if (!add_method) {
485+
/* Remove what already exists */
486+
AST_VECTOR_RESET(&default_logger->log_methods, ast_free);
487+
}
488+
489+
/* Already in the list? */
490+
if (AST_VECTOR_CALLBACK(&default_logger->log_methods, method_logging_info_cmp, NULL, method) != NULL) {
491+
ast_cli(fd, "Method '%s' is already enabled\n", method->name);
492+
ao2_unlock(default_logger);
493+
ast_free(method);
494+
return CLI_SUCCESS;
495+
}
496+
497+
if (AST_VECTOR_APPEND(&default_logger->log_methods, method)) {
498+
ast_log(LOG_ERROR, "Cannot register logger method '%s'. Unable to append.\n", method->name);
499+
ao2_unlock(default_logger);
500+
ast_free(method);
501+
return CLI_SUCCESS;
502+
}
503+
504+
AST_VECTOR_SORT(&default_logger->log_methods, method_logging_info_sort_cmp);
505+
506+
str = ast_str_create(256);
507+
if (str) {
508+
size_t i;
509+
for (i = 0; i < AST_VECTOR_SIZE(&default_logger->log_methods); i++) {
510+
method = AST_VECTOR_GET(&default_logger->log_methods, i);
511+
ast_str_append(&str, 0, "%s%.*s",
512+
ast_str_strlen(str) ? ", " : "",
513+
(int) method->pj_name.slen, method->pj_name.ptr);
514+
}
515+
516+
ast_cli(fd, "PJSIP Logging Enabled for SIP Methods: %s\n", ast_str_buffer(str));
517+
ast_free(str);
518+
}
519+
520+
ao2_unlock(default_logger);
521+
522+
return CLI_SUCCESS;
523+
}
524+
396525
static char *pjsip_disable_logger(int fd)
397526
{
398527
ao2_wrlock(default_logger);
@@ -404,6 +533,8 @@ static char *pjsip_disable_logger(int fd)
404533
default_logger->log_to_verbose = 1;
405534
default_logger->log_to_pcap = 0;
406535

536+
AST_VECTOR_RESET(&default_logger->log_methods, ast_free);
537+
407538
/* Stop logging to the PCAP file if active */
408539
if (default_logger->pcap_file) {
409540
fclose(default_logger->pcap_file);
@@ -469,18 +600,32 @@ static char *pjsip_set_logger_pcap(int fd, const char *arg)
469600

470601
static char *pjsip_set_logger(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
471602
{
603+
static const char * const method_choices[] = {
604+
"INVITE", "CANCEL", "ACK",
605+
"BYE", "REGISTER", "OPTION",
606+
"SUBSCRIBE", "NOTIFY", "PUBLISH",
607+
"INFO", "MESSAGE",
608+
NULL
609+
};
610+
472611
const char *what;
473612

474613
if (cmd == CLI_INIT) {
475-
e->command = "pjsip set logger {on|off|host|add|verbose|pcap}";
614+
e->command = "pjsip set logger {on|off|host|add|method|methodadd|verbose|pcap}";
476615
e->usage =
477-
"Usage: pjsip set logger {on|off|host <name/subnet>|add <name/subnet>|verbose <on/off>|pcap <filename>}\n"
616+
"Usage: pjsip set logger {on|off|host <name/subnet>|add <name/subnet>|method <method>|methodadd <method>|verbose <on/off>|pcap <filename>}\n"
478617
" Enables or disabling logging of SIP packets\n"
479618
" read on ports bound to PJSIP transports either\n"
480619
" globally or enables logging for an individual\n"
481-
" host.\n";
620+
" host or particular SIP method(s).\n"
621+
" Messages can be filtered by SIP request methods\n"
622+
" INVITE, CANCEL, ACK, BYE, REGISTER, OPTION\n"
623+
" SUBSCRIBE, NOTIFY, PUBLISH, INFO, and MESSAGE\n";
482624
return NULL;
483625
} else if (cmd == CLI_GENERATE) {
626+
if (a->argc && !strncasecmp(a->argv[e->args - 1], "method", 6)) {
627+
return ast_cli_complete(a->word, method_choices, a->n);
628+
}
484629
return NULL;
485630
}
486631

@@ -497,6 +642,10 @@ static char *pjsip_set_logger(struct ast_cli_entry *e, int cmd, struct ast_cli_a
497642
return pjsip_enable_logger_host(a->fd, a->argv[e->args], 0);
498643
} else if (!strcasecmp(what, "add")) {
499644
return pjsip_enable_logger_host(a->fd, a->argv[e->args], 1);
645+
} else if (!strcasecmp(what, "method")) {
646+
return pjsip_enable_logger_method(a->fd, a->argv[e->args], 0);
647+
} else if (!strcasecmp(what, "methodadd")) {
648+
return pjsip_enable_logger_method(a->fd, a->argv[e->args], 1);
500649
} else if (!strcasecmp(what, "verbose")) {
501650
return pjsip_set_logger_verbose(a->fd, a->argv[e->args]);
502651
} else if (!strcasecmp(what, "pcap")) {

0 commit comments

Comments
 (0)