dect
/
asterisk
Archived
13
0
Fork 0

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:
dvossel 2010-08-13 20:05:44 +00:00
parent eb0ec7df90
commit 30ec863881
7 changed files with 478 additions and 4 deletions

View File

@ -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(&registrations);
AST_LIST_TRAVERSE(&registrations, reg, entry) {
iax2_do_register(reg);
}
AST_LIST_UNLOCK(&registrations);
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;
}

View File

@ -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 */

View File

@ -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.

View File

@ -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
;

View File

@ -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

View File

@ -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 */

311
res/res_stun_monitor.c Normal file
View File

@ -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
);