[svn-commits] file: trunk r364500 - in /trunk: ./ channels/ channels/sip/include/ configs/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sat Apr 28 15:24:54 CDT 2012


Author: file
Date: Sat Apr 28 15:24:45 2012
New Revision: 364500

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=364500
Log:
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/

Modified:
    trunk/CHANGES
    trunk/channels/chan_sip.c
    trunk/channels/sip/include/sip.h
    trunk/configs/sip.conf.sample

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=364500&r1=364499&r2=364500
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Sat Apr 28 15:24:45 2012
@@ -76,6 +76,9 @@
    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
 ------------------

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=364500&r1=364499&r2=364500
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Sat Apr 28 15:24:45 2012
@@ -688,6 +688,7 @@
 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_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);
@@ -2880,6 +2882,10 @@
 	if (peer->expire != -1) {
 		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"));
 	}
 }
 
@@ -18301,6 +18307,7 @@
 		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 @@
 	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 @@
 	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 @@
 		*/
 		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 @@
 	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 @@
 				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 @@
 	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));
@@ -29755,6 +29825,15 @@
 			} else if (sscanf(v->value, "%30d", &default_qualify) != 1) {
 				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;
@@ -30737,6 +30816,29 @@
 	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)
 {
@@ -30841,6 +30943,9 @@
 
 	/* 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 @@
 	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();

Modified: trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/include/sip.h?view=diff&rev=364500&r1=364499&r2=364500
==============================================================================
--- trunk/channels/sip/include/sip.h (original)
+++ trunk/channels/sip/include/sip.h Sat Apr 28 15:24:45 2012
@@ -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 @@
 	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) */

Modified: trunk/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/sip.conf.sample?view=diff&rev=364500&r1=364499&r2=364500
==============================================================================
--- trunk/configs/sip.conf.sample (original)
+++ trunk/configs/sip.conf.sample Sat Apr 28 15:24:45 2012
@@ -273,6 +273,9 @@
 				; 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 @@
 ; host
 ; port
 ; qualify
+; keepalive
 ; defaultip
 ; defaultuser
 ; rtptimeout




More information about the svn-commits mailing list