Merged revisions 282269 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.8 ........ r282269 | dvossel | 2010-08-13 15:03:56 -0500 (Fri, 13 Aug 2010) | 4 lines res_stun_monitor for monitoring network changes behind a NAT device Review: https://reviewboard.asterisk.org/r/854 ........ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@282270 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
eb0ec7df90
commit
30ec863881
|
@ -268,6 +268,9 @@ static char default_parkinglot[AST_MAX_CONTEXT];
|
|||
static char language[MAX_LANGUAGE] = "";
|
||||
static char regcontext[AST_MAX_CONTEXT] = "";
|
||||
|
||||
static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
|
||||
static int network_change_event_sched_id = -1;
|
||||
|
||||
static int maxauthreq = 3;
|
||||
static int max_retries = 4;
|
||||
static int ping_time = 21;
|
||||
|
@ -1177,6 +1180,8 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
|
|||
static int iax2_queryoption(struct ast_channel *c, int option, void *data, int *datalen);
|
||||
static int iax2_transfer(struct ast_channel *c, const char *dest);
|
||||
static int iax2_write(struct ast_channel *c, struct ast_frame *f);
|
||||
static int iax2_sched_add(struct ast_sched_thread *st, int when, ast_sched_cb callback, const void *data);
|
||||
|
||||
static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now);
|
||||
static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
|
||||
static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
|
||||
|
@ -1201,6 +1206,7 @@ static void build_rand_pad(unsigned char *buf, ssize_t len);
|
|||
static struct callno_entry *get_unused_callno(int trunk, int validated);
|
||||
static int replace_callno(const void *obj);
|
||||
static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
|
||||
static void network_change_event_cb(const struct ast_event *, void *);
|
||||
|
||||
static const struct ast_channel_tech iax2_tech = {
|
||||
.type = "IAX2",
|
||||
|
@ -1236,6 +1242,47 @@ static void mwi_event_cb(const struct ast_event *event, void *userdata)
|
|||
* is time to send MWI, since it is only sent with a REGACK. */
|
||||
}
|
||||
|
||||
static void network_change_event_subscribe(void)
|
||||
{
|
||||
if (!network_change_event_subscription) {
|
||||
network_change_event_subscription = ast_event_subscribe(AST_EVENT_NETWORK_CHANGE,
|
||||
network_change_event_cb,
|
||||
"SIP Network Change ",
|
||||
NULL,
|
||||
AST_EVENT_IE_END);
|
||||
}
|
||||
}
|
||||
|
||||
static void network_change_event_unsubscribe(void)
|
||||
{
|
||||
if (network_change_event_subscription) {
|
||||
network_change_event_subscription = ast_event_unsubscribe(network_change_event_subscription);
|
||||
}
|
||||
}
|
||||
|
||||
static int network_change_event_sched_cb(const void *data)
|
||||
{
|
||||
struct iax2_registry *reg;
|
||||
network_change_event_sched_id = -1;
|
||||
AST_LIST_LOCK(®istrations);
|
||||
AST_LIST_TRAVERSE(®istrations, reg, entry) {
|
||||
iax2_do_register(reg);
|
||||
}
|
||||
AST_LIST_UNLOCK(®istrations);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void network_change_event_cb(const struct ast_event *event, void *userdata)
|
||||
{
|
||||
ast_debug(1, "IAX, got a network change event, renewing all IAX registrations.\n");
|
||||
if (network_change_event_sched_id == -1) {
|
||||
network_change_event_sched_id = iax2_sched_add(sched, 1000, network_change_event_sched_cb, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Send manager event at call setup to link between Asterisk channel name
|
||||
and IAX2 call identifiers */
|
||||
static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
|
||||
|
@ -1246,7 +1293,6 @@ static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
|
|||
pvt->callno, pvt->peercallno, pvt->peer ? pvt->peer : "");
|
||||
}
|
||||
|
||||
|
||||
static struct ast_datastore_info iax2_variable_datastore_info = {
|
||||
.type = "IAX2_VARIABLE",
|
||||
.duplicate = iax2_dup_variable_datastore,
|
||||
|
@ -12779,7 +12825,8 @@ static int set_config(const char *config_file, int reload)
|
|||
int format;
|
||||
int portno = IAX_DEFAULT_PORTNO;
|
||||
int x;
|
||||
int mtuv;
|
||||
int mtuv;
|
||||
int subscribe_network_change = 1;
|
||||
struct iax2_user *user;
|
||||
struct iax2_peer *peer;
|
||||
struct ast_netsock *ns;
|
||||
|
@ -13097,6 +13144,14 @@ static int set_config(const char *config_file, int reload)
|
|||
if (add_calltoken_ignore(v->value)) {
|
||||
ast_log(LOG_WARNING, "Invalid calltokenoptional address range - '%s' line %d\n", v->value, v->lineno);
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "subscribe_network_change_event")) {
|
||||
if (ast_true(v->value)) {
|
||||
subscribe_network_change = 1;
|
||||
} else if (ast_false(v->value)) {
|
||||
subscribe_network_change = 0;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "subscribe_network_change_event value %s is not valid at line %d.\n", v->value, v->lineno);
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "shrinkcallerid")) {
|
||||
if (ast_true(v->value)) {
|
||||
ast_set_flag64((&globalflags), IAX_SHRINKCALLERID);
|
||||
|
@ -13109,7 +13164,13 @@ static int set_config(const char *config_file, int reload)
|
|||
/* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
|
||||
v = v->next;
|
||||
}
|
||||
|
||||
|
||||
if (subscribe_network_change) {
|
||||
network_change_event_subscribe();
|
||||
} else {
|
||||
network_change_event_unsubscribe();
|
||||
}
|
||||
|
||||
if (defaultsockfd < 0) {
|
||||
if (!(ns = ast_netsock_bind(netsock, io, "0.0.0.0", portno, qos.tos, qos.cos, socket_read, NULL))) {
|
||||
ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
|
||||
|
@ -14050,6 +14111,8 @@ static int __unload_module(void)
|
|||
struct ast_context *con;
|
||||
int x;
|
||||
|
||||
network_change_event_unsubscribe();
|
||||
|
||||
ast_manager_unregister("IAXpeers");
|
||||
ast_manager_unregister("IAXpeerlist");
|
||||
ast_manager_unregister("IAXnetstats");
|
||||
|
@ -14530,6 +14593,8 @@ static int load_module(void)
|
|||
|
||||
ast_realtime_require_field("iaxpeers", "name", RQ_CHAR, 10, "ipaddr", RQ_CHAR, 15, "port", RQ_UINTEGER2, 5, "regseconds", RQ_UINTEGER2, 6, SENTINEL);
|
||||
|
||||
network_change_event_subscribe();
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -762,6 +762,9 @@ static int regobjs = 0; /*!< Registry objects */
|
|||
static struct ast_flags global_flags[3] = {{0}}; /*!< global SIP_ flags */
|
||||
static int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */
|
||||
|
||||
static struct ast_event_sub *network_change_event_subscription; /*!< subscription id for network change events */
|
||||
static int network_change_event_sched_id = -1;
|
||||
|
||||
static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(netlock);
|
||||
|
@ -1334,6 +1337,7 @@ static int sip_poke_peer(struct sip_peer *peer, int force);
|
|||
static void sip_poke_all_peers(void);
|
||||
static void sip_peer_hold(struct sip_pvt *p, int hold);
|
||||
static void mwi_event_cb(const struct ast_event *, void *);
|
||||
static void network_change_event_cb(const struct ast_event *, void *);
|
||||
|
||||
/*--- Applications, functions, CLI and manager command helpers */
|
||||
static const char *sip_nat_mode(const struct sip_pvt *p);
|
||||
|
@ -13435,6 +13439,39 @@ static void mwi_event_cb(const struct ast_event *event, void *userdata)
|
|||
ao2_unlock(peer);
|
||||
}
|
||||
|
||||
static void network_change_event_subscribe(void)
|
||||
{
|
||||
if (!network_change_event_subscription) {
|
||||
network_change_event_subscription = ast_event_subscribe(AST_EVENT_NETWORK_CHANGE,
|
||||
network_change_event_cb,
|
||||
"SIP Network Change ",
|
||||
NULL, AST_EVENT_IE_END);
|
||||
}
|
||||
}
|
||||
|
||||
static void network_change_event_unsubscribe(void)
|
||||
{
|
||||
if (network_change_event_subscription) {
|
||||
network_change_event_subscription = ast_event_unsubscribe(network_change_event_subscription);
|
||||
}
|
||||
}
|
||||
|
||||
static int network_change_event_sched_cb(const void *data)
|
||||
{
|
||||
network_change_event_sched_id = -1;
|
||||
sip_send_all_registers();
|
||||
sip_send_all_mwi_subscriptions();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void network_change_event_cb(const struct ast_event *event, void *userdata)
|
||||
{
|
||||
ast_debug(1, "SIP, got a network change event, renewing all SIP registrations.\n");
|
||||
if (network_change_event_sched_id == -1) {
|
||||
network_change_event_sched_id = ast_sched_add(sched, 1000, network_change_event_sched_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
|
||||
\note If you add an "hint" priority to the extension in the dial plan,
|
||||
you will get notifications on device state changes */
|
||||
|
@ -26140,6 +26177,7 @@ static int reload_config(enum channelreloadreason reason)
|
|||
int auto_sip_domains = FALSE;
|
||||
struct ast_sockaddr old_bindaddr = bindaddr;
|
||||
int registry_count = 0, peer_count = 0, timerb_set = 0, timert1_set = 0;
|
||||
int subscribe_network_change = 1;
|
||||
time_t run_start, run_end;
|
||||
struct sockaddr_in externaddr_sin;
|
||||
int bindport = 0;
|
||||
|
@ -26843,11 +26881,25 @@ static int reload_config(enum channelreloadreason reason)
|
|||
ast_log(LOG_WARNING, "'%s' is not a valid maxforwards value at line %d. Using default.\n", v->value, v->lineno);
|
||||
sip_cfg.default_max_forwards = DEFAULT_MAX_FORWARDS;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "subscribe_network_change_event")) {
|
||||
if (ast_true(v->value)) {
|
||||
subscribe_network_change = 1;
|
||||
} else if (ast_false(v->value)) {
|
||||
subscribe_network_change = 0;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "subscribe_network_change_event value %s is not valid at line %d.\n", v->value, v->lineno);
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
|
||||
ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
|
||||
}
|
||||
}
|
||||
|
||||
if (subscribe_network_change) {
|
||||
network_change_event_subscribe();
|
||||
} else {
|
||||
network_change_event_unsubscribe();
|
||||
}
|
||||
|
||||
if (global_t1 < global_t1min) {
|
||||
ast_log(LOG_WARNING, "'t1min' (%d) cannot be greater than 't1timer' (%d). Resetting 't1timer' to the value of 't1min'\n", global_t1min, global_t1);
|
||||
global_t1 = global_t1min;
|
||||
|
@ -28354,6 +28406,7 @@ static int load_module(void)
|
|||
|
||||
|
||||
sip_register_tests();
|
||||
network_change_event_subscribe();
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
@ -28366,6 +28419,8 @@ static int unload_module(void)
|
|||
struct ast_context *con;
|
||||
struct ao2_iterator i;
|
||||
|
||||
network_change_event_unsubscribe();
|
||||
|
||||
ast_sched_dump(sched);
|
||||
|
||||
/* First, take us out of the channel type list */
|
||||
|
|
|
@ -244,6 +244,15 @@ forcejitterbuffer=no
|
|||
;
|
||||
;register => FWDNumber:passwd@iax.fwdnet.net
|
||||
;
|
||||
; Through the use of the res_stun_monitor module, Asterisk has the ability to detect when the
|
||||
; perceived external network address has changed. When the stun_monitor is installed and
|
||||
; configured, chan_iax will renew all outbound registrations when the monitor detects any sort
|
||||
; of network change has occurred. By default this option is enabled, but only takes effect once
|
||||
; res_stun_monitor is configured. If res_stun_monitor is enabled and you wish to not
|
||||
; generate all outbound registrations on a network change, use the option below to disable
|
||||
; this feature.
|
||||
;
|
||||
; subscribe_network_change_event = yes ; on by default
|
||||
;
|
||||
; You can disable authentication debugging to reduce the amount of
|
||||
; debugging traffic.
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
;
|
||||
; Configuration file for the res_stun_monitor module
|
||||
;
|
||||
; The res_stun_monitor module sends STUN requests to a configured STUN server
|
||||
; periodically. If the monitor detects a change in the external ip or port
|
||||
; provided by the STUN server an event is sent out internally within Asterisk
|
||||
; to alert all listeners to that event of the change.
|
||||
|
||||
; The current default listeners for the netork change event include chan_sip
|
||||
; and chan_iax. Both of these channel drivers by default react to this event
|
||||
; by renewing all outbound registrations. This allows the endpoints Asterisk
|
||||
; is registering with to become aware of the address change and know the new
|
||||
; location.
|
||||
;
|
||||
[general]
|
||||
;
|
||||
; ---- STUN Server configuration ---
|
||||
; Setting the 'stunaddr' option to a valid address enables the stun monitor.
|
||||
;
|
||||
; stunaddr = mystunserver.com ; address of the stun server to query.
|
||||
; stunrefresh = 30 ; number of seconds between stun refreshes. default is 30
|
||||
;
|
|
@ -783,6 +783,16 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
|||
; can not be set per-user or per-peer.
|
||||
;
|
||||
; media_address = 172.16.42.1
|
||||
;
|
||||
; Through the use of the res_stun_monitor module, Asterisk has the ability to detect when the
|
||||
; perceived external network address has changed. When the stun_monitor is installed and
|
||||
; configured, chan_sip will renew all outbound registrations when the monitor detects any sort
|
||||
; of network change has occurred. By default this option is enabled, but only takes effect once
|
||||
; res_stun_monitor is configured. If res_stun_monitor is enabled and you wish to not
|
||||
; generate all outbound registrations on a network change, use the option below to disable
|
||||
; this feature.
|
||||
;
|
||||
; subscribe_network_change_event = yes ; on by default
|
||||
|
||||
;----------------------------------- MEDIA HANDLING --------------------------------
|
||||
; By default, Asterisk tries to re-invite media streams to an optimal path. If there's
|
||||
|
|
|
@ -52,8 +52,10 @@ enum ast_event_type {
|
|||
AST_EVENT_CEL = 0x07,
|
||||
/*! A report of a security related event (see security_events.h) */
|
||||
AST_EVENT_SECURITY = 0x08,
|
||||
/*! Used by res_stun_monitor to alert listeners to an exernal network address change. */
|
||||
AST_EVENT_NETWORK_CHANGE = 0x09,
|
||||
/*! Number of event types. This should be the last event type + 1 */
|
||||
AST_EVENT_TOTAL = 0x09,
|
||||
AST_EVENT_TOTAL = 0x0a,
|
||||
};
|
||||
|
||||
/*! \brief Event Information Element types */
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* David Vossel <dvossel@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
* \brief STUN Network Monitor
|
||||
*
|
||||
* \author David Vossel <dvossel@digium.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/event.h"
|
||||
#include "asterisk/sched.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/stun.h"
|
||||
#include "asterisk/netsock2.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include <fcntl.h>
|
||||
|
||||
static const int STANDARD_STUN_PORT = 3478;
|
||||
static const int DEFAULT_MONITOR_REFRESH = 30;
|
||||
|
||||
static const char stun_conf_file[] = "res_stun_monitor.conf";
|
||||
static struct ast_sched_thread *sched;
|
||||
|
||||
static struct {
|
||||
struct sockaddr_in stunaddr; /*!< The stun address we send requests to*/
|
||||
struct sockaddr_in externaladdr; /*!< current perceived external address. */
|
||||
ast_mutex_t lock;
|
||||
unsigned int refresh;
|
||||
int stunsock;
|
||||
unsigned int monitor_enabled:1;
|
||||
unsigned int externaladdr_known:1;
|
||||
} args;
|
||||
|
||||
static inline void stun_close_sock(void)
|
||||
{
|
||||
if (args.stunsock != -1) {
|
||||
close(args.stunsock);
|
||||
args.stunsock = -1;
|
||||
memset(&args.externaladdr, 0, sizeof(args.externaladdr));
|
||||
args.externaladdr_known = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* \brief purge the stun socket's receive buffer before issuing a new request
|
||||
*
|
||||
* XXX Note that this is somewhat of a hack. This function is essentially doing
|
||||
* a cleanup on the socket rec buffer to handle removing any STUN responses we have not
|
||||
* handled. This is called before sending out a new STUN request so we don't read
|
||||
* a latent previous response thinking it is new.
|
||||
*/
|
||||
static void stun_purge_socket(void)
|
||||
{
|
||||
int flags = fcntl(args.stunsock, F_GETFL);
|
||||
int res = 0;
|
||||
unsigned char reply_buf[1024];
|
||||
|
||||
fcntl(args.stunsock, F_SETFL, flags | O_NONBLOCK);
|
||||
while (res != -1) {
|
||||
/* throw away everything in the buffer until we reach the end. */
|
||||
res = recv(args.stunsock, reply_buf, sizeof(reply_buf), 0);
|
||||
}
|
||||
fcntl(args.stunsock, F_SETFL, flags & ~O_NONBLOCK);
|
||||
}
|
||||
|
||||
/* \brief called by scheduler to send STUN request */
|
||||
static int stun_monitor_request(const void *blarg)
|
||||
{
|
||||
int res;
|
||||
int generate_event = 0;
|
||||
struct sockaddr_in answer = { 0, };
|
||||
|
||||
|
||||
/* once the stun socket goes away, this scheduler item will go away as well */
|
||||
ast_mutex_lock(&args.lock);
|
||||
if (args.stunsock == -1) {
|
||||
ast_log(LOG_ERROR, "STUN monitor: can not send STUN request, socket is not open\n");
|
||||
goto monitor_request_cleanup;
|
||||
}
|
||||
|
||||
stun_purge_socket();
|
||||
|
||||
if (!(ast_stun_request(args.stunsock, &args.stunaddr, NULL, &answer)) &&
|
||||
(memcmp(&args.externaladdr, &answer, sizeof(args.externaladdr)))) {
|
||||
const char *newaddr = ast_strdupa(ast_inet_ntoa(answer.sin_addr));
|
||||
int newport = ntohs(answer.sin_port);
|
||||
|
||||
ast_log(LOG_NOTICE, "STUN MONITOR: Old external address/port %s:%d now seen as %s:%d \n",
|
||||
ast_inet_ntoa(args.externaladdr.sin_addr), ntohs(args.externaladdr.sin_port),
|
||||
newaddr, newport);
|
||||
|
||||
memcpy(&args.externaladdr, &answer, sizeof(args.externaladdr));
|
||||
|
||||
if (args.externaladdr_known) {
|
||||
/* the external address was already known, and has changed... generate event. */
|
||||
generate_event = 1;
|
||||
|
||||
} else {
|
||||
/* this was the first external address we found, do not alert listeners
|
||||
* until this address changes to something else. */
|
||||
args.externaladdr_known = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (generate_event) {
|
||||
struct ast_event *event = ast_event_new(AST_EVENT_NETWORK_CHANGE, AST_EVENT_IE_END);
|
||||
if (!event) {
|
||||
ast_log(LOG_ERROR, "STUN monitor: could not create AST_EVENT_NETWORK_CHANGE event.\n");
|
||||
goto monitor_request_cleanup;
|
||||
}
|
||||
if (ast_event_queue(event)) {
|
||||
ast_event_destroy(event);
|
||||
event = NULL;
|
||||
ast_log(LOG_ERROR, "STUN monitor: could not queue AST_EVENT_NETWORK_CHANGE event.\n");
|
||||
goto monitor_request_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
monitor_request_cleanup:
|
||||
/* always refresh this scheduler item. It will be removed elsewhere when
|
||||
* it is supposed to go away */
|
||||
res = args.refresh * 1000;
|
||||
ast_mutex_unlock(&args.lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* \brief stops the stun monitor thread
|
||||
* \note do not hold the args->lock while calling this
|
||||
*/
|
||||
static void stun_stop_monitor(void)
|
||||
{
|
||||
if (sched) {
|
||||
sched = ast_sched_thread_destroy(sched);
|
||||
ast_log(LOG_NOTICE, "STUN monitor stopped\n");
|
||||
}
|
||||
/* it is only safe to destroy the socket without holding arg->lock
|
||||
* after the sched thread is destroyed */
|
||||
stun_close_sock();
|
||||
}
|
||||
|
||||
/* \brief starts the stun monitor thread
|
||||
* \note The args->lock MUST be held when calling this function
|
||||
*/
|
||||
static int stun_start_monitor(void)
|
||||
{
|
||||
struct ast_sockaddr dst;
|
||||
/* clean up any previous open socket */
|
||||
stun_close_sock();
|
||||
|
||||
/* create destination ast_sockaddr */
|
||||
ast_sockaddr_from_sin(&dst, &args.stunaddr);
|
||||
|
||||
/* open new socket binding */
|
||||
args.stunsock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (args.stunsock < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to create STUN socket: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_connect(args.stunsock, &dst) != 0) {
|
||||
ast_log(LOG_WARNING, "SIP STUN Failed to connect to %s\n", ast_sockaddr_stringify(&dst));
|
||||
stun_close_sock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if scheduler thread is not started, make sure to start it now */
|
||||
if (sched) {
|
||||
return 0; /* already started */
|
||||
}
|
||||
|
||||
if (!(sched = ast_sched_thread_create())) {
|
||||
ast_log(LOG_ERROR, "Failed to create stun monitor scheduler thread\n");
|
||||
stun_close_sock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_sched_thread_add_variable(sched, (args.refresh * 1000), stun_monitor_request, NULL, 1) < 0) {
|
||||
ast_log(LOG_ERROR, "Unable to schedule STUN network monitor \n");
|
||||
sched = ast_sched_thread_destroy(sched);
|
||||
stun_close_sock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_log(LOG_NOTICE, "STUN monitor started\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_config(int startup)
|
||||
{
|
||||
struct ast_flags config_flags = { 0, };
|
||||
struct ast_config *cfg;
|
||||
struct ast_variable *v;
|
||||
|
||||
if (!startup) {
|
||||
ast_set_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
|
||||
}
|
||||
|
||||
if (!(cfg = ast_config_load2(stun_conf_file, "res_stun_monitor", config_flags)) ||
|
||||
cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
ast_log(LOG_ERROR, "Unable to load config %s\n", stun_conf_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cfg == CONFIG_STATUS_FILEUNCHANGED && !startup) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set defaults */
|
||||
args.monitor_enabled = 0;
|
||||
memset(&args.stunaddr, 0, sizeof(args.stunaddr));
|
||||
args.refresh = DEFAULT_MONITOR_REFRESH;
|
||||
|
||||
for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
|
||||
if (!strcasecmp(v->name, "stunaddr")) {
|
||||
args.stunaddr.sin_port = htons(STANDARD_STUN_PORT);
|
||||
if (ast_parse_arg(v->value, PARSE_INADDR, &args.stunaddr)) {
|
||||
ast_log(LOG_WARNING, "Invalid STUN server address: %s\n", v->value);
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "STUN monitor enabled: %s\n", v->value);
|
||||
args.monitor_enabled = 1;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "stunrefresh")) {
|
||||
if ((sscanf(v->value, "%30u", &args.refresh) != 1) || !args.refresh) {
|
||||
ast_log(LOG_WARNING, "Invalid stunrefresh value '%s', must be an integer > 0 at line %d\n", v->value, v->lineno);
|
||||
args.refresh = DEFAULT_MONITOR_REFRESH;
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "STUN Monitor set to refresh every %d seconds\n", args.refresh);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "SIP STUN: invalid config option %s at line %d\n", v->value, v->lineno);
|
||||
}
|
||||
}
|
||||
|
||||
ast_config_destroy(cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __reload(int startup)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_mutex_lock(&args.lock);
|
||||
if (!(res = load_config(startup)) && args.monitor_enabled) {
|
||||
res = stun_start_monitor();
|
||||
}
|
||||
ast_mutex_unlock(&args.lock);
|
||||
|
||||
if ((res == -1) || !args.monitor_enabled) {
|
||||
args.monitor_enabled = 0;
|
||||
stun_stop_monitor();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int reload(void)
|
||||
{
|
||||
return __reload(0);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stun_stop_monitor();
|
||||
ast_mutex_destroy(&args.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
ast_mutex_init(&args.lock);
|
||||
args.stunsock = -1;
|
||||
memset(&args.externaladdr, 0, sizeof(args.externaladdr));
|
||||
args.externaladdr_known = 0;
|
||||
sched = NULL;
|
||||
if (__reload(1)) {
|
||||
stun_stop_monitor();
|
||||
ast_mutex_destroy(&args.lock);
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "STUN Network Monitor",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
.load_pri = AST_MODPRI_CHANNEL_DEPEND
|
||||
);
|
Reference in New Issue