mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-04 11:58:52 +00:00
Add support for lightweight NAT keepalive.
If enabled using the keepalive option in sip.conf a small packet will be sent at a regular interval to keep the NAT mapping open. This is lightweight as the remote side does not need to parse and handle a SIP message. (closes issue AST-783) Review: https://reviewboard.asterisk.org/r/1756/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@364500 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
3
CHANGES
3
CHANGES
@@ -76,6 +76,9 @@ SIP Changes
|
||||
the remote endpoint.
|
||||
* Adds an option send_diversion which can be disabled to prevent
|
||||
diversion headers from automatically being added to invites.
|
||||
* Add support for lightweight NAT keepalive. If enabled a blank packet will
|
||||
be sent to the remote host at a given interval to keep the NAT mapping open.
|
||||
This can be enabled using the keepalive configuration option.
|
||||
|
||||
Chan_local changes
|
||||
------------------
|
||||
|
@@ -688,6 +688,7 @@ static int default_fromdomainport; /*!< Default domain port on o
|
||||
static char default_notifymime[AST_MAX_EXTENSION]; /*!< Default MIME media type for MWI notify messages */
|
||||
static char default_vmexten[AST_MAX_EXTENSION]; /*!< Default From Username on MWI updates */
|
||||
static int default_qualify; /*!< Default Qualify= setting */
|
||||
static int default_keepalive; /*!< Default keepalive= setting */
|
||||
static char default_mohinterpret[MAX_MUSICCLASS]; /*!< Global setting for moh class to use when put on hold */
|
||||
static char default_mohsuggest[MAX_MUSICCLASS]; /*!< Global setting for moh class to suggest when putting
|
||||
* a bridged channel on hold */
|
||||
@@ -1379,6 +1380,7 @@ 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 *);
|
||||
static void sip_keepalive_all_peers(void);
|
||||
|
||||
/*--- Applications, functions, CLI and manager command helpers */
|
||||
static const char *sip_nat_mode(const struct sip_pvt *p);
|
||||
@@ -2881,6 +2883,10 @@ static void peer_sched_cleanup(struct sip_peer *peer)
|
||||
AST_SCHED_DEL_UNREF(sched, peer->expire,
|
||||
sip_unref_peer(peer, "remove register expire ref"));
|
||||
}
|
||||
if (peer->keepalivesend != -1) {
|
||||
AST_SCHED_DEL_UNREF(sched, peer->keepalivesend,
|
||||
sip_unref_peer(peer, "remove keepalive peer ref"));
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
@@ -18301,6 +18307,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
|
||||
ast_cli(fd, " Useragent : %s\n", peer->useragent);
|
||||
ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact);
|
||||
ast_cli(fd, " Qualify Freq : %d ms\n", peer->qualifyfreq);
|
||||
ast_cli(fd, " Keepalive : %d ms\n", peer->keepalive * 1000);
|
||||
if (peer->chanvars) {
|
||||
ast_cli(fd, " Variables :\n");
|
||||
for (v = peer->chanvars ; v ; v = v->next)
|
||||
@@ -18974,6 +18981,7 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
|
||||
ast_cli(a->fd, " Force rport: %s\n", force_rport_string(global_flags));
|
||||
ast_cli(a->fd, " DTMF: %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF)));
|
||||
ast_cli(a->fd, " Qualify: %d\n", default_qualify);
|
||||
ast_cli(a->fd, " Keepalive: %d\n", default_keepalive);
|
||||
ast_cli(a->fd, " Use ClientCode: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_USECLIENTCODE)));
|
||||
ast_cli(a->fd, " Progress inband: %s\n", (ast_test_flag(&global_flags[0], SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER) ? "Never" : (AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROG_INBAND) != SIP_PROG_INBAND_NO)));
|
||||
ast_cli(a->fd, " Language: %s\n", default_language);
|
||||
@@ -27312,6 +27320,56 @@ enum st_mode st_get_mode(struct sip_pvt *p, int no_cached)
|
||||
return global_st_mode;
|
||||
}
|
||||
|
||||
/*! \brief Send keep alive packet to peer */
|
||||
static int sip_send_keepalive(const void *data)
|
||||
{
|
||||
struct sip_peer *peer = (struct sip_peer*) data;
|
||||
int res = 0;
|
||||
const char keepalive[] = "\r\n";
|
||||
|
||||
peer->keepalivesend = -1;
|
||||
|
||||
if (!peer->keepalive || ast_sockaddr_isnull(&peer->addr)) {
|
||||
sip_unref_peer(peer, "release keepalive peer ref");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send the packet out using the proper method for this peer */
|
||||
if ((peer->socket.fd != -1) && (peer->socket.type == SIP_TRANSPORT_UDP)) {
|
||||
res = ast_sendto(peer->socket.fd, keepalive, sizeof(keepalive), 0, &peer->addr);
|
||||
} else if ((peer->socket.type & (SIP_TRANSPORT_TCP | SIP_TRANSPORT_TLS)) &&
|
||||
(peer->socket.tcptls_session) &&
|
||||
(peer->socket.tcptls_session->fd != -1)) {
|
||||
res = sip_tcptls_write(peer->socket.tcptls_session, keepalive, sizeof(keepalive));
|
||||
} else if (peer->socket.type == SIP_TRANSPORT_UDP) {
|
||||
res = ast_sendto(sipsock, keepalive, sizeof(keepalive), 0, &peer->addr);
|
||||
}
|
||||
|
||||
if (res == -1) {
|
||||
switch (errno) {
|
||||
case EBADF: /* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
|
||||
case EHOSTUNREACH: /* Host can't be reached */
|
||||
case ENETDOWN: /* Interface down */
|
||||
case ENETUNREACH: /* Network failure */
|
||||
case ECONNREFUSED: /* ICMP port unreachable */
|
||||
res = XMIT_ERROR; /* Don't bother with trying to transmit again */
|
||||
}
|
||||
}
|
||||
|
||||
if (res != sizeof(keepalive)) {
|
||||
ast_log(LOG_WARNING, "sip_send_keepalive to %s returned %d: %s\n", ast_sockaddr_stringify(&peer->addr), res, strerror(errno));
|
||||
}
|
||||
|
||||
AST_SCHED_REPLACE_UNREF(peer->keepalivesend, sched,
|
||||
peer->keepalive * 1000, sip_send_keepalive, peer,
|
||||
sip_unref_peer(_data, "removing keepalive peer ref"),
|
||||
sip_unref_peer(peer, "removing keepalive peer ref"),
|
||||
sip_ref_peer(peer, "adding keepalive peer ref"));
|
||||
|
||||
sip_unref_peer(peer, "release keepalive peer ref");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief React to lack of answer to Qualify poke */
|
||||
static int sip_poke_noanswer(const void *data)
|
||||
@@ -28175,6 +28233,7 @@ static void set_peer_defaults(struct sip_peer *peer)
|
||||
*/
|
||||
peer->expire = -1;
|
||||
peer->pokeexpire = -1;
|
||||
peer->keepalivesend = -1;
|
||||
set_socket_transport(&peer->socket, SIP_TRANSPORT_UDP);
|
||||
}
|
||||
peer->type = SIP_TYPE_PEER;
|
||||
@@ -28217,6 +28276,7 @@ static void set_peer_defaults(struct sip_peer *peer)
|
||||
peer->callgroup = 0;
|
||||
peer->pickupgroup = 0;
|
||||
peer->maxms = default_qualify;
|
||||
peer->keepalive = default_keepalive;
|
||||
peer->prefs = default_prefs;
|
||||
ast_string_field_set(peer, zone, default_zone);
|
||||
peer->stimer.st_mode_oper = global_st_mode; /* Session-Timers */
|
||||
@@ -28816,6 +28876,15 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
|
||||
ast_log(LOG_WARNING, "Qualify is incompatible with dynamic uncached realtime. Please either turn rtcachefriends on or turn qualify off on peer '%s'\n", peer->name);
|
||||
peer->maxms = 0;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "keepalive")) {
|
||||
if (!strcasecmp(v->value, "no")) {
|
||||
peer->keepalive = 0;
|
||||
} else if (!strcasecmp(v->value, "yes")) {
|
||||
peer->keepalive = DEFAULT_KEEPALIVE_INTERVAL;
|
||||
} else if (sscanf(v->value, "%30d", &peer->keepalive) != 1) {
|
||||
ast_log(LOG_WARNING, "Keep alive of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", peer->name, v->lineno);
|
||||
peer->keepalive = 0;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "callcounter")) {
|
||||
peer->call_limit = ast_true(v->value) ? INT_MAX : 0;
|
||||
} else if (!strcasecmp(v->name, "call-limit")) {
|
||||
@@ -29280,6 +29349,7 @@ static int reload_config(enum channelreloadreason reason)
|
||||
default_fromdomain[0] = '\0';
|
||||
default_fromdomainport = 0;
|
||||
default_qualify = DEFAULT_QUALIFY;
|
||||
default_keepalive = DEFAULT_KEEPALIVE;
|
||||
default_zone[0] = '\0';
|
||||
default_maxcallbitrate = DEFAULT_MAX_CALL_BITRATE;
|
||||
ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
|
||||
@@ -29756,6 +29826,15 @@ static int reload_config(enum channelreloadreason reason)
|
||||
ast_log(LOG_WARNING, "Qualification default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
|
||||
default_qualify = 0;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "keepalive")) {
|
||||
if (!strcasecmp(v->value, "no")) {
|
||||
default_keepalive = 0;
|
||||
} else if (!strcasecmp(v->value, "yes")) {
|
||||
default_keepalive = DEFAULT_KEEPALIVE_INTERVAL;
|
||||
} else if (sscanf(v->value, "%30d", &default_keepalive) != 1) {
|
||||
ast_log(LOG_WARNING, "Keep alive default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
|
||||
default_keepalive = 0;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "qualifyfreq")) {
|
||||
int i;
|
||||
if (sscanf(v->value, "%30d", &i) == 1) {
|
||||
@@ -30737,6 +30816,29 @@ static void sip_poke_all_peers(void)
|
||||
ao2_iterator_destroy(&i);
|
||||
}
|
||||
|
||||
/*! \brief Send a keepalive to all known peers */
|
||||
static void sip_keepalive_all_peers(void)
|
||||
{
|
||||
struct ao2_iterator i;
|
||||
struct sip_peer *peer;
|
||||
|
||||
if (!speerobjs) { /* No peers, just give up */
|
||||
return;
|
||||
}
|
||||
|
||||
i = ao2_iterator_init(peers, 0);
|
||||
while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
|
||||
ao2_lock(peer);
|
||||
AST_SCHED_REPLACE_UNREF(peer->keepalivesend, sched, 0, sip_send_keepalive, peer,
|
||||
sip_unref_peer(_data, "removing poke peer ref"),
|
||||
sip_unref_peer(peer, "removing poke peer ref"),
|
||||
sip_ref_peer(peer, "adding poke peer ref"));
|
||||
ao2_unlock(peer);
|
||||
sip_unref_peer(peer, "toss iterator peer ptr");
|
||||
}
|
||||
ao2_iterator_destroy(&i);
|
||||
}
|
||||
|
||||
/*! \brief Send all known registrations */
|
||||
static void sip_send_all_registers(void)
|
||||
{
|
||||
@@ -30842,6 +30944,9 @@ static int sip_do_reload(enum channelreloadreason reason)
|
||||
/* Send qualify (OPTIONS) to all peers */
|
||||
sip_poke_all_peers();
|
||||
|
||||
/* Send keepalive to all peers */
|
||||
sip_keepalive_all_peers();
|
||||
|
||||
/* Register with all services */
|
||||
sip_send_all_registers();
|
||||
|
||||
@@ -31604,7 +31709,8 @@ static int load_module(void)
|
||||
ast_manager_register_xml("SIPqualifypeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_qualify_peer);
|
||||
ast_manager_register_xml("SIPshowregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_show_registry);
|
||||
ast_manager_register_xml("SIPnotify", EVENT_FLAG_SYSTEM, manager_sipnotify);
|
||||
sip_poke_all_peers();
|
||||
sip_poke_all_peers();
|
||||
sip_keepalive_all_peers();
|
||||
sip_send_all_registers();
|
||||
sip_send_all_mwi_subscriptions();
|
||||
initialize_escs();
|
||||
|
@@ -212,6 +212,8 @@
|
||||
#define DEFAULT_AUTOCREATEPEER AUTOPEERS_DISABLED /*!< Don't create peers automagically */
|
||||
#define DEFAULT_MATCHEXTERNADDRLOCALLY FALSE /*!< Match extern IP locally default setting */
|
||||
#define DEFAULT_QUALIFY FALSE /*!< Don't monitor devices */
|
||||
#define DEFAULT_KEEPALIVE 0 /*!< Don't send keep alive packets */
|
||||
#define DEFAULT_KEEPALIVE_INTERVAL 60 /*!< Send keep alive packets at 60 second intervals */
|
||||
#define DEFAULT_CALLEVENTS FALSE /*!< Extra manager SIP call events */
|
||||
#define DEFAULT_ALWAYSAUTHREJECT TRUE /*!< Don't reject authentication requests always */
|
||||
#define DEFAULT_AUTH_OPTIONS FALSE
|
||||
@@ -1319,6 +1321,8 @@ struct sip_peer {
|
||||
int maxms; /*!< Qualification: Max ms we will accept for the host to be up, 0 to not monitor */
|
||||
int qualifyfreq; /*!< Qualification: Qualification: How often to check for the host to be up */
|
||||
struct timeval ps; /*!< Qualification: Time for sending SIP OPTION in sip_pke_peer() */
|
||||
int keepalive; /*!< Keepalive: How often to send keep alive packet */
|
||||
int keepalivesend; /*!< Keepalive: Scheduled item for sending keep alive packet */
|
||||
struct ast_sockaddr defaddr; /*!< Default IP address, used until registration */
|
||||
struct ast_ha *ha; /*!< Access control list */
|
||||
struct ast_ha *contactha; /*!< Restrict what IPs are allowed in the Contact header (for registration) */
|
||||
|
@@ -273,6 +273,9 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
||||
; Default: 100
|
||||
;qualifypeers=1 ; Number of peers in a group to be qualified at the same time
|
||||
; Default: 1
|
||||
;keepalive=60 ; Interval at which keepalive packets should be sent to a peer
|
||||
; Valid options are yes (60 seconds), no, or the number of seconds.
|
||||
; Default: 0
|
||||
;notifymimetype=text/plain ; Allow overriding of mime type in MWI NOTIFY
|
||||
;buggymwi=no ; Cisco SIP firmware doesn't support the MWI RFC
|
||||
; fully. Enable this option to not get error messages
|
||||
@@ -1194,6 +1197,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
|
||||
; host
|
||||
; port
|
||||
; qualify
|
||||
; keepalive
|
||||
; defaultip
|
||||
; defaultuser
|
||||
; rtptimeout
|
||||
|
Reference in New Issue
Block a user