Asterisk - The Open Source Telephony Project  18.5.0
res_stun_monitor.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * David Vossel <[email protected]>
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 /*!
20  * \file
21  * \brief STUN Network Monitor
22  *
23  * \author David Vossel <[email protected]>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include "asterisk/module.h"
33 #include "asterisk/sched.h"
34 #include "asterisk/config.h"
35 #include "asterisk/stun.h"
36 #include "asterisk/netsock2.h"
37 #include "asterisk/lock.h"
38 #include "asterisk/acl.h"
39 #include "asterisk/cli.h"
40 #include "asterisk/json.h"
41 #include "asterisk/stasis.h"
42 #include "asterisk/stasis_system.h"
43 #include "asterisk/astobj2.h"
44 
45 #include <fcntl.h>
46 
47 #define DEFAULT_MONITOR_REFRESH 30 /*!< Default refresh period in seconds */
48 #define DEFAULT_RETRIES 3 /*!< retries shown in stun show status
49  matching static retries in stun.c */
50 
51 static const char stun_conf_file[] = "res_stun_monitor.conf";
52 static struct ast_sched_context *sched;
53 
54 static struct {
55  /*! STUN monitor protection lock. */
57  /*! Current perceived external address. */
58  struct sockaddr_in external_addr;
59  /*! STUN server host name. */
60  const char *server_hostname;
61  /*! Port of STUN server to use */
62  unsigned int stun_port;
63  /*! Number of seconds between polls to the STUN server for the external address. */
64  unsigned int refresh;
65  /*! Monitoring STUN socket. */
66  int stun_sock;
67  /*! TRUE if the STUN monitor is enabled. */
68  unsigned int monitor_enabled:1;
69  /*! TRUE if the perceived external address is valid/known. */
70  unsigned int external_addr_known:1;
71  /*! TRUE if we have already griped about a STUN poll failing. */
72  unsigned int stun_poll_failed_gripe:1;
73 } args;
74 
75 static void stun_close_sock(void)
76 {
77  if (0 <= args.stun_sock) {
78  close(args.stun_sock);
79  args.stun_sock = -1;
80  }
81 }
82 
83 /* \brief called by scheduler to send STUN request */
84 static int stun_monitor_request(const void *blarg)
85 {
86  int res;
87  struct sockaddr_in answer;
88  static const struct sockaddr_in no_addr = { 0, };
89 
90  ast_mutex_lock(&args.lock);
91  if (!args.monitor_enabled) {
92  goto monitor_request_cleanup;
93  }
94 
95  if (args.stun_sock < 0) {
96  struct ast_sockaddr stun_addr;
97 
98  /* STUN socket not open. Refresh the server DNS address resolution. */
99  if (!args.server_hostname) {
100  /* No STUN hostname? */
101  goto monitor_request_cleanup;
102  }
103 
104  /* Lookup STUN address. */
105  memset(&stun_addr, 0, sizeof(stun_addr));
106  stun_addr.ss.ss_family = AF_INET;
107  if (ast_get_ip(&stun_addr, args.server_hostname)) {
108  /* Lookup failed. */
109  ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n",
110  args.server_hostname);
111  goto monitor_request_cleanup;
112  }
113  ast_sockaddr_set_port(&stun_addr, args.stun_port);
114 
115  /* open socket binding */
116  args.stun_sock = socket(AF_INET, SOCK_DGRAM, 0);
117  if (args.stun_sock < 0) {
118  ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
119  goto monitor_request_cleanup;
120  }
121  if (ast_connect(args.stun_sock, &stun_addr)) {
122  ast_log(LOG_WARNING, "STUN Failed to connect to %s: %s\n",
123  ast_sockaddr_stringify(&stun_addr), strerror(errno));
124  stun_close_sock();
125  goto monitor_request_cleanup;
126  }
127  }
128 
129  res = ast_stun_request(args.stun_sock, NULL, NULL, &answer);
130  if (res) {
131  /*
132  * STUN request timed out or errored.
133  *
134  * Refresh the server DNS address resolution next time around.
135  */
136  if (!args.stun_poll_failed_gripe) {
137  args.stun_poll_failed_gripe = 1;
138  ast_log(LOG_WARNING, "STUN poll %s. Re-evaluating STUN server address.\n",
139  res < 0 ? "failed" : "got no response");
140  }
141  stun_close_sock();
142  } else {
143  args.stun_poll_failed_gripe = 0;
144  if (memcmp(&no_addr, &answer, sizeof(no_addr))
145  && memcmp(&args.external_addr, &answer, sizeof(args.external_addr))) {
146  const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
147  int newport = ntohs(answer.sin_port);
148 
149  ast_log(LOG_NOTICE, "Old external address/port %s:%d now seen as %s:%d.\n",
150  ast_inet_ntoa(args.external_addr.sin_addr),
151  ntohs(args.external_addr.sin_port), newaddr, newport);
152 
153  args.external_addr = answer;
154 
155  if (args.external_addr_known) {
156  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
157  RAII_VAR(struct ast_json_payload *, json_payload, NULL, ao2_cleanup);
158  RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
159 
160  if (!ast_network_change_type()) {
161  goto publish_failure;
162  }
163 
164  /* This json_object doesn't actually contain anything yet. We have to reference something
165  * for stasis, and this is useful for if we want to ever add data for any reason. */
166  json_object = ast_json_object_create();
167  if (!json_object) {
168  goto publish_failure;
169  }
170 
171  if (!(json_payload = ast_json_payload_create(json_object))) {
172  goto publish_failure;
173  }
174 
175  msg = stasis_message_create(ast_network_change_type(), json_payload);
176 
177 publish_failure:
178  if (msg) {
180  } else {
181  ast_log(LOG_ERROR, "Failed to issue network change message.\n");
182  }
183  } else {
184  /* this was the first external address we found, do not alert listeners
185  * until this address changes to something else. */
186  args.external_addr_known = 1;
187  }
188  }
189  }
190 
191 monitor_request_cleanup:
192  /* always refresh this scheduler item. It will be removed elsewhere when
193  * it is supposed to go away */
194  res = args.refresh * 1000;
195  ast_mutex_unlock(&args.lock);
196 
197  return res;
198 }
199 
200 /*!
201  * \internal
202  * \brief Stops the STUN monitor thread.
203  *
204  * \note do not hold the args->lock while calling this
205  *
206  * \return Nothing
207  */
208 static void stun_stop_monitor(void)
209 {
210  ast_mutex_lock(&args.lock);
211  args.monitor_enabled = 0;
212  ast_free((char *) args.server_hostname);
213  args.server_hostname = NULL;
214  stun_close_sock();
215  ast_mutex_unlock(&args.lock);
216 
217  if (sched) {
219  sched = NULL;
220  ast_log(LOG_NOTICE, "STUN monitor stopped\n");
221  }
222 }
223 
224 /*!
225  * \internal
226  * \brief Starts the STUN monitor thread.
227  *
228  * \note The args->lock MUST be held when calling this function
229  *
230  * \return Nothing
231  */
232 static int stun_start_monitor(void)
233 {
234  /* if scheduler thread is not started, make sure to start it now */
235  if (sched) {
236  return 0; /* already started */
237  }
238 
239  if (!(sched = ast_sched_context_create())) {
240  ast_log(LOG_ERROR, "Failed to create stun monitor scheduler context\n");
241  return -1;
242  }
243 
244  if (ast_sched_start_thread(sched)) {
246  sched = NULL;
247  stun_close_sock();
248  return -1;
249  }
250 
251  if (ast_sched_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
252  ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
254  sched = NULL;
255  return -1;
256  }
257 
258  ast_log(LOG_NOTICE, "STUN monitor started\n");
259 
260  return 0;
261 }
262 
263 /*!
264  * \internal
265  * \brief Parse and setup the stunaddr parameter.
266  *
267  * \param value Configuration parameter variable value.
268  *
269  * \retval 0 on success.
270  * \retval -1 on error.
271  */
272 static int setup_stunaddr(const char *value, int reload)
273 {
274  char *val;
275  char *host_str;
276  char *port_str;
277  unsigned int port;
278  struct ast_sockaddr stun_addr;
279 
280  if (ast_strlen_zero(value)) {
281  /* Setting to an empty value disables STUN monitoring. */
282  args.monitor_enabled = 0;
283  return 0;
284  }
285 
286  val = ast_strdupa(value);
287  if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
288  || ast_strlen_zero(host_str)) {
289  return -1;
290  }
291 
292  /* Determine STUN port */
293  if (ast_strlen_zero(port_str)
294  || 1 != sscanf(port_str, "%30u", &port)) {
295  port = STANDARD_STUN_PORT;
296  }
297 
298  host_str = ast_strdup(host_str);
299  if (!host_str) {
300  return -1;
301  }
302 
303  /* Lookup STUN address. */
304  memset(&stun_addr, 0, sizeof(stun_addr));
305  stun_addr.ss.ss_family = AF_INET;
306  if (ast_get_ip(&stun_addr, host_str)) {
307  ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
308 
309  /* Only treat this as fatal if we are reloading */
310  if (reload) {
311  ast_free(host_str);
312  return -1;
313  }
314  }
315 
316  /* Save STUN server information. */
317  ast_free((char *) args.server_hostname);
318  args.server_hostname = host_str;
319  args.stun_port = port;
320 
321  /* Enable STUN monitor */
322  args.monitor_enabled = 1;
323  return 0;
324 }
325 
326 static int load_config(int startup)
327 {
328  struct ast_flags config_flags = { 0, };
329  struct ast_config *cfg;
330  struct ast_variable *v;
331 
332  if (!startup) {
333  ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
334  }
335 
336  cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
337  if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
338  ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
339  return -1;
340  }
341  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
342  return 0;
343  }
344 
345  /* clean up any previous open socket */
346  stun_close_sock();
347  args.stun_poll_failed_gripe = 0;
348 
349  /* set defaults */
350  args.monitor_enabled = 0;
351  args.refresh = DEFAULT_MONITOR_REFRESH;
352 
353  for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
354  if (!strcasecmp(v->name, "stunaddr")) {
355  if (setup_stunaddr(v->value, !startup)) {
356  ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
357  v->value, v->lineno);
358  }
359  } else if (!strcasecmp(v->name, "stunrefresh")) {
360  if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
361  ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
362  args.refresh = DEFAULT_MONITOR_REFRESH;
363  }
364  } else {
365  ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
366  v->value, v->lineno);
367  }
368  }
369 
370  ast_config_destroy(cfg);
371 
372  return 0;
373 }
374 
375 /*! \brief Execute stun show status command */
376 static void _stun_show_status(int fd)
377 {
378  const char *status;
379 
380 #define DATALN "%-25s %-5u %-7u %-8d %-7s %-16s %-d\n"
381 #define HEADER "%-25s %-5s %-7s %-8s %-7s %-16s %-s\n"
382 
383  /*! we only have one stun server, but start to play well with more */
384  ast_cli(fd, HEADER, "Hostname", "Port", "Period", "Retries", "Status", "ExternAddr", "ExternPort");
385 
386  if (args.stun_poll_failed_gripe) {
387  status = "FAIL";
388  } else if (args.external_addr_known) {
389  status = "OK";
390  } else {
391  status = "INIT";
392  }
393  ast_cli( fd, DATALN,
394  args.server_hostname,
395  args.stun_port,
396  args.refresh,
398  status,
399  ast_inet_ntoa(args.external_addr.sin_addr),
400  ntohs(args.external_addr.sin_port)
401  );
402 
403 #undef HEADER
404 #undef DATALN
405 }
406 
407 static char *handle_cli_stun_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
408 {
409  switch (cmd) {
410  case CLI_INIT:
411  e->command = "stun show status";
412  e->usage =
413  "Usage: stun show status\n"
414  " List all known STUN servers and statuses.\n";
415  return NULL;
416  case CLI_GENERATE:
417  return NULL;
418  }
419 
420  if (a->argc != 3) {
421  return CLI_SHOWUSAGE;
422  }
423 
424  _stun_show_status(a->fd);
425  return CLI_SUCCESS;
426 }
427 
428 static struct ast_cli_entry cli_stun[] = {
429  AST_CLI_DEFINE(handle_cli_stun_show_status, "Show STUN servers and statuses"),
430 };
431 
432 static int __reload(int startup)
433 {
434  int res;
435 
436  ast_mutex_lock(&args.lock);
437  if (!(res = load_config(startup)) && args.monitor_enabled) {
438  res = stun_start_monitor();
439  }
440  ast_mutex_unlock(&args.lock);
441 
442  if (res < 0 || !args.monitor_enabled) {
444  }
445 
446  return res;
447 }
448 
449 static int reload(void)
450 {
451  return __reload(0);
452 }
453 
454 static int unload_module(void)
455 {
457  ast_mutex_destroy(&args.lock);
458 
459  /*! Unregister CLI commands */
460  ast_cli_unregister_multiple(cli_stun, ARRAY_LEN(cli_stun));
461 
462  return 0;
463 }
464 
465 static int load_module(void)
466 {
467  ast_mutex_init(&args.lock);
468  args.stun_sock = -1;
469  if (__reload(1)) {
470  ast_mutex_destroy(&args.lock);
472  }
473 
474  /*! Register CLI commands */
475  ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
476 
478 }
479 
481  .support_level = AST_MODULE_SUPPORT_CORE,
482  .load = load_module,
483  .unload = unload_module,
484  .reload = reload,
485  .load_pri = AST_MODPRI_CHANNEL_DEPEND
486 );
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition: sched.c:195
struct ast_variable * next
struct sockaddr_storage ss
Definition: netsock2.h:98
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
#define DEFAULT_MONITOR_REFRESH
static int stun_monitor_request(const void *blarg)
Definition: ast_expr2.c:325
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
static int __reload(int startup)
static void _stun_show_status(int fd)
Execute stun show status command.
struct ast_json_payload * ast_json_payload_create(struct ast_json *json)
Create an ao2 object to pass json blobs as data payloads for stasis.
Definition: json.c:735
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
static struct @499 args
#define ast_set_flag(p, flag)
Definition: utils.h:70
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
static int setup_stunaddr(const char *value, int reload)
#define CONFIG_STATUS_FILEINVALID
struct stasis_message_type * ast_network_change_type(void)
A stasis_message_type for network changes.
struct ast_config * ast_config_load2(const char *filename, const char *who_asked, struct ast_flags flags)
Load a config file.
Definition: main/config.c:3154
Structure for variables, used for configurations and for channel variables.
static struct ast_sched_context * sched
Definition: cli.h:152
int ast_sched_add_variable(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data, int variable) attribute_warn_unused_result
Adds a scheduled event with rescheduling support.
Definition: sched.c:524
static int reload(void)
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ast_mutex_lock(a)
Definition: lock.h:187
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
Socket address structure.
Definition: netsock2.h:97
#define ast_strlen_zero(foo)
Definition: strings.h:52
static char * handle_cli_stun_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Configuration File Parser.
struct sockaddr_in external_addr
#define ast_log
Definition: astobj2.c:42
int ast_sockaddr_split_hostport(char *str, char **host, char **port, int flags)
Splits a string into its host and port components.
Definition: netsock2.c:164
Asterisk JSON abstraction layer.
static const int STANDARD_STUN_PORT
Definition: stun.h:60
#define HEADER
#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:911
const int fd
Definition: cli.h:159
int ast_stun_request(int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
Generic STUN request.
Definition: stun.c:384
Access Control of various sorts.
Scheduler Routines (derived from cheops)
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
static int load_config(int startup)
unsigned int stun_port
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
static int stun_start_monitor(void)
STUN support.
static int answer(void *data)
Definition: chan_pjsip.c:682
Network socket handling.
#define CONFIG_STATUS_FILEUNCHANGED
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:236
#define LOG_ERROR
Definition: logger.h:285
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:537
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
int errno
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
static int load_module(void)
int ast_get_ip(struct ast_sockaddr *addr, const char *hostname)
Get the IP address given a hostname.
Definition: acl.c:1000
#define LOG_NOTICE
Definition: logger.h:263
struct stasis_topic * ast_system_topic(void)
A Stasis Message Bus API topic which publishes messages regarding system changes. ...
#define DATALN
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic&#39;s subscribers.
Definition: stasis.c:1511
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: main/utils.c:782
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
static const char stun_conf_file[]
#define DEFAULT_RETRIES
static void stun_close_sock(void)
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
int stun_sock
Structure used to handle boolean flags.
Definition: utils.h:199
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
const char * usage
Definition: cli.h:177
unsigned int monitor_enabled
#define CLI_SUCCESS
Definition: cli.h:44
const char * server_hostname
struct ast_json * ast_json_object_create(void)
Create a new JSON object.
Definition: json.c:389
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
unsigned int stun_poll_failed_gripe
Abstract JSON element (object, array, string, int, ...).
ast_mutex_t lock
Definition: sched.c:110
#define ast_mutex_init(pmutex)
Definition: lock.h:184
static int unload_module(void)
#define ast_mutex_destroy(a)
Definition: lock.h:186
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:269
int ast_connect(int sockfd, const struct ast_sockaddr *addr)
Wrapper around connect(2) that uses struct ast_sockaddr.
Definition: netsock2.c:595
Structure for mutex and tracking information.
Definition: lock.h:135
unsigned int external_addr_known
jack_status_t status
Definition: app_jack.c:146
#define ast_mutex_unlock(a)
Definition: lock.h:188
unsigned int refresh
static void stun_stop_monitor(void)
static struct test_val a