Asterisk - The Open Source Telephony Project GIT-master-0bf3178
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"
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
51static const char stun_conf_file[] = "res_stun_monitor.conf";
52static struct ast_sched_context *sched;
53
54static 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. */
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;
74
75static 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 */
84static 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) {
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 }
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",
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 }
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
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
177publish_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
191monitor_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 */
206static 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;
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 */
228static 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
242 sched = NULL;
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 */
269static 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
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
323static 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) {
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 */
344 args.stun_poll_failed_gripe = 0;
345
346 /* set defaults */
347 args.monitor_enabled = 0;
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);
360 }
361 } else {
362 ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
363 v->value, v->lineno);
364 }
365 }
366
368
369 return 0;
370}
371
372/*! \brief Execute stun show status command */
373static 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
404static 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
425static struct ast_cli_entry cli_stun[] = {
426 AST_CLI_DEFINE(handle_cli_stun_show_status, "Show STUN servers and statuses"),
427};
428
429static 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) {
441 }
442
443 return res;
444}
445
446static int reload(void)
447{
448 return __reload(0);
449}
450
451static int unload_module(void)
452{
454 ast_mutex_destroy(&args.lock);
455
456 /*! Unregister CLI commands */
458
459 return 0;
460}
461
462static 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
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
Access Control of various sorts.
int ast_get_ip(struct ast_sockaddr *addr, const char *hostname)
Get the IP address given a hostname.
Definition: acl.c:1016
jack_status_t status
Definition: app_jack.c:146
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
static int answer(void *data)
Definition: chan_pjsip.c:687
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
Configuration File Parser.
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:3336
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
@ CONFIG_FLAG_FILEUNCHANGED
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
Asterisk JSON abstraction layer.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json * ast_json_object_create(void)
Create a new JSON object.
Definition: json.c:399
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
Asterisk locking-related definitions:
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Network socket handling.
int ast_connect(int sockfd, const struct ast_sockaddr *addr)
Wrapper around connect(2) that uses struct ast_sockaddr.
Definition: netsock2.c:595
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
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
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
const char * ast_inet_ntoa(struct in_addr ia)
thread-safe replacement for inet_ntoa().
Definition: utils.c:928
static void stun_stop_monitor(void)
#define DEFAULT_RETRIES
static struct @481 args
#define DEFAULT_MONITOR_REFRESH
static int stun_start_monitor(void)
static void stun_close_sock(void)
static struct ast_sched_context * sched
struct sockaddr_in external_addr
static void _stun_show_status(int fd)
Execute stun show status command.
static int load_config(int startup)
static int setup_stunaddr(const char *value, int reload)
unsigned int stun_poll_failed_gripe
unsigned int stun_port
static int stun_monitor_request(const void *blarg)
called by scheduler to send STUN request
const char * server_hostname
static int __reload(int startup)
int stun_sock
static char * handle_cli_stun_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define HEADER
unsigned int refresh
static const char stun_conf_file[]
static int load_module(void)
static int unload_module(void)
static int reload(void)
static struct ast_cli_entry cli_stun[]
unsigned int external_addr_known
#define DATALN
unsigned int monitor_enabled
#define NULL
Definition: resample.c:96
Scheduler Routines (derived from cheops)
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:271
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition: sched.c:197
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
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:238
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1512
struct stasis_topic * ast_system_topic(void)
A Stasis Message Bus API topic which publishes messages regarding system changes.
struct stasis_message_type * ast_network_change_type(void)
A stasis_message_type for network changes.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Structure used to handle boolean flags.
Definition: utils.h:199
Abstract JSON element (object, array, string, int, ...).
Structure for mutex and tracking information.
Definition: lock.h:135
ast_mutex_t lock
Definition: sched.c:112
Socket address structure.
Definition: netsock2.h:97
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Definition: sched.c:76
Definition: stun.c:87
Definition: ast_expr2.c:325
STUN support.
int ast_stun_request(int s, struct sockaddr_in *dst, const char *username, struct sockaddr_in *answer)
Generic STUN request.
Definition: stun.c:415
static const int STANDARD_STUN_PORT
Definition: stun.h:61
int value
Definition: syslog.c:37
static struct test_val a
#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
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:666