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 language[MAX_LANGUAGE] = "";
|
||||||
static char regcontext[AST_MAX_CONTEXT] = "";
|
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 maxauthreq = 3;
|
||||||
static int max_retries = 4;
|
static int max_retries = 4;
|
||||||
static int ping_time = 21;
|
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_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_transfer(struct ast_channel *c, const char *dest);
|
||||||
static int iax2_write(struct ast_channel *c, struct ast_frame *f);
|
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_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(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);
|
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 struct callno_entry *get_unused_callno(int trunk, int validated);
|
||||||
static int replace_callno(const void *obj);
|
static int replace_callno(const void *obj);
|
||||||
static void sched_delay_remove(struct sockaddr_in *sin, struct callno_entry *callno_entry);
|
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 = {
|
static const struct ast_channel_tech iax2_tech = {
|
||||||
.type = "IAX2",
|
.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. */
|
* 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
|
/*! \brief Send manager event at call setup to link between Asterisk channel name
|
||||||
and IAX2 call identifiers */
|
and IAX2 call identifiers */
|
||||||
static void iax2_ami_channelupdate(struct chan_iax2_pvt *pvt)
|
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 : "");
|
pvt->callno, pvt->peercallno, pvt->peer ? pvt->peer : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct ast_datastore_info iax2_variable_datastore_info = {
|
static struct ast_datastore_info iax2_variable_datastore_info = {
|
||||||
.type = "IAX2_VARIABLE",
|
.type = "IAX2_VARIABLE",
|
||||||
.duplicate = iax2_dup_variable_datastore,
|
.duplicate = iax2_dup_variable_datastore,
|
||||||
|
@ -12779,7 +12825,8 @@ static int set_config(const char *config_file, int reload)
|
||||||
int format;
|
int format;
|
||||||
int portno = IAX_DEFAULT_PORTNO;
|
int portno = IAX_DEFAULT_PORTNO;
|
||||||
int x;
|
int x;
|
||||||
int mtuv;
|
int mtuv;
|
||||||
|
int subscribe_network_change = 1;
|
||||||
struct iax2_user *user;
|
struct iax2_user *user;
|
||||||
struct iax2_peer *peer;
|
struct iax2_peer *peer;
|
||||||
struct ast_netsock *ns;
|
struct ast_netsock *ns;
|
||||||
|
@ -13097,6 +13144,14 @@ static int set_config(const char *config_file, int reload)
|
||||||
if (add_calltoken_ignore(v->value)) {
|
if (add_calltoken_ignore(v->value)) {
|
||||||
ast_log(LOG_WARNING, "Invalid calltokenoptional address range - '%s' line %d\n", v->value, v->lineno);
|
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")) {
|
} else if (!strcasecmp(v->name, "shrinkcallerid")) {
|
||||||
if (ast_true(v->value)) {
|
if (ast_true(v->value)) {
|
||||||
ast_set_flag64((&globalflags), IAX_SHRINKCALLERID);
|
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); */
|
/* ast_log(LOG_WARNING, "Ignoring %s\n", v->name); */
|
||||||
v = v->next;
|
v = v->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (subscribe_network_change) {
|
||||||
|
network_change_event_subscribe();
|
||||||
|
} else {
|
||||||
|
network_change_event_unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
if (defaultsockfd < 0) {
|
if (defaultsockfd < 0) {
|
||||||
if (!(ns = ast_netsock_bind(netsock, io, "0.0.0.0", portno, qos.tos, qos.cos, socket_read, NULL))) {
|
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));
|
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;
|
struct ast_context *con;
|
||||||
int x;
|
int x;
|
||||||
|
|
||||||
|
network_change_event_unsubscribe();
|
||||||
|
|
||||||
ast_manager_unregister("IAXpeers");
|
ast_manager_unregister("IAXpeers");
|
||||||
ast_manager_unregister("IAXpeerlist");
|
ast_manager_unregister("IAXpeerlist");
|
||||||
ast_manager_unregister("IAXnetstats");
|
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);
|
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;
|
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 struct ast_flags global_flags[3] = {{0}}; /*!< global SIP_ flags */
|
||||||
static int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */
|
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 */
|
static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */
|
||||||
|
|
||||||
AST_MUTEX_DEFINE_STATIC(netlock);
|
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_poke_all_peers(void);
|
||||||
static void sip_peer_hold(struct sip_pvt *p, int hold);
|
static void sip_peer_hold(struct sip_pvt *p, int hold);
|
||||||
static void mwi_event_cb(const struct ast_event *, void *);
|
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 */
|
/*--- Applications, functions, CLI and manager command helpers */
|
||||||
static const char *sip_nat_mode(const struct sip_pvt *p);
|
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);
|
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
|
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
|
||||||
\note If you add an "hint" priority to the extension in the dial plan,
|
\note If you add an "hint" priority to the extension in the dial plan,
|
||||||
you will get notifications on device state changes */
|
you will get notifications on device state changes */
|
||||||
|
@ -26140,6 +26177,7 @@ static int reload_config(enum channelreloadreason reason)
|
||||||
int auto_sip_domains = FALSE;
|
int auto_sip_domains = FALSE;
|
||||||
struct ast_sockaddr old_bindaddr = bindaddr;
|
struct ast_sockaddr old_bindaddr = bindaddr;
|
||||||
int registry_count = 0, peer_count = 0, timerb_set = 0, timert1_set = 0;
|
int registry_count = 0, peer_count = 0, timerb_set = 0, timert1_set = 0;
|
||||||
|
int subscribe_network_change = 1;
|
||||||
time_t run_start, run_end;
|
time_t run_start, run_end;
|
||||||
struct sockaddr_in externaddr_sin;
|
struct sockaddr_in externaddr_sin;
|
||||||
int bindport = 0;
|
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);
|
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;
|
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")) {
|
} else if (!strcasecmp(v->name, "snom_aoc_enabled")) {
|
||||||
ast_set2_flag(&global_flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC);
|
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) {
|
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);
|
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;
|
global_t1 = global_t1min;
|
||||||
|
@ -28354,6 +28406,7 @@ static int load_module(void)
|
||||||
|
|
||||||
|
|
||||||
sip_register_tests();
|
sip_register_tests();
|
||||||
|
network_change_event_subscribe();
|
||||||
|
|
||||||
return AST_MODULE_LOAD_SUCCESS;
|
return AST_MODULE_LOAD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -28366,6 +28419,8 @@ static int unload_module(void)
|
||||||
struct ast_context *con;
|
struct ast_context *con;
|
||||||
struct ao2_iterator i;
|
struct ao2_iterator i;
|
||||||
|
|
||||||
|
network_change_event_unsubscribe();
|
||||||
|
|
||||||
ast_sched_dump(sched);
|
ast_sched_dump(sched);
|
||||||
|
|
||||||
/* First, take us out of the channel type list */
|
/* First, take us out of the channel type list */
|
||||||
|
|
|
@ -244,6 +244,15 @@ forcejitterbuffer=no
|
||||||
;
|
;
|
||||||
;register => FWDNumber:passwd@iax.fwdnet.net
|
;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
|
; You can disable authentication debugging to reduce the amount of
|
||||||
; debugging traffic.
|
; 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.
|
; can not be set per-user or per-peer.
|
||||||
;
|
;
|
||||||
; media_address = 172.16.42.1
|
; 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 --------------------------------
|
;----------------------------------- MEDIA HANDLING --------------------------------
|
||||||
; By default, Asterisk tries to re-invite media streams to an optimal path. If there's
|
; 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,
|
AST_EVENT_CEL = 0x07,
|
||||||
/*! A report of a security related event (see security_events.h) */
|
/*! A report of a security related event (see security_events.h) */
|
||||||
AST_EVENT_SECURITY = 0x08,
|
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 */
|
/*! 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 */
|
/*! \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