2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2010, Digium, Inc.
6 * David Vossel <dvossel@digium.com>
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.
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.
21 * \brief STUN Network Monitor
23 * \author David Vossel <dvossel@digium.com>
27 <support_level>core</support_level>
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
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"
44 #define DEFAULT_MONITOR_REFRESH 30 /*!< Default refresh period in seconds */
46 static const char stun_conf_file[] = "res_stun_monitor.conf";
47 static struct ast_sched_context *sched;
50 /*! STUN monitor protection lock. */
52 /*! Current perceived external address. */
53 struct sockaddr_in external_addr;
54 /*! STUN server host name. */
55 const char *server_hostname;
56 /*! Port of STUN server to use */
57 unsigned int stun_port;
58 /*! Number of seconds between polls to the STUN server for the external address. */
60 /*! Monitoring STUN socket. */
62 /*! TRUE if the STUN monitor is enabled. */
63 unsigned int monitor_enabled:1;
64 /*! TRUE if the perceived external address is valid/known. */
65 unsigned int external_addr_known:1;
66 /*! TRUE if we have already griped about a STUN poll failing. */
67 unsigned int stun_poll_failed_gripe:1;
70 static void stun_close_sock(void)
72 if (0 <= args.stun_sock) {
73 close(args.stun_sock);
78 /* \brief called by scheduler to send STUN request */
79 static int stun_monitor_request(const void *blarg)
82 struct sockaddr_in answer;
83 static const struct sockaddr_in no_addr = { 0, };
85 ast_mutex_lock(&args.lock);
86 if (!args.monitor_enabled) {
87 goto monitor_request_cleanup;
90 if (args.stun_sock < 0) {
91 struct ast_sockaddr stun_addr;
93 /* STUN socket not open. Refresh the server DNS address resolution. */
94 if (!args.server_hostname) {
95 /* No STUN hostname? */
96 goto monitor_request_cleanup;
99 /* Lookup STUN address. */
100 memset(&stun_addr, 0, sizeof(stun_addr));
101 stun_addr.ss.ss_family = AF_INET;
102 if (ast_get_ip(&stun_addr, args.server_hostname)) {
104 ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n",
105 args.server_hostname);
106 goto monitor_request_cleanup;
108 ast_sockaddr_set_port(&stun_addr, args.stun_port);
110 /* open socket binding */
111 args.stun_sock = socket(AF_INET, SOCK_DGRAM, 0);
112 if (args.stun_sock < 0) {
113 ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
114 goto monitor_request_cleanup;
116 if (ast_connect(args.stun_sock, &stun_addr)) {
117 ast_log(LOG_WARNING, "STUN Failed to connect to %s: %s\n",
118 ast_sockaddr_stringify(&stun_addr), strerror(errno));
120 goto monitor_request_cleanup;
124 res = ast_stun_request(args.stun_sock, NULL, NULL, &answer);
127 * STUN request timed out or errored.
129 * Refresh the server DNS address resolution next time around.
131 if (!args.stun_poll_failed_gripe) {
132 args.stun_poll_failed_gripe = 1;
133 ast_log(LOG_WARNING, "STUN poll %s. Re-evaluating STUN server address.\n",
134 res < 0 ? "failed" : "got no response");
138 args.stun_poll_failed_gripe = 0;
139 if (memcmp(&no_addr, &answer, sizeof(no_addr))
140 && memcmp(&args.external_addr, &answer, sizeof(args.external_addr))) {
141 const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
142 int newport = ntohs(answer.sin_port);
144 ast_log(LOG_NOTICE, "Old external address/port %s:%d now seen as %s:%d.\n",
145 ast_inet_ntoa(args.external_addr.sin_addr),
146 ntohs(args.external_addr.sin_port), newaddr, newport);
148 args.external_addr = answer;
150 if (args.external_addr_known) {
151 struct ast_event *event;
154 * The external address was already known, and has changed...
157 event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END);
159 ast_log(LOG_ERROR, "Could not create AST_EVENT_NETWORK_CHANGE event.\n");
160 } else if (ast_event_queue(event)) {
161 ast_event_destroy(event);
162 ast_log(LOG_ERROR, "Could not queue AST_EVENT_NETWORK_CHANGE event.\n");
165 /* this was the first external address we found, do not alert listeners
166 * until this address changes to something else. */
167 args.external_addr_known = 1;
172 monitor_request_cleanup:
173 /* always refresh this scheduler item. It will be removed elsewhere when
174 * it is supposed to go away */
175 res = args.refresh * 1000;
176 ast_mutex_unlock(&args.lock);
183 * \brief Stops the STUN monitor thread.
185 * \note do not hold the args->lock while calling this
189 static void stun_stop_monitor(void)
191 ast_mutex_lock(&args.lock);
192 args.monitor_enabled = 0;
193 ast_free((char *) args.server_hostname);
194 args.server_hostname = NULL;
196 ast_mutex_unlock(&args.lock);
199 ast_sched_context_destroy(sched);
201 ast_log(LOG_NOTICE, "STUN monitor stopped\n");
207 * \brief Starts the STUN monitor thread.
209 * \note The args->lock MUST be held when calling this function
213 static int stun_start_monitor(void)
215 /* if scheduler thread is not started, make sure to start it now */
217 return 0; /* already started */
220 if (!(sched = ast_sched_context_create())) {
221 ast_log(LOG_ERROR, "Failed to create stun monitor scheduler context\n");
225 if (ast_sched_start_thread(sched)) {
226 ast_sched_context_destroy(sched);
232 if (ast_sched_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
233 ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
234 ast_sched_context_destroy(sched);
239 ast_log(LOG_NOTICE, "STUN monitor started\n");
246 * \brief Parse and setup the stunaddr parameter.
248 * \param value Configuration parameter variable value.
250 * \retval 0 on success.
251 * \retval -1 on error.
253 static int setup_stunaddr(const char *value)
259 struct ast_sockaddr stun_addr;
261 if (ast_strlen_zero(value)) {
262 /* Setting to an empty value disables STUN monitoring. */
263 args.monitor_enabled = 0;
267 val = ast_strdupa(value);
268 if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
269 || ast_strlen_zero(host_str)) {
273 /* Determine STUN port */
274 if (ast_strlen_zero(port_str)
275 || 1 != sscanf(port_str, "%30u", &port)) {
276 port = STANDARD_STUN_PORT;
279 host_str = ast_strdup(host_str);
284 /* Lookup STUN address. */
285 memset(&stun_addr, 0, sizeof(stun_addr));
286 stun_addr.ss.ss_family = AF_INET;
287 if (ast_get_ip(&stun_addr, host_str)) {
288 ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
293 /* Save STUN server information. */
294 ast_free((char *) args.server_hostname);
295 args.server_hostname = host_str;
296 args.stun_port = port;
298 /* Enable STUN monitor */
299 args.monitor_enabled = 1;
303 static int load_config(int startup)
305 struct ast_flags config_flags = { 0, };
306 struct ast_config *cfg;
307 struct ast_variable *v;
310 ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
313 cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
314 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
315 ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
318 if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
322 /* clean up any previous open socket */
324 args.stun_poll_failed_gripe = 0;
327 args.monitor_enabled = 0;
328 args.refresh = DEFAULT_MONITOR_REFRESH;
330 for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
331 if (!strcasecmp(v->name, "stunaddr")) {
332 if (setup_stunaddr(v->value)) {
333 ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
334 v->value, v->lineno);
336 } else if (!strcasecmp(v->name, "stunrefresh")) {
337 if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
338 ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
339 args.refresh = DEFAULT_MONITOR_REFRESH;
342 ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
343 v->value, v->lineno);
347 ast_config_destroy(cfg);
352 static int __reload(int startup)
356 ast_mutex_lock(&args.lock);
357 if (!(res = load_config(startup)) && args.monitor_enabled) {
358 res = stun_start_monitor();
360 ast_mutex_unlock(&args.lock);
362 if (res < 0 || !args.monitor_enabled) {
369 static int reload(void)
374 static int unload_module(void)
377 ast_mutex_destroy(&args.lock);
381 static int load_module(void)
383 ast_mutex_init(&args.lock);
386 ast_mutex_destroy(&args.lock);
387 return AST_MODULE_LOAD_DECLINE;
390 return AST_MODULE_LOAD_SUCCESS;
393 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
395 .unload = unload_module,
397 .load_pri = AST_MODPRI_CHANNEL_DEPEND