Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 380806)
+++ channels/chan_sip.c	(working copy)
@@ -3972,7 +3972,18 @@
 static void ast_sip_ouraddrfor(const struct ast_sockaddr *them, struct ast_sockaddr *us, struct sip_pvt *p)
 {
 	struct ast_sockaddr theirs;
+	struct ast_sockaddr *peer_externaddr;
+	struct sip_peer *peer;
 
+	/* 
+	 * Pick which externaddr to use. "externaddr" is a new option for per-peer settings
+	 * We check with configured peer devices if any has "externaddr"(peer->externip), that matches "them"(p->sa).
+	 * Otherwise, use the default one. 
+	 *  
+	 */
+	peer = sip_find_peer(NULL, &p->sa, TRUE, FINDPEERS, FALSE, p->socket.type);
+	peer_externaddr = (peer) ? (!ast_sockaddr_isnull(&peer->externaddr) ? &peer->externaddr : &externaddr) : &externaddr;
+	
 	/* Set want_remap to non-zero if we want to remap 'us' to an externally
 	 * reachable IP address and port. This is done if:
 	 * 1. we have a localaddr list (containing 'internal' addresses marked
@@ -3993,14 +4004,14 @@
 	ast_sockaddr_copy(&theirs, them);
 
 	if (ast_sockaddr_is_ipv6(&theirs)) {
-		if (localaddr && !ast_sockaddr_isnull(&externaddr) && !ast_sockaddr_is_any(&bindaddr)) {
+		if (localaddr && !ast_sockaddr_isnull(peer_externaddr) && !ast_sockaddr_is_any(&bindaddr)) {
 			ast_log(LOG_WARNING, "Address remapping activated in sip.conf "
 				"but we're using IPv6, which doesn't need it. Please "
 				"remove \"localnet\" and/or \"externaddr\" settings.\n");
 		}
 	} else {
 		want_remap = localaddr &&
-			!ast_sockaddr_isnull(&externaddr) &&
+			!ast_sockaddr_isnull(peer_externaddr) &&
 			ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
 	}
 
@@ -4008,18 +4019,18 @@
 	    (!sip_cfg.matchexternaddrlocally || !ast_apply_ha(localaddr, us)) ) {
 		/* if we used externhost, see if it is time to refresh the info */
 		if (externexpire && time(NULL) >= externexpire) {
-			if (ast_sockaddr_resolve_first(&externaddr, externhost, 0)) {
+			if (ast_sockaddr_resolve_first(peer_externaddr, externhost, 0)) {
 				ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
 			}
 			externexpire = time(NULL) + externrefresh;
 		}
-		if (!ast_sockaddr_isnull(&externaddr)) {
-			ast_sockaddr_copy(us, &externaddr);
+		if (!ast_sockaddr_isnull(peer_externaddr)) {
+			ast_sockaddr_copy(us, peer_externaddr);
 			switch (p->socket.type) {
 			case SIP_TRANSPORT_TCP:
-				if (!externtcpport && ast_sockaddr_port(&externaddr)) {
+				if (!externtcpport && ast_sockaddr_port(peer_externaddr)) {
 					/* for consistency, default to the externaddr port */
-					externtcpport = ast_sockaddr_port(&externaddr);
+					externtcpport = ast_sockaddr_port(peer_externaddr);
 				}
 				ast_sockaddr_set_port(us, externtcpport);
 				break;
@@ -4027,7 +4038,7 @@
 				ast_sockaddr_set_port(us, externtlsport);
 				break;
 			case SIP_TRANSPORT_UDP:
-				if (!ast_sockaddr_port(&externaddr)) {
+				if (!ast_sockaddr_port(peer_externaddr)) {
 					ast_sockaddr_set_port(us, ast_sockaddr_port(&bindaddr));
 				}
 				break;
@@ -4035,8 +4046,8 @@
 				break;
 			}
 		}
-		ast_debug(1, "Target address %s is not local, substituting externaddr\n",
-			  ast_sockaddr_stringify(them));
+		ast_debug(1, "Target address %s is not local, substituting with externaddr %s\n",
+			  ast_sockaddr_stringify(them), ast_sockaddr_stringify(peer_externaddr));
 	} else {
 		/* no remapping, but we bind to a specific address, so use it. */
 		switch (p->socket.type) {
@@ -30270,6 +30281,7 @@
 	ast_string_field_set(peer, engine, default_engine);
 	ast_sockaddr_setnull(&peer->addr);
 	ast_sockaddr_setnull(&peer->defaddr);
+	ast_sockaddr_setnull(&peer->externaddr);
 	ast_format_cap_copy(peer->caps, sip_cfg.caps);
 	peer->maxcallbitrate = default_maxcallbitrate;
 	peer->rtptimeout = global_rtptimeout;
@@ -30537,6 +30549,17 @@
 						peer->default_outbound_transport = peer->transports;
 					}
 				}
+			} else if (!strcasecmp(v->name, "externaddr")) {
+				if (localaddr == NULL) {
+					ast_log(LOG_ERROR, "Externaddr for peer %s not enabled, since we have no local networks configured in [general]\n", peer->name);
+				} else {
+					if (!ast_strlen_zero(v->value) && ast_parse_arg(v->value, PARSE_ADDR, &peer->externaddr)) {
+						ast_log(LOG_WARNING, "Invalid address for externaddr keyword: %s for peer %s\n", v->value, peer->name);
+					}
+					else if (!ast_sockaddr_port(&peer->externaddr) && ast_sockaddr_port(&bindaddr)) {
+						ast_sockaddr_set_port(&peer->externaddr, ast_sockaddr_port(&bindaddr));					  
+					}					
+				}				
 			} else if (realtime && !strcasecmp(v->name, "regseconds")) {
 				ast_get_time_t(v->value, &regseconds, 0, NULL);
 			} else if (realtime && !strcasecmp(v->name, "name")) {
@@ -31761,7 +31784,7 @@
 			if (ast_parse_arg(v->value, PARSE_ADDR, &media_address))
 				ast_log(LOG_WARNING, "Invalid address for media_address keyword: %s\n", v->value);
 		} else if (!strcasecmp(v->name, "externaddr") || !strcasecmp(v->name, "externip")) {
-			if (ast_parse_arg(v->value, PARSE_ADDR, &externaddr)) {
+			if (!ast_strlen_zero(v->value) && ast_parse_arg(v->value, PARSE_ADDR, &externaddr)) {
 				ast_log(LOG_WARNING,
 					"Invalid address for externaddr keyword: %s\n",
 					v->value);
Index: channels/sip/include/sip.h
===================================================================
--- channels/sip/include/sip.h	(revision 380806)
+++ channels/sip/include/sip.h	(working copy)
@@ -1373,7 +1373,7 @@
 	enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */
 	unsigned int disallowed_methods;
 	struct ast_cc_config_params *cc_params;
-
+	struct ast_sockaddr externaddr; /*!<  External IP to use for peer's connections */
 	struct ast_rtp_dtls_cfg dtls_cfg;
 };
 
Index: CHANGES
===================================================================
--- CHANGES	(revision 380806)
+++ CHANGES	(working copy)
@@ -358,6 +358,8 @@
 
 chan_sip
 ------------------
+ * externaddr is now a valid setting for SIP peer devices in sip.conf
+
  * Asterisk will no longer substitute CID number for CID name in the display
    name field if CID number exists without a CID name. This change improves
    compatibility with certain device features such as Avaya IP500's directory
