55308390455e5682e2192d3bddccbc132f9509b2
[asterisk/asterisk.git] / res / res_stun_monitor.c
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 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33
34 #include "asterisk/module.h"
35 #include "asterisk/event.h"
36 #include "asterisk/sched.h"
37 #include "asterisk/config.h"
38 #include "asterisk/stun.h"
39 #include "asterisk/netsock2.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/acl.h"
42 #include "asterisk/cli.h"
43
44 #include <fcntl.h>
45
46 #define DEFAULT_MONITOR_REFRESH 30      /*!< Default refresh period in seconds */
47 #define DEFAULT_RETRIES 3               /*!< retries shown in stun show status
48                                              matching static retries in stun.c */
49
50 static const char stun_conf_file[] = "res_stun_monitor.conf";
51 static struct ast_sched_context *sched;
52
53 static struct {
54         /*! STUN monitor protection lock. */
55         ast_mutex_t lock;
56         /*! Current perceived external address. */
57         struct sockaddr_in external_addr;
58         /*! STUN server host name. */
59         const char *server_hostname;
60         /*! Port of STUN server to use */
61         unsigned int stun_port;
62         /*! Number of seconds between polls to the STUN server for the external address. */
63         unsigned int refresh;
64         /*! Monitoring STUN socket. */
65         int stun_sock;
66         /*! TRUE if the STUN monitor is enabled. */
67         unsigned int monitor_enabled:1;
68         /*! TRUE if the perceived external address is valid/known. */
69         unsigned int external_addr_known:1;
70         /*! TRUE if we have already griped about a STUN poll failing. */
71         unsigned int stun_poll_failed_gripe:1;
72 } args;
73
74 static void stun_close_sock(void)
75 {
76         if (0 <= args.stun_sock) {
77                 close(args.stun_sock);
78                 args.stun_sock = -1;
79         }
80 }
81
82 /* \brief called by scheduler to send STUN request */
83 static int stun_monitor_request(const void *blarg)
84 {
85         int res;
86         struct sockaddr_in answer;
87         static const struct sockaddr_in no_addr = { 0, };
88
89         ast_mutex_lock(&args.lock);
90         if (!args.monitor_enabled) {
91                 goto monitor_request_cleanup;
92         }
93
94         if (args.stun_sock < 0) {
95                 struct ast_sockaddr stun_addr;
96
97                 /* STUN socket not open.  Refresh the server DNS address resolution. */
98                 if (!args.server_hostname) {
99                         /* No STUN hostname? */
100                         goto monitor_request_cleanup;
101                 }
102
103                 /* Lookup STUN address. */
104                 memset(&stun_addr, 0, sizeof(stun_addr));
105                 stun_addr.ss.ss_family = AF_INET;
106                 if (ast_get_ip(&stun_addr, args.server_hostname)) {
107                         /* Lookup failed. */
108                         ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n",
109                                 args.server_hostname);
110                         goto monitor_request_cleanup;
111                 }
112                 ast_sockaddr_set_port(&stun_addr, args.stun_port);
113
114                 /* open socket binding */
115                 args.stun_sock = socket(AF_INET, SOCK_DGRAM, 0);
116                 if (args.stun_sock < 0) {
117                         ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
118                         goto monitor_request_cleanup;
119                 }
120                 if (ast_connect(args.stun_sock, &stun_addr)) {
121                         ast_log(LOG_WARNING, "STUN Failed to connect to %s: %s\n",
122                                 ast_sockaddr_stringify(&stun_addr), strerror(errno));
123                         stun_close_sock();
124                         goto monitor_request_cleanup;
125                 }
126         }
127
128         res = ast_stun_request(args.stun_sock, NULL, NULL, &answer);
129         if (res) {
130                 /*
131                  * STUN request timed out or errored.
132                  *
133                  * Refresh the server DNS address resolution next time around.
134                  */
135                 if (!args.stun_poll_failed_gripe) {
136                         args.stun_poll_failed_gripe = 1;
137                         ast_log(LOG_WARNING, "STUN poll %s. Re-evaluating STUN server address.\n",
138                                 res < 0 ? "failed" : "got no response");
139                 }
140                 stun_close_sock();
141         } else {
142                 args.stun_poll_failed_gripe = 0;
143                 if (memcmp(&no_addr, &answer, sizeof(no_addr))
144                         && memcmp(&args.external_addr, &answer, sizeof(args.external_addr))) {
145                         const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
146                         int newport = ntohs(answer.sin_port);
147
148                         ast_log(LOG_NOTICE, "Old external address/port %s:%d now seen as %s:%d.\n",
149                                 ast_inet_ntoa(args.external_addr.sin_addr),
150                                 ntohs(args.external_addr.sin_port), newaddr, newport);
151
152                         args.external_addr = answer;
153
154                         if (args.external_addr_known) {
155                                 struct ast_event *event;
156
157                                 /*
158                                  * The external address was already known, and has changed...
159                                  * generate event.
160                                  */
161                                 event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END);
162                                 if (!event) {
163                                         ast_log(LOG_ERROR, "Could not create AST_EVENT_NETWORK_CHANGE event.\n");
164                                 } else if (ast_event_queue(event)) {
165                                         ast_event_destroy(event);
166                                         ast_log(LOG_ERROR, "Could not queue AST_EVENT_NETWORK_CHANGE event.\n");
167                                 }
168                         } else {
169                                 /* this was the first external address we found, do not alert listeners
170                                  * until this address changes to something else. */
171                                 args.external_addr_known = 1;
172                         }
173                 }
174         }
175
176 monitor_request_cleanup:
177         /* always refresh this scheduler item.  It will be removed elsewhere when
178          * it is supposed to go away */
179         res = args.refresh * 1000;
180         ast_mutex_unlock(&args.lock);
181
182         return res;
183 }
184
185 /*!
186  * \internal
187  * \brief Stops the STUN monitor thread.
188  *
189  * \note do not hold the args->lock while calling this
190  *
191  * \return Nothing
192  */
193 static void stun_stop_monitor(void)
194 {
195         ast_mutex_lock(&args.lock);
196         args.monitor_enabled = 0;
197         ast_free((char *) args.server_hostname);
198         args.server_hostname = NULL;
199         stun_close_sock();
200         ast_mutex_unlock(&args.lock);
201
202         if (sched) {
203                 ast_sched_context_destroy(sched);
204                 sched = NULL;
205                 ast_log(LOG_NOTICE, "STUN monitor stopped\n");
206         }
207 }
208
209 /*!
210  * \internal
211  * \brief Starts the STUN monitor thread.
212  *
213  * \note The args->lock MUST be held when calling this function
214  *
215  * \return Nothing
216  */
217 static int stun_start_monitor(void)
218 {
219         /* if scheduler thread is not started, make sure to start it now */
220         if (sched) {
221                 return 0; /* already started */
222         }
223
224         if (!(sched = ast_sched_context_create())) {
225                 ast_log(LOG_ERROR, "Failed to create stun monitor scheduler context\n");
226                 return -1;
227         }
228
229         if (ast_sched_start_thread(sched)) {
230                 ast_sched_context_destroy(sched);
231                 sched = NULL;
232                 stun_close_sock();
233                 return -1;
234         }
235
236         if (ast_sched_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
237                 ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
238                 ast_sched_context_destroy(sched);
239                 sched = NULL;
240                 return -1;
241         }
242
243         ast_log(LOG_NOTICE, "STUN monitor started\n");
244
245         return 0;
246 }
247
248 /*!
249  * \internal
250  * \brief Parse and setup the stunaddr parameter.
251  *
252  * \param value Configuration parameter variable value.
253  *
254  * \retval 0 on success.
255  * \retval -1 on error.
256  */
257 static int setup_stunaddr(const char *value)
258 {
259         char *val;
260         char *host_str;
261         char *port_str;
262         unsigned int port;
263         struct ast_sockaddr stun_addr;
264
265         if (ast_strlen_zero(value)) {
266                 /* Setting to an empty value disables STUN monitoring. */
267                 args.monitor_enabled = 0;
268                 return 0;
269         }
270
271         val = ast_strdupa(value);
272         if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
273                 || ast_strlen_zero(host_str)) {
274                 return -1;
275         }
276
277         /* Determine STUN port */
278         if (ast_strlen_zero(port_str)
279                 || 1 != sscanf(port_str, "%30u", &port)) {
280                 port = STANDARD_STUN_PORT;
281         }
282
283         host_str = ast_strdup(host_str);
284         if (!host_str) {
285                 return -1;
286         }
287
288         /* Lookup STUN address. */
289         memset(&stun_addr, 0, sizeof(stun_addr));
290         stun_addr.ss.ss_family = AF_INET;
291         if (ast_get_ip(&stun_addr, host_str)) {
292                 ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
293                 ast_free(host_str);
294                 return -1;
295         }
296
297         /* Save STUN server information. */
298         ast_free((char *) args.server_hostname);
299         args.server_hostname = host_str;
300         args.stun_port = port;
301
302         /* Enable STUN monitor */
303         args.monitor_enabled = 1;
304         return 0;
305 }
306
307 static int load_config(int startup)
308 {
309         struct ast_flags config_flags = { 0, };
310         struct ast_config *cfg;
311         struct ast_variable *v;
312
313         if (!startup) {
314                 ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
315         }
316
317         cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
318         if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
319                 ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
320                 return -1;
321         }
322         if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
323                 return 0;
324         }
325
326         /* clean up any previous open socket */
327         stun_close_sock();
328         args.stun_poll_failed_gripe = 0;
329
330         /* set defaults */
331         args.monitor_enabled = 0;
332         args.refresh = DEFAULT_MONITOR_REFRESH;
333
334         for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
335                 if (!strcasecmp(v->name, "stunaddr")) {
336                         if (setup_stunaddr(v->value)) {
337                                 ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
338                                         v->value, v->lineno);
339                         }
340                 } else if (!strcasecmp(v->name, "stunrefresh")) {
341                         if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
342                                 ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
343                                 args.refresh = DEFAULT_MONITOR_REFRESH;
344                         }
345                 } else {
346                         ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
347                                 v->value, v->lineno);
348                 }
349         }
350
351         ast_config_destroy(cfg);
352
353         return 0;
354 }
355
356 /*! \brief Execute stun show status command */
357 static void _stun_show_status(int fd)
358 {
359         const char *status;
360
361 #define DATALN "%-25s %-5d %-7d %-8d %-7s %-16s %-d\n"
362 #define HEADER "%-25s %-5s %-7s %-8s %-7s %-16s %-s\n"
363
364         /*! we only have one stun server, but start to play well with more */
365         ast_cli(fd, HEADER, "Hostname", "Port", "Period", "Retries", "Status", "ExternAddr", "ExternPort");
366
367         if (args.stun_poll_failed_gripe) {
368                 status = "FAIL";
369         } else if (args.external_addr_known) {
370                 status = "OK";
371         } else {
372                 status = "INIT";
373         }
374         ast_cli( fd, DATALN,
375                      args.server_hostname,
376                      args.stun_port,
377                      args.refresh,
378                      DEFAULT_RETRIES,
379                      status,
380                      ast_inet_ntoa(args.external_addr.sin_addr),
381                      ntohs(args.external_addr.sin_port)
382                    );
383
384 #undef HEADER
385 #undef DATALN
386 }
387
388 static char *handle_cli_stun_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
389 {
390         switch (cmd) {
391         case CLI_INIT:
392                 e->command = "stun show status";
393                 e->usage =
394                     "Usage: stun show status\n"
395                     "       List all known STUN servers and statuses.\n";
396                 return NULL;
397         case CLI_GENERATE:
398                 return NULL;
399         }
400
401         if (a->argc != 3) {
402                 return CLI_SHOWUSAGE;
403         }
404
405         _stun_show_status(a->fd);
406         return CLI_SUCCESS;
407 }
408
409 static struct ast_cli_entry cli_stun[] = {
410         AST_CLI_DEFINE(handle_cli_stun_show_status, "Show STUN servers and statuses"),
411 };
412
413 static int __reload(int startup)
414 {
415         int res;
416
417         ast_mutex_lock(&args.lock);
418         if (!(res = load_config(startup)) && args.monitor_enabled) {
419                 res = stun_start_monitor();
420         }
421         ast_mutex_unlock(&args.lock);
422
423         if (res < 0 || !args.monitor_enabled) {
424                 stun_stop_monitor();
425         }
426
427         return res;
428 }
429
430 static int reload(void)
431 {
432         return __reload(0);
433 }
434
435 static int unload_module(void)
436 {
437         stun_stop_monitor();
438         ast_mutex_destroy(&args.lock);
439
440         /*! Unregister CLI commands */
441         ast_cli_unregister_multiple(cli_stun, ARRAY_LEN(cli_stun));
442
443         return 0;
444 }
445
446 static int load_module(void)
447 {
448         ast_mutex_init(&args.lock);
449         args.stun_sock = -1;
450         if (__reload(1)) {
451                 ast_mutex_destroy(&args.lock);
452                 return AST_MODULE_LOAD_DECLINE;
453         }
454
455         /*! Register CLI commands */
456         ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
457
458         return AST_MODULE_LOAD_SUCCESS;
459 }
460
461 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
462                 .load = load_module,
463                 .unload = unload_module,
464                 .reload = reload,
465                 .load_pri = AST_MODPRI_CHANNEL_DEPEND
466         );