diff -rupN asterisk-1.8.32.2_orig//channels/chan_sip.c asterisk-1.8.32.2_sip_patch//channels/chan_sip.c --- asterisk-1.8.32.2_orig//channels/chan_sip.c 2014-10-30 02:57:11.000000000 +0100 +++ asterisk-1.8.32.2_sip_patch//channels/chan_sip.c 2015-03-08 16:52:01.542932000 +0100 @@ -3578,7 +3578,17 @@ static void build_via(struct sip_pvt *p) 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 = 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 @@ -3599,14 +3609,14 @@ static void ast_sip_ouraddrfor(const str 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 ; } @@ -3614,18 +3624,18 @@ static void ast_sip_ouraddrfor(const str (!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; @@ -3633,7 +3643,7 @@ static void ast_sip_ouraddrfor(const str 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; @@ -3641,8 +3651,8 @@ static void ast_sip_ouraddrfor(const str 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) { @@ -27966,6 +27976,7 @@ static void set_peer_defaults(struct sip ast_string_field_set(peer, engine, default_engine); ast_sockaddr_setnull(&peer->addr); ast_sockaddr_setnull(&peer->defaddr); + ast_sockaddr_setnull(&peer->externaddr); peer->capability = sip_cfg.capability; peer->maxcallbitrate = default_maxcallbitrate; peer->rtptimeout = global_rtptimeout; @@ -28218,6 +28229,17 @@ static struct sip_peer *build_peer(const 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, ®seconds, 0, NULL); } else if (realtime && !strcasecmp(v->name, "name")) { @@ -29326,7 +29348,7 @@ static int reload_config(enum channelrel 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); diff -rupN asterisk-1.8.32.2_orig//channels/sip/include/sip.h asterisk-1.8.32.2_sip_patch//channels/sip/include/sip.h --- asterisk-1.8.32.2_orig//channels/sip/include/sip.h 2014-05-10 00:18:59.000000000 +0200 +++ asterisk-1.8.32.2_sip_patch//channels/sip/include/sip.h 2015-03-08 16:23:39.655431000 +0100 @@ -1282,6 +1282,7 @@ struct sip_peer { 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 */ }; /*!