Asterisk - The Open Source Telephony Project  21.4.1
res_pjsip_logger.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Mark Michelson <mmichelson@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*** MODULEINFO
20  <depend>pjproject</depend>
21  <depend>res_pjsip</depend>
22  <defaultenabled>yes</defaultenabled>
23  <support_level>core</support_level>
24  ***/
25 
26 #include "asterisk.h"
27 
28 #include <netinet/in.h> /* For IPPROTO_UDP and in6_addr */
29 
30 #include <pjsip.h>
31 
32 #include "asterisk/res_pjsip.h"
33 #include "asterisk/vector.h"
34 #include "asterisk/module.h"
35 #include "asterisk/logger.h"
36 #include "asterisk/cli.h"
37 #include "asterisk/netsock2.h"
38 #include "asterisk/acl.h"
39 
40 /*! \brief PCAP Header */
41 struct pcap_header {
42  uint32_t magic_number; /*! \brief PCAP file format magic number */
43  uint16_t version_major; /*! \brief Major version number of the file format */
44  uint16_t version_minor; /*! \brief Minor version number of the file format */
45  int32_t thiszone; /*! \brief GMT to local correction */
46  uint32_t sigfigs; /*! \brief Accuracy of timestamps */
47  uint32_t snaplen; /*! \brief The maximum size that can be recorded in the file */
48  uint32_t network; /*! \brief Type of packets held within the file */
49 };
50 
51 /*! \brief PCAP Packet Record Header */
53  uint32_t ts_sec; /*! \brief When the record was created */
54  uint32_t ts_usec; /*! \brief When the record was created */
55  uint32_t incl_len; /*! \brief Length of packet as saved in the file */
56  uint32_t orig_len; /*! \brief Length of packet as sent over network */
57 };
58 
59 /*! \brief PCAP Ethernet Header */
61  uint8_t dst[6]; /*! \brief Destination MAC address */
62  uint8_t src[6]; /*! \brief Source MAD address */
63  uint16_t type; /*! \brief The type of packet contained within */
64 } __attribute__((__packed__));
65 
66 /*! \brief PCAP IPv4 Header */
68  uint8_t ver_ihl; /*! \brief IP header version and other bits */
69  uint8_t ip_tos; /*! \brief Type of service details */
70  uint16_t ip_len; /*! \brief Total length of the packet (including IPv4 header) */
71  uint16_t ip_id; /*! \brief Identification value */
72  uint16_t ip_off; /*! \brief Fragment offset */
73  uint8_t ip_ttl; /*! \brief Time to live for the packet */
74  uint8_t ip_protocol; /*! \brief Protocol of the data held within the packet (always UDP) */
75  uint16_t ip_sum; /*! \brief Checksum (not calculated for our purposes */
76  uint32_t ip_src; /*! \brief Source IP address */
77  uint32_t ip_dst; /*! \brief Destination IP address */
78 };
79 
80 /*! \brief PCAP IPv6 Header */
82  union {
83  struct ip6_hdrctl {
84  uint32_t ip6_un1_flow; /*! \brief Version, traffic class, flow label */
85  uint16_t ip6_un1_plen; /*! \brief Length of the packet (not including IPv6 header) */
86  uint8_t ip6_un1_nxt; /*! \brief Next header field */
87  uint8_t ip6_un1_hlim; /*! \brief Hop Limit */
88  } ip6_un1;
89  uint8_t ip6_un2_vfc; /*! \brief Version, traffic class */
90  } ip6_ctlun;
91  struct in6_addr ip6_src; /*! \brief Source IP address */
92  struct in6_addr ip6_dst; /*! \brief Destination IP address */
93 };
94 
95 /*! \brief PCAP UDP Header */
97  uint16_t src; /*! \brief Source IP port */
98  uint16_t dst; /*! \brief Destination IP port */
99  uint16_t length; /*! \brief Length of the UDP header plus UDP packet */
100  uint16_t checksum; /*! \brief Packet checksum, left uncalculated for our purposes */
101 };
102 
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 
109 /*! \brief PJSIP Logging Session */
111  /*! \brief Explicit addresses or ranges being logged */
112  struct ast_ha *matches;
113  /*! \brief Filename used for the pcap file */
114  char pcap_filename[PATH_MAX];
115  /*! \brief The pcap file itself */
116  FILE *pcap_file;
117  /*! \brief Whether the session is enabled or not */
118  unsigned int enabled:1;
119  /*! \brief Whether the session is logging all traffic or not */
120  unsigned int log_all_traffic:1;
121  /*! \brief Whether to log to verbose or not */
122  unsigned int log_to_verbose:1;
123  /*! \brief Whether to log to pcap or not */
124  unsigned int log_to_pcap:1;
125  /*! \brief Vector of SIP methods to log */
127 };
128 
129 /*! \brief The default logger session */
130 static struct pjsip_logger_session *default_logger;
131 
132 /*! \brief Destructor for logger session */
133 static void pjsip_logger_session_destroy(void *obj)
134 {
135  struct pjsip_logger_session *session = obj;
136 
137  if (session->pcap_file) {
138  fclose(session->pcap_file);
139  }
140 
141  ast_free_ha(session->matches);
142 
143  AST_VECTOR_RESET(&session->log_methods, ast_free);
144  AST_VECTOR_FREE(&session->log_methods);
145 }
146 
147 /*! \brief Allocator for logger session */
148 static struct pjsip_logger_session *pjsip_logger_session_alloc(void)
149 {
150  struct pjsip_logger_session *session;
151 
152  session = ao2_alloc_options(sizeof(struct pjsip_logger_session), pjsip_logger_session_destroy,
154  if (!session) {
155  return NULL;
156  }
157 
158  session->log_to_verbose = 1;
159 
160  AST_VECTOR_INIT(&session->log_methods, 0);
161 
162  return session;
163 }
164 
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)
189 {
190  struct ast_sockaddr test_addr;
191 
192  if (!session->enabled) {
193  return 0;
194  }
195 
196  if (session->log_all_traffic) {
197  return 1;
198  }
199 
200  if (apply_method_filter(session, method)) {
201  /* The method filter didn't match anything, so reject. */
202  return 0;
203  }
204 
205  /* A null address was passed in or no explicit matches. Just reject it. */
206  if (ast_strlen_zero(address) || !session->matches) {
207  /* If we matched on method and host is empty, accept, otherwise reject. */
208  return AST_VECTOR_SIZE(&session->log_methods) > 0;
209  }
210 
211  ast_sockaddr_parse(&test_addr, address, PARSE_PORT_IGNORE);
212  ast_sockaddr_set_port(&test_addr, port);
213 
214  /* Compare the address against the matches */
215  if (ast_apply_ha(session->matches, &test_addr) != AST_SENSE_ALLOW) {
216  return 1;
217  } else {
218  return 0;
219  }
220 }
221 
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)
224 {
225  struct timeval now = ast_tvnow();
227  .ts_sec = now.tv_sec,
228  .ts_usec = now.tv_usec,
229  };
231  .type = 0,
232  };
234  .ver_ihl = 0x45, /* IPv4 + 20 bytes of header */
235  .ip_ttl = 128, /* We always put a TTL of 128 to keep Wireshark less blue */
236  };
238  .ip6_ctlun.ip6_un2_vfc = 0x60,
239  };
240  void *pcap_ip_header;
241  size_t pcap_ip_header_len;
243 
244  /* Packets are always stored as UDP to simplify this logic */
245  if (source) {
246  pcap_udp_header.src = ntohs(pj_sockaddr_get_port(source));
247  } else {
248  pcap_udp_header.src = ntohs(0);
249  }
250  if (destination) {
251  pcap_udp_header.dst = ntohs(pj_sockaddr_get_port(destination));
252  } else {
253  pcap_udp_header.dst = ntohs(0);
254  }
255  pcap_udp_header.length = ntohs(sizeof(struct pcap_udp_header) + msg_len);
256 
257  /* Construct the appropriate IP header */
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); /* We are providing an IPv4 packet */
261  pcap_ip_header = &pcap_ipv4_header;
262  pcap_ip_header_len = sizeof(struct pcap_ipv4_header);
263  if (source) {
264  memcpy(&pcap_ipv4_header.ip_src, pj_sockaddr_get_addr(source), pj_sockaddr_get_addr_len(source));
265  }
266  if (destination) {
267  memcpy(&pcap_ipv4_header.ip_dst, pj_sockaddr_get_addr(destination), pj_sockaddr_get_addr_len(destination));
268  }
269  pcap_ipv4_header.ip_len = htons(sizeof(struct pcap_udp_header) + sizeof(struct pcap_ipv4_header) + msg_len);
270  pcap_ipv4_header.ip_protocol = IPPROTO_UDP; /* We always provide UDP */
271  } else {
272  pcap_ethernet_header.type = htons(0x86DD); /* We are providing an IPv6 packet */
273  pcap_ip_header = &pcap_ipv6_header;
274  pcap_ip_header_len = sizeof(struct pcap_ipv6_header);
275  if (source) {
276  memcpy(&pcap_ipv6_header.ip6_src, pj_sockaddr_get_addr(source), pj_sockaddr_get_addr_len(source));
277  }
278  if (destination) {
279  memcpy(&pcap_ipv6_header.ip6_dst, pj_sockaddr_get_addr(destination), pj_sockaddr_get_addr_len(destination));
280  }
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;
283  }
284 
285  /* Add up all the sizes for this record */
286  pcap_record_header.incl_len = pcap_record_header.orig_len = sizeof(pcap_ethernet_header) + pcap_ip_header_len + sizeof(pcap_udp_header) + msg_len;
287 
288  /* We lock the logger session since we're writing these out in parts */
289  ao2_wrlock(session);
290  if (session->pcap_file) {
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));
293  }
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));
296  }
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));
299  }
300  if (fwrite(&pcap_udp_header, sizeof(struct pcap_udp_header), 1, session->pcap_file) != 1) {
301  ast_log(LOG_WARNING, "Writing UDP header to pcap failed: %s\n", strerror(errno));
302  }
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));
305  }
306  }
307  ao2_unlock(session);
308 }
309 
310 static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
311 {
312  char buffer[AST_SOCKADDR_BUFLEN];
313 
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);
317  return PJ_SUCCESS;
318  }
319  ao2_unlock(default_logger);
320 
321  if (default_logger->log_to_verbose) {
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);
328  }
329 
330  if (default_logger->log_to_pcap) {
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);
333  }
334 
335  return PJ_SUCCESS;
336 }
337 
338 static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
339 {
340  char buffer[AST_SOCKADDR_BUFLEN];
341 
342  if (!rdata->msg_info.msg) {
343  return PJ_FALSE;
344  }
345 
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);
349  return PJ_FALSE;
350  }
351  ao2_unlock(default_logger);
352 
353  if (default_logger->log_to_verbose) {
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",
356  rdata->msg_info.len,
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);
360  }
361 
362  if (default_logger->log_to_pcap) {
363  pjsip_logger_write_to_pcap(default_logger, rdata->pkt_info.packet, rdata->msg_info.len,
364  &rdata->pkt_info.src_addr, NULL);
365  }
366 
367  return PJ_FALSE;
368 }
369 
370 static pjsip_module logging_module = {
371  .name = { "Logging Module", 14 },
372  .priority = 0,
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,
377 };
378 
379 static char *pjsip_enable_logger_all(int fd)
380 {
381  ao2_wrlock(default_logger);
382  default_logger->enabled = 1;
383  default_logger->log_all_traffic = 1;
384  ao2_unlock(default_logger);
385 
386  if (fd >= 0) {
387  ast_cli(fd, "PJSIP Logging enabled\n");
388  }
389 
390  return CLI_SUCCESS;
391 }
392 
393 static char *pjsip_enable_logger_host(int fd, const char *arg, unsigned int add_host)
394 {
395  const char *host = arg;
396  char *mask;
397  struct ast_sockaddr address;
398  int error = 0;
399 
400  ao2_wrlock(default_logger);
401  default_logger->enabled = 1;
402 
403  if (!add_host) {
404  /* If this is not adding an additional host or subnet then we have to
405  * remove what already exists.
406  */
407  ast_free_ha(default_logger->matches);
408  default_logger->matches = NULL;
409  }
410 
411  mask = strrchr(host, '/');
412  if (!mask && !ast_sockaddr_parse(&address, arg, 0)) {
413  if (ast_sockaddr_resolve_first_af(&address, arg, 0, AST_AF_UNSPEC)) {
414  ao2_unlock(default_logger);
415  return CLI_SHOWUSAGE;
416  }
417  host = ast_sockaddr_stringify(&address);
418  }
419 
420  default_logger->matches = ast_append_ha_with_port("d", host, default_logger->matches, &error);
421  if (!default_logger->matches || error) {
422  if (fd >= 0) {
423  ast_cli(fd, "Failed to add address '%s' for logging\n", host);
424  }
425  ao2_unlock(default_logger);
426  return CLI_SUCCESS;
427  }
428 
429  ao2_unlock(default_logger);
430 
431  if (fd >= 0) {
432  ast_cli(fd, "PJSIP Logging Enabled for host: %s\n", ast_sockaddr_stringify_addr(&address));
433  }
434 
435  return CLI_SUCCESS;
436 }
437 
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 
525 static char *pjsip_disable_logger(int fd)
526 {
527  ao2_wrlock(default_logger);
528 
529  /* Default the settings back to the way they were */
530  default_logger->enabled = 0;
531  default_logger->log_all_traffic = 0;
532  default_logger->pcap_filename[0] = '\0';
533  default_logger->log_to_verbose = 1;
534  default_logger->log_to_pcap = 0;
535 
536  AST_VECTOR_RESET(&default_logger->log_methods, ast_free);
537 
538  /* Stop logging to the PCAP file if active */
539  if (default_logger->pcap_file) {
540  fclose(default_logger->pcap_file);
541  default_logger->pcap_file = NULL;
542  }
543 
544  ast_free_ha(default_logger->matches);
545  default_logger->matches = NULL;
546 
547  ao2_unlock(default_logger);
548 
549  if (fd >= 0) {
550  ast_cli(fd, "PJSIP Logging disabled\n");
551  }
552 
553  return CLI_SUCCESS;
554 }
555 
556 static char *pjsip_set_logger_verbose(int fd, const char *arg)
557 {
558  ao2_wrlock(default_logger);
559  default_logger->log_to_verbose = ast_true(arg);
560  ao2_unlock(default_logger);
561 
562  ast_cli(fd, "PJSIP Logging to verbose has been %s\n", ast_true(arg) ? "enabled" : "disabled");
563 
564  return CLI_SUCCESS;
565 }
566 
567 static char *pjsip_set_logger_pcap(int fd, const char *arg)
568 {
569  struct pcap_header pcap_header = {
570  .magic_number = 0xa1b2c3d4,
571  .version_major = 2,
572  .version_minor = 4,
573  .snaplen = 65535,
574  .network = 1, /* We always use ethernet so we can combine IPv4 and IPv6 in same pcap */
575  };
576 
577  ao2_wrlock(default_logger);
578  ast_copy_string(default_logger->pcap_filename, arg, sizeof(default_logger->pcap_filename));
579 
580  if (default_logger->pcap_file) {
581  fclose(default_logger->pcap_file);
582  default_logger->pcap_file = NULL;
583  }
584 
585  default_logger->pcap_file = fopen(arg, "wb");
586  if (!default_logger->pcap_file) {
587  ao2_unlock(default_logger);
588  ast_cli(fd, "Failed to open file '%s' for pcap writing\n", arg);
589  return CLI_SUCCESS;
590  }
591  fwrite(&pcap_header, 1, sizeof(struct pcap_header), default_logger->pcap_file);
592 
593  default_logger->log_to_pcap = 1;
594  ao2_unlock(default_logger);
595 
596  ast_cli(fd, "PJSIP logging to pcap file '%s'\n", arg);
597 
598  return CLI_SUCCESS;
599 }
600 
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),
609 };
610 
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;
613 
614 static char *pjsip_set_logger(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
615 {
616  static const char * const method_choices[] = {
617  "INVITE", "CANCEL", "ACK",
618  "BYE", "REGISTER", "OPTION",
619  "SUBSCRIBE", "NOTIFY", "PUBLISH",
620  "INFO", "MESSAGE",
621  NULL
622  };
623 
624  const char *what;
625 
626  if (cmd == CLI_INIT) {
627  e->command = "pjsip set logger {on|off|host|add|method|methodadd|verbose|pcap}";
628  e->usage =
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";
637  return NULL;
638  } else if (cmd == CLI_GENERATE) {
639  if (a->argc && !strncasecmp(a->argv[e->args - 1], "method", 6)) {
640  return ast_cli_complete(a->word, method_choices, a->n);
641  }
642  return NULL;
643  }
644 
645  what = a->argv[e->args - 1]; /* Guaranteed to exist */
646 
647  if (a->argc == e->args) { /* on/off */
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);
654  }
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]);
674  }
675  }
676 
677  return CLI_SHOWUSAGE;
678 }
679 
680 static struct ast_cli_entry cli_pjsip[] = {
681  AST_CLI_DEFINE(pjsip_set_logger, "Enable/Disable PJSIP Logger Output")
682 };
683 
684 static void check_debug(void)
685 {
686  RAII_VAR(char *, debug, ast_sip_get_debug(), ast_free);
687 
688  /* Got directive to disable debug */
689  if (ast_false(debug)) {
690  /* If the logger was enabled via the CLI instead of through the config file,
691  * then we shouldn't disable it on a reload.
692  * Only disable logging if logging isn't enabled via the CLI. */
693  if (logger_cli_settings == AST_PJSIP_LOGGER_NONE || logger_cli_settings == AST_PJSIP_LOGGER_UNSET) {
694  /* Logger not enabled via CLI currently so good to go ahead and disable. */
695  pjsip_disable_logger(-1);
696  } else {
697  ast_debug(3, "Leaving logger enabled since logging settings overridden using CLI\n");
698  }
699  logger_config_settings = AST_PJSIP_LOGGER_NONE;
700  return;
701  }
702 
703  /* Got directive to enable debug */
704  if (ast_true(debug)) {
705  if (logger_cli_settings != AST_PJSIP_LOGGER_UNSET) {
706  /* Logging was modified using the CLI command,
707  * and this overrides the default from the config. */
708  ast_debug(3, "Leaving logger alone since logging has been overridden using CLI\n");
709  return;
710  }
711  /* If logger already enabled via config, then nothing has changed. */
712  if (!(logger_config_settings & AST_PJSIP_LOGGER_ALL)) {
713  /* Logging was not previously enabled via config,
714  * but has been enabled via CLI. */
715  logger_config_settings |= AST_PJSIP_LOGGER_ALL;
716  pjsip_enable_logger_all(-1);
717  }
718  return;
719  }
720 
721  /* Enabling debug, only for specific host */
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 "
725  "logging\n", debug);
726  }
727 }
728 
729 static void global_reloaded(const char *object_type)
730 {
731  check_debug();
732 }
733 
734 static const struct ast_sorcery_observer global_observer = {
735  .loaded = global_reloaded
736 };
737 
738 static int load_module(void)
739 {
740  if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer)) {
741  ast_log(LOG_WARNING, "Unable to add global observer\n");
743  }
744 
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");
751  }
752 
753  check_debug();
754 
755  ast_sip_register_service(&logging_module);
756  ast_cli_register_multiple(cli_pjsip, ARRAY_LEN(cli_pjsip));
757 
759 }
760 
761 static int unload_module(void)
762 {
763  ast_cli_unregister_multiple(cli_pjsip, ARRAY_LEN(cli_pjsip));
764  ast_sip_unregister_service(&logging_module);
765 
767  ast_sip_get_sorcery(), "global", &global_observer);
768 
769  ao2_cleanup(default_logger);
770  default_logger = NULL;
771 
772  return 0;
773 }
774 
775 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Packet Logger",
776  .support_level = AST_MODULE_SUPPORT_CORE,
777  .load = load_module,
778  .unload = unload_module,
779  .load_pri = AST_MODPRI_APP_DEPEND,
780  .requires = "res_pjsip",
781 );
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
Definition: netsock2.h:286
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
uint16_t version_minor
Major version number of the file format.
PCAP Header.
Asterisk main include file. File version handling, generic pbx functions.
uint32_t ip_src
Checksum (not calculated for our purposes.
uint8_t src[6]
Destination MAC address.
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
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.
Definition: clicompat.c:30
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.
uint16_t version_major
PCAP file format magic number.
descriptor for a cli entry.
Definition: cli.h:171
uint32_t orig_len
Length of packet as saved in the file.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static int debug
Global debug status.
Definition: res_xmpp.c:441
uint32_t network
The maximum size that can be recorded in the file.
struct in6_addr ip6_dst
Source IP address.
uint16_t dst
Source IP port.
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
uint16_t type
Source MAD address.
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.
Definition: acl.c:807
uint8_t ip_ttl
Fragment offset.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
PCAP Packet Record Header.
uint16_t ip_sum
Protocol of the data held within the packet (always UDP)
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
uint32_t incl_len
When the record was created.
uint16_t ip_off
Identification value.
uint8_t ip_protocol
Time to live for the packet.
int32_t thiszone
Minor version number of the file format.
uint16_t checksum
Length of the UDP header plus UDP packet.
Socket address structure.
Definition: netsock2.h:97
PJSIP Logging Session.
uint32_t sigfigs
GMT to local correction.
#define AST_VECTOR_SORT(vec, cmp)
Sort a vector in-place.
Definition: vector.h:396
int args
This gets set in ast_cli_register()
Definition: cli.h:185
uint16_t ip6_un1_plen
Version, traffic class, flow label.
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
Definition: main/cli.c:1846
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...
Definition: acl.h:51
char name[]
The PJSIP method structure used for comparisons.
uint16_t length
Destination IP port.
uint16_t ip_len
Type of service details.
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
uint32_t ts_usec
When the record was created.
uint32_t ip_dst
Source IP address.
Access Control of various sorts.
uint8_t ip6_un1_nxt
Length of the packet (not including IPv6 header)
PCAP IPv6 Header.
PCAP IPv4 Header.
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.
Definition: netsock2.c:337
uint32_t snaplen
Accuracy of timestamps.
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
Network socket handling.
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.
Definition: sorcery.c:2391
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".
Definition: utils.c:2199
Support for dynamic strings.
Definition: strings.h:623
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
Interface for a sorcery object type observer.
Definition: sorcery.h:332
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
uint8_t ip_tos
IP header version and other bits.
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
Definition: vector.h:625
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.
Definition: module.h:78
Vector container support.
void(* loaded)(const char *object_type)
Callback for when an object type is loaded/reloaded.
Definition: sorcery.h:343
const char * usage
Definition: cli.h:177
void ast_free_ha(struct ast_ha *ha)
Free a list of HAs.
Definition: acl.c:222
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680
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.
Definition: sorcery.c:2423
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.
Definition: acl.c:717
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.
Definition: strings.h:425
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"...
Definition: utils.c:2216
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.
Definition: vector.h:765
PCAP UDP Header.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
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.
Definition: utils.h:941
uint16_t ip_id
Total length of the packet (including IPv4 header)
uint8_t ip6_un1_hlim
Next header field.
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
PCAP Ethernet Header.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659