Sat Apr 26 2014 22:01:41

Asterisk developer's documentation


res_stun_monitor.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2010, Digium, Inc.
00005  *
00006  * David Vossel <dvossel@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \brief STUN Network Monitor
00022  *
00023  * \author David Vossel <dvossel@digium.com>
00024  */
00025 
00026 /*** MODULEINFO
00027    <support_level>core</support_level>
00028  ***/
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 369681 $")
00033 
00034 #include "asterisk/module.h"
00035 #include "asterisk/event.h"
00036 #include "asterisk/sched.h"
00037 #include "asterisk/config.h"
00038 #include "asterisk/stun.h"
00039 #include "asterisk/netsock2.h"
00040 #include "asterisk/lock.h"
00041 #include "asterisk/acl.h"
00042 #include "asterisk/cli.h"
00043 
00044 #include <fcntl.h>
00045 
00046 #define DEFAULT_MONITOR_REFRESH 30  /*!< Default refresh period in seconds */
00047 #define DEFAULT_RETRIES 3     /*!< retries shown in stun show status
00048                     matching static retries in stun.c */
00049 
00050 static const char stun_conf_file[] = "res_stun_monitor.conf";
00051 static struct ast_sched_context *sched;
00052 
00053 static struct {
00054    /*! STUN monitor protection lock. */
00055    ast_mutex_t lock;
00056    /*! Current perceived external address. */
00057    struct sockaddr_in external_addr;
00058    /*! STUN server host name. */
00059    const char *server_hostname;
00060    /*! Port of STUN server to use */
00061    unsigned int stun_port;
00062    /*! Number of seconds between polls to the STUN server for the external address. */
00063    unsigned int refresh;
00064    /*! Monitoring STUN socket. */
00065    int stun_sock;
00066    /*! TRUE if the STUN monitor is enabled. */
00067    unsigned int monitor_enabled:1;
00068    /*! TRUE if the perceived external address is valid/known. */
00069    unsigned int external_addr_known:1;
00070    /*! TRUE if we have already griped about a STUN poll failing. */
00071    unsigned int stun_poll_failed_gripe:1;
00072 } args;
00073 
00074 static void stun_close_sock(void)
00075 {
00076    if (0 <= args.stun_sock) {
00077       close(args.stun_sock);
00078       args.stun_sock = -1;
00079    }
00080 }
00081 
00082 /* \brief called by scheduler to send STUN request */
00083 static int stun_monitor_request(const void *blarg)
00084 {
00085    int res;
00086    struct sockaddr_in answer;
00087    static const struct sockaddr_in no_addr = { 0, };
00088 
00089    ast_mutex_lock(&args.lock);
00090    if (!args.monitor_enabled) {
00091       goto monitor_request_cleanup;
00092    }
00093 
00094    if (args.stun_sock < 0) {
00095       struct ast_sockaddr stun_addr;
00096 
00097       /* STUN socket not open.  Refresh the server DNS address resolution. */
00098       if (!args.server_hostname) {
00099          /* No STUN hostname? */
00100          goto monitor_request_cleanup;
00101       }
00102 
00103       /* Lookup STUN address. */
00104       memset(&stun_addr, 0, sizeof(stun_addr));
00105       stun_addr.ss.ss_family = AF_INET;
00106       if (ast_get_ip(&stun_addr, args.server_hostname)) {
00107          /* Lookup failed. */
00108          ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n",
00109             args.server_hostname);
00110          goto monitor_request_cleanup;
00111       }
00112       ast_sockaddr_set_port(&stun_addr, args.stun_port);
00113 
00114       /* open socket binding */
00115       args.stun_sock = socket(AF_INET, SOCK_DGRAM, 0);
00116       if (args.stun_sock < 0) {
00117          ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
00118          goto monitor_request_cleanup;
00119       }
00120       if (ast_connect(args.stun_sock, &stun_addr)) {
00121          ast_log(LOG_WARNING, "STUN Failed to connect to %s: %s\n",
00122             ast_sockaddr_stringify(&stun_addr), strerror(errno));
00123          stun_close_sock();
00124          goto monitor_request_cleanup;
00125       }
00126    }
00127 
00128    res = ast_stun_request(args.stun_sock, NULL, NULL, &answer);
00129    if (res) {
00130       /*
00131        * STUN request timed out or errored.
00132        *
00133        * Refresh the server DNS address resolution next time around.
00134        */
00135       if (!args.stun_poll_failed_gripe) {
00136          args.stun_poll_failed_gripe = 1;
00137          ast_log(LOG_WARNING, "STUN poll %s. Re-evaluating STUN server address.\n",
00138             res < 0 ? "failed" : "got no response");
00139       }
00140       stun_close_sock();
00141    } else {
00142       args.stun_poll_failed_gripe = 0;
00143       if (memcmp(&no_addr, &answer, sizeof(no_addr))
00144          && memcmp(&args.external_addr, &answer, sizeof(args.external_addr))) {
00145          const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
00146          int newport = ntohs(answer.sin_port);
00147 
00148          ast_log(LOG_NOTICE, "Old external address/port %s:%d now seen as %s:%d.\n",
00149             ast_inet_ntoa(args.external_addr.sin_addr),
00150             ntohs(args.external_addr.sin_port), newaddr, newport);
00151 
00152          args.external_addr = answer;
00153 
00154          if (args.external_addr_known) {
00155             struct ast_event *event;
00156 
00157             /*
00158              * The external address was already known, and has changed...
00159              * generate event.
00160              */
00161             event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END);
00162             if (!event) {
00163                ast_log(LOG_ERROR, "Could not create AST_EVENT_NETWORK_CHANGE event.\n");
00164             } else if (ast_event_queue(event)) {
00165                ast_event_destroy(event);
00166                ast_log(LOG_ERROR, "Could not queue AST_EVENT_NETWORK_CHANGE event.\n");
00167             }
00168          } else {
00169             /* this was the first external address we found, do not alert listeners
00170              * until this address changes to something else. */
00171             args.external_addr_known = 1;
00172          }
00173       }
00174    }
00175 
00176 monitor_request_cleanup:
00177    /* always refresh this scheduler item.  It will be removed elsewhere when
00178     * it is supposed to go away */
00179    res = args.refresh * 1000;
00180    ast_mutex_unlock(&args.lock);
00181 
00182    return res;
00183 }
00184 
00185 /*!
00186  * \internal
00187  * \brief Stops the STUN monitor thread.
00188  *
00189  * \note do not hold the args->lock while calling this
00190  *
00191  * \return Nothing
00192  */
00193 static void stun_stop_monitor(void)
00194 {
00195    ast_mutex_lock(&args.lock);
00196    args.monitor_enabled = 0;
00197    ast_free((char *) args.server_hostname);
00198    args.server_hostname = NULL;
00199    stun_close_sock();
00200    ast_mutex_unlock(&args.lock);
00201 
00202    if (sched) {
00203       ast_sched_context_destroy(sched);
00204       sched = NULL;
00205       ast_log(LOG_NOTICE, "STUN monitor stopped\n");
00206    }
00207 }
00208 
00209 /*!
00210  * \internal
00211  * \brief Starts the STUN monitor thread.
00212  *
00213  * \note The args->lock MUST be held when calling this function
00214  *
00215  * \return Nothing
00216  */
00217 static int stun_start_monitor(void)
00218 {
00219    /* if scheduler thread is not started, make sure to start it now */
00220    if (sched) {
00221       return 0; /* already started */
00222    }
00223 
00224    if (!(sched = ast_sched_context_create())) {
00225       ast_log(LOG_ERROR, "Failed to create stun monitor scheduler context\n");
00226       return -1;
00227    }
00228 
00229    if (ast_sched_start_thread(sched)) {
00230       ast_sched_context_destroy(sched);
00231       sched = NULL;
00232       stun_close_sock();
00233       return -1;
00234    }
00235 
00236    if (ast_sched_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
00237       ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
00238       ast_sched_context_destroy(sched);
00239       sched = NULL;
00240       return -1;
00241    }
00242 
00243    ast_log(LOG_NOTICE, "STUN monitor started\n");
00244 
00245    return 0;
00246 }
00247 
00248 /*!
00249  * \internal
00250  * \brief Parse and setup the stunaddr parameter.
00251  *
00252  * \param value Configuration parameter variable value.
00253  *
00254  * \retval 0 on success.
00255  * \retval -1 on error.
00256  */
00257 static int setup_stunaddr(const char *value)
00258 {
00259    char *val;
00260    char *host_str;
00261    char *port_str;
00262    unsigned int port;
00263    struct ast_sockaddr stun_addr;
00264 
00265    if (ast_strlen_zero(value)) {
00266       /* Setting to an empty value disables STUN monitoring. */
00267       args.monitor_enabled = 0;
00268       return 0;
00269    }
00270 
00271    val = ast_strdupa(value);
00272    if (!ast_sockaddr_split_hostport(val, &host_str, &port_str, 0)
00273       || ast_strlen_zero(host_str)) {
00274       return -1;
00275    }
00276 
00277    /* Determine STUN port */
00278    if (ast_strlen_zero(port_str)
00279       || 1 != sscanf(port_str, "%30u", &port)) {
00280       port = STANDARD_STUN_PORT;
00281    }
00282 
00283    host_str = ast_strdup(host_str);
00284    if (!host_str) {
00285       return -1;
00286    }
00287 
00288    /* Lookup STUN address. */
00289    memset(&stun_addr, 0, sizeof(stun_addr));
00290    stun_addr.ss.ss_family = AF_INET;
00291    if (ast_get_ip(&stun_addr, host_str)) {
00292       ast_log(LOG_WARNING, "Unable to lookup STUN server '%s'\n", host_str);
00293       ast_free(host_str);
00294       return -1;
00295    }
00296 
00297    /* Save STUN server information. */
00298    ast_free((char *) args.server_hostname);
00299    args.server_hostname = host_str;
00300    args.stun_port = port;
00301 
00302    /* Enable STUN monitor */
00303    args.monitor_enabled = 1;
00304    return 0;
00305 }
00306 
00307 static int load_config(int startup)
00308 {
00309    struct ast_flags config_flags = { 0, };
00310    struct ast_config *cfg;
00311    struct ast_variable *v;
00312 
00313    if (!startup) {
00314       ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
00315    }
00316 
00317    cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags);
00318    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00319       ast_log(LOG_WARNING, "Unable to load config %s\n", stun_conf_file);
00320       return -1;
00321    }
00322    if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00323       return 0;
00324    }
00325 
00326    /* clean up any previous open socket */
00327    stun_close_sock();
00328    args.stun_poll_failed_gripe = 0;
00329 
00330    /* set defaults */
00331    args.monitor_enabled = 0;
00332    args.refresh = DEFAULT_MONITOR_REFRESH;
00333 
00334    for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
00335       if (!strcasecmp(v->name, "stunaddr")) {
00336          if (setup_stunaddr(v->value)) {
00337             ast_log(LOG_WARNING, "Invalid STUN server address: %s at line %d\n",
00338                v->value, v->lineno);
00339          }
00340       } else if (!strcasecmp(v->name, "stunrefresh")) {
00341          if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
00342             ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
00343             args.refresh = DEFAULT_MONITOR_REFRESH;
00344          }
00345       } else {
00346          ast_log(LOG_WARNING, "Invalid config option %s at line %d\n",
00347             v->value, v->lineno);
00348       }
00349    }
00350 
00351    ast_config_destroy(cfg);
00352 
00353    return 0;
00354 }
00355 
00356 /*! \brief Execute stun show status command */
00357 static void _stun_show_status(int fd)
00358 {
00359    const char *status;
00360 
00361 #define DATALN "%-25s %-5d %-7d %-8d %-7s %-16s %-d\n"
00362 #define HEADER "%-25s %-5s %-7s %-8s %-7s %-16s %-s\n"
00363 
00364    /*! we only have one stun server, but start to play well with more */
00365    ast_cli(fd, HEADER, "Hostname", "Port", "Period", "Retries", "Status", "ExternAddr", "ExternPort");
00366 
00367    if (args.stun_poll_failed_gripe) {
00368       status = "FAIL";
00369    } else if (args.external_addr_known) {
00370       status = "OK";
00371    } else {
00372       status = "INIT";
00373    }
00374    ast_cli( fd, DATALN,
00375            args.server_hostname,
00376            args.stun_port,
00377            args.refresh,
00378            DEFAULT_RETRIES,
00379            status,
00380            ast_inet_ntoa(args.external_addr.sin_addr),
00381            ntohs(args.external_addr.sin_port)
00382          );
00383 
00384 #undef HEADER
00385 #undef DATALN
00386 }
00387 
00388 static char *handle_cli_stun_show_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00389 {
00390    switch (cmd) {
00391    case CLI_INIT:
00392       e->command = "stun show status";
00393       e->usage =
00394           "Usage: stun show status\n"
00395           "       List all known STUN servers and statuses.\n";
00396       return NULL;
00397    case CLI_GENERATE:
00398       return NULL;
00399    }
00400 
00401    if (a->argc != 3) {
00402       return CLI_SHOWUSAGE;
00403    }
00404 
00405    _stun_show_status(a->fd);
00406    return CLI_SUCCESS;
00407 }
00408 
00409 static struct ast_cli_entry cli_stun[] = {
00410    AST_CLI_DEFINE(handle_cli_stun_show_status, "Show STUN servers and statuses"),
00411 };
00412 
00413 static int __reload(int startup)
00414 {
00415    int res;
00416 
00417    ast_mutex_lock(&args.lock);
00418    if (!(res = load_config(startup)) && args.monitor_enabled) {
00419       res = stun_start_monitor();
00420    }
00421    ast_mutex_unlock(&args.lock);
00422 
00423    if (res < 0 || !args.monitor_enabled) {
00424       stun_stop_monitor();
00425    }
00426 
00427    return res;
00428 }
00429 
00430 static int reload(void)
00431 {
00432    return __reload(0);
00433 }
00434 
00435 static int unload_module(void)
00436 {
00437    stun_stop_monitor();
00438    ast_mutex_destroy(&args.lock);
00439 
00440    /*! Unregister CLI commands */
00441    ast_cli_unregister_multiple(cli_stun, ARRAY_LEN(cli_stun));
00442 
00443    return 0;
00444 }
00445 
00446 static int load_module(void)
00447 {
00448    ast_mutex_init(&args.lock);
00449    args.stun_sock = -1;
00450    if (__reload(1)) {
00451       ast_mutex_destroy(&args.lock);
00452       return AST_MODULE_LOAD_DECLINE;
00453    }
00454 
00455    /*! Register CLI commands */
00456    ast_cli_register_multiple(cli_stun, sizeof(cli_stun) / sizeof(struct ast_cli_entry));
00457 
00458    return AST_MODULE_LOAD_SUCCESS;
00459 }
00460 
00461 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
00462       .load = load_module,
00463       .unload = unload_module,
00464       .reload = reload,
00465       .load_pri = AST_MODPRI_CHANNEL_DEPEND
00466    );