Asterisk - The Open Source Telephony Project  21.4.1
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 <dvossel@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 /*!
20  * \file
21  * \brief STUN Network Monitor
22  *
23  * \author David Vossel <dvossel@digium.com>
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. */
56  ast_mutex_t 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 static void stun_stop_monitor(void)
207 {
208  ast_mutex_lock(&args.lock);
209  args.monitor_enabled = 0;
210  ast_free((char *) args.server_hostname);
211  args.server_hostname = NULL;
212  stun_close_sock();
213  ast_mutex_unlock(&args.lock);
214 
215  if (sched) {
217  sched = NULL;
218  ast_log(LOG_NOTICE, "STUN monitor stopped\n");
219  }
220 }
221 
222 /*!
223  * \internal
224  * \brief Starts the STUN monitor thread.
225  *
226  * \note The args->lock MUST be held when calling this function
227  */
228 static int stun_start_monitor(void)
229 {
230  /* if scheduler thread is not started, make sure to start it now */
231  if (sched) {
232  return 0; /* already started */
233  }
234 
235  if (!(sched = ast_sched_context_create())) {
236  ast_log(LOG_ERROR, "Failed to create stun monitor scheduler context\n");
237  return -1;
238  }
239 
240  if (ast_sched_start_thread(sched)) {
242  sched = NULL;
243  stun_close_sock();
244  return -1;
245  }
246 
247  if (ast_sched_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
248  ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
250  sched = NULL;
251  return -1;
252  }
253 
254  ast_log(LOG_NOTICE, "STUN monitor started\n");
255 
256  return 0;
257 }
258 
259 /*!
260  * \internal
261  * \brief Parse and setup the stunaddr parameter.
262  *
263  * \param value Configuration parameter variable value.
264  * \param reload
265  *
266  * \retval 0 on success.
267  * \retval -1 on error.
268  */
269 static int setup_stunaddr(const char *value, int reload)
270 {
271  char *val;
272  char *host_str;
273  char *port_str;
274  unsigned int port;
275  struct ast_sockaddr stun_addr;
276 
277  if (ast_strlen_zero(value)) {
278  /* Setting to an empty value disables STUN monitoring. */
279  args.monitor_enabled = 0;
280  return 0;
281  }
282 
283  val = ast_strdupa(value);
284  if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
285  || ast_strlen_zero(host_str)) {
286  return -1;
287  }
288 
289  /* Determine STUN port */
290  if (ast_strlen_zero(port_str)
291  || 1 != sscanf(port_str, "%30u", &port)) {
292  port = STANDARD_STUN_PORT;
293  }
294 
295  host_str = ast_strdup(host_str);
296  if (!host_str) {
297  return -1;
298  }
299 
300  /* Lookup STUN address. */
301  memset(&stun_addr, 0, sizeof(stun_addr));
302  stun_addr.ss.ss_family = AF_INET;
303  if (ast_get_ip(&stun_addr, host_str)) {
304  ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
305 
306  /* Only treat this as fatal if we are reloading */
307  if (reload) {
308  ast_free(host_str);
309  return -1;
310  }
311  }
312 
313  /* Save STUN server information. */
314  ast_free((char *) args.server_hostname);
315  args.server_hostname = host_str;
316  args.stun_port = port;
317 
318  /* Enable STUN monitor */
319  args.monitor_enabled = 1;
320  return 0;
321 }
322 
323 static int load_config(int startup)
324 {
325  struct ast_flags config_flags = { 0, };
326  struct ast_config *cfg;
327  struct ast_variable *v;
328 
329  if (!startup) {
330  ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
331  }
332 
333  cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
334  if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
335  ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
336  return -1;
337  }
338  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
339  return 0;
340  }
341 
342  /* clean up any previous open socket */
343  stun_close_sock();
344  args.stun_poll_failed_gripe = 0;
345 
346  /* set defaults */
347  args.monitor_enabled = 0;
348  args.refresh = DEFAULT_MONITOR_REFRESH;
349 
350  for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
351  if (!strcasecmp(v->name, "stunaddr")) {
352  if (setup_stunaddr(v->value, !startup)) {
353  ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
354  v->value, v->lineno);
355  }
356  } else if (!strcasecmp(v->name, "stunrefresh")) {
357  if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
358  ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
359  args.refresh = DEFAULT_MONITOR_REFRESH;
360  }
361  } else {
362  ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
363  v->value, v->lineno);
364  }
365  }
366 
367  ast_config_destroy(cfg);
368 
369  return 0;
370 }
371 
372 /*! \brief Execute stun show status command */
373 static void _stun_show_status(int fd)
374 {
375  const char *status;
376 
377 #define DATALN "%-25s %-5u %-7u %-8d %-7s %-16s %-d\n"
378 #define HEADER "%-25s %-5s %-7s %-8s %-7s %-16s %-s\n"
379 
380  /*! we only have one stun server, but start to play well with more */
381  ast_cli(fd, HEADER, "Hostname", "Port", "Period", "Retries", "Status", "ExternAddr", "ExternPort");
382 
383  if (args.stun_poll_failed_gripe) {
384  status = "FAIL";
385  } else if (args.external_addr_known) {
386  status = "OK";
387  } else {
388  status = "INIT";
389  }
390  ast_cli( fd, DATALN,
391  args.server_hostname,
392  args.stun_port,
393  args.refresh,
395  status,
396  ast_inet_ntoa(args.external_addr.sin_addr),
397  ntohs(args.external_addr.sin_port)
398  );
399 
400 #undef HEADER
401 #undef DATALN
402 }
403 
404 static char *handle_cli_stun_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
405 {
406  switch (cmd) {
407  case CLI_INIT:
408  e->command = "stun show status";
409  e->usage =
410  "Usage: stun show status\n"
411  " List all known STUN servers and statuses.\n";
412  return NULL;
413  case CLI_GENERATE:
414  return NULL;
415  }
416 
417  if (a->argc != 3) {
418  return CLI_SHOWUSAGE;
419  }
420 
421  _stun_show_status(a->fd);
422  return CLI_SUCCESS;
423 }
424 
425 static struct ast_cli_entry cli_stun[] = {
426  AST_CLI_DEFINE(handle_cli_stun_show_status, "Show STUN servers and statuses"),
427 };
428 
429 static int __reload(int startup)
430 {
431  int res;
432 
433  ast_mutex_lock(&args.lock);
434  if (!(res = load_config(startup)) && args.monitor_enabled) {
435  res = stun_start_monitor();
436  }
437  ast_mutex_unlock(&args.lock);
438 
439  if (res < 0 || !args.monitor_enabled) {
440  stun_stop_monitor();
441  }
442 
443  return res;
444 }
445 
446 static int reload(void)
447 {
448  return __reload(0);
449 }
450 
451 static int unload_module(void)
452 {
453  stun_stop_monitor();
454  ast_mutex_destroy(&args.lock);
455 
456  /*! Unregister CLI commands */
457  ast_cli_unregister_multiple(cli_stun, ARRAY_LEN(cli_stun));
458 
459  return 0;
460 }
461 
462 static int load_module(void)
463 {
464  ast_mutex_init(&args.lock);
465  args.stun_sock = -1;
466  if (__reload(1)) {
467  ast_mutex_destroy(&args.lock);
469  }
470 
471  /*! Register CLI commands */
472  ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
473 
475 }
476 
477 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
478  .support_level = AST_MODULE_SUPPORT_CORE,
479  .load = load_module,
480  .unload = unload_module,
481  .reload = reload,
482  .load_pri = AST_MODPRI_CHANNEL_DEPEND
483 );
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition: sched.c:197
struct ast_variable * next
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
struct stasis_topic * ast_system_topic(void)
A Stasis Message Bus API topic which publishes messages regarding system changes. ...
#define DEFAULT_MONITOR_REFRESH
static int stun_monitor_request(const void *blarg)
called by scheduler to send STUN request
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
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:756
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
Definition: cli.h:171
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:3321
Structure for variables, used for configurations and for channel variables.
Definition: sched.c:76
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:526
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Socket address structure.
Definition: netsock2.h:97
int args
This gets set in ast_cli_register()
Definition: cli.h:185
Configuration File Parser.
struct sockaddr_in external_addr
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.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int ast_stun_request(int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
Generic STUN request.
Definition: stun.c:415
Access Control of various sorts.
Definition: stun.c:87
Scheduler Routines (derived from cheops)
unsigned int stun_port
STUN support.
Network socket handling.
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:238
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
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:999
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: utils.c:928
char * command
Definition: cli.h:186
#define DEFAULT_RETRIES
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
const char * usage
Definition: cli.h:177
unsigned int monitor_enabled
const char * server_hostname
struct ast_json * ast_json_object_create(void)
Create a new JSON object.
Definition: json.c:399
Standard Command Line Interface.
unsigned int stun_poll_failed_gripe
Abstract JSON element (object, array, string, int, ...).
static int unload_module(void)
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
#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
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:271
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
unsigned int refresh