mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-29 15:36:13 +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(); | ||||
| 
 | ||||
| @@ -31605,6 +31710,7 @@ static int load_module(void) | ||||
| 	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_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