From fcbb9f0c8dd43dde059abf2deb2ed09b2c4e4539 Mon Sep 17 00:00:00 2001 From: "Michael L. Young" Date: Fri, 12 Apr 2013 15:06:09 +0000 Subject: Fix One-Way Audio With auto_* NAT Settings When SIP Calls Initiated By PBX When we reload Asterisk or chan_sip, the flags force_rport and comedia that are turned on and off when using the auto_force_rport and auto_comedia nat settings go back to the default setting off. These flags are turned on when needed or off when not needed at the time that a peer registers, re-registers or initiates a call. This would apply even when only the default global setting "nat=auto_force_rport" is being used, which in this case would only affect the force_rport flag. Everything is good except for the following: The nat setting is set to auto_force_rport and auto_comedia. We reload Asterisk and the peer's registration has not expired. We load in the settings for the peer which turns force_rport and comedia back to off. Since the peer has not re-registered or placed a call yet, those flags remain off. We then initiate a call to the peer from the PBX. The force_rport and comedia flags stay off. If NAT is involved, we end up with one-way audio since we never checked to see if the peer is behind NAT or not. This patch does the following: * Moves the checking of whether a peer is behind NAT into its own function * Create a function to set the peer's NAT flags if they are using the auto_* NAT settings * Adds calls in sip_request_call() to these new functions in order to setup the dialog according to the peer's settings (closes issue ASTERISK-21374) Reported by: Michael L. Young Tested by: Michael L. Young Patches: asterisk-21374-auto-nat-outgoing-fix_v2.diff Michael L. Young (license 5026) Review: https://reviewboard.asterisk.org/r/2421/ ........ Merged revisions 385473 from http://svn.asterisk.org/svn/asterisk/branches/11 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@385474 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_sip.c | 128 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 42 deletions(-) (limited to 'channels') diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 13269b067..d7d75e7c5 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1273,6 +1273,8 @@ static int sip_notify_alloc(struct sip_pvt *p); static void ast_quiet_chan(struct ast_channel *chan); static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context); +static void set_peer_nat(const struct sip_pvt *p, struct sip_peer *peer); +static void check_for_nat(const struct ast_sockaddr *them, struct sip_pvt *p); /*--- Device monitoring and Device/extension state/event handling */ static int extensionstate_update(const char *context, const char *exten, struct state_notify_data *data, struct sip_pvt *p, int force); @@ -17107,22 +17109,12 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); res = AUTH_PEER_NOT_DYNAMIC; } else { - if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { - if (p->natdetected) { - ast_set_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT); - } else { - ast_clear_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT); - } - } - if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) { - if (p->natdetected) { - ast_set_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP); - } else { - ast_clear_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP); - } + + set_peer_nat(p, peer); + if (p->natdetected && ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { + ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT_FORCE_RPORT); } - ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_NAT_FORCE_RPORT); if (!(res = check_auth(p, req, peer->name, peer->secret, peer->md5secret, SIP_REGISTER, uri2, XMIT_UNRELIABLE))) { if (sip_cancel_destroy(p)) ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n"); @@ -18151,6 +18143,67 @@ static int get_also_info(struct sip_pvt *p, struct sip_request *oreq) return -1; } +/*! \brief Set the peers nat flags if they are using auto_* settings */ +static void set_peer_nat(const struct sip_pvt *p, struct sip_peer *peer) +{ + + if (!p || !peer) { + return; + } + + if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { + if (p->natdetected) { + ast_set_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT); + } else { + ast_clear_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT); + } + } + + if (ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) { + if (p->natdetected) { + ast_set_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP); + } else { + ast_clear_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP); + } + } +} + +/*! \brief Check and see if the requesting UA is likely to be behind a NAT. + * + * If the requesting NAT is behind NAT, set the * natdetected flag so that + * later, peers with nat=auto_* can use the value. Also, set the flags so + * that Asterisk responds identically whether or not a peer exists so as + * not to leak peer name information. + */ +static void check_for_nat(const struct ast_sockaddr *addr, struct sip_pvt *p) +{ + + if (!addr || !p) { + return; + } + + if (ast_sockaddr_cmp(addr, &p->recv)) { + char *tmp_str = ast_strdupa(ast_sockaddr_stringify(addr)); + ast_debug(3, "NAT detected for %s / %s\n", tmp_str, ast_sockaddr_stringify(&p->recv)); + p->natdetected = 1; + if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { + ast_set_flag(&p->flags[0], SIP_NAT_FORCE_RPORT); + } + if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) { + ast_set_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP); + } + } else { + p->natdetected = 0; + if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { + ast_clear_flag(&p->flags[0], SIP_NAT_FORCE_RPORT); + } + if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) { + ast_clear_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP); + } + } + +} + /*! \brief check Via: header for hostname, port and rport request/answer */ static void check_via(struct sip_pvt *p, const struct sip_request *req) { @@ -18214,29 +18267,7 @@ static void check_via(struct sip_pvt *p, const struct sip_request *req) ast_sockaddr_set_port(&p->sa, port); - /* Check and see if the requesting UA is likely to be behind a NAT. If they are, set the - * natdetected flag so that later, peers with nat=auto_* can use the value. Also - * set the flags so that Asterisk responds identically whether or not a peer exists - * so as not to leak peer name information. */ - if (ast_sockaddr_cmp(&tmp, &p->recv)) { - char *tmp_str = ast_strdupa(ast_sockaddr_stringify(&tmp)); - ast_debug(3, "NAT detected for %s / %s\n", tmp_str, ast_sockaddr_stringify(&p->recv)); - p->natdetected = 1; - if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { - ast_set_flag(&p->flags[0], SIP_NAT_FORCE_RPORT); - } - if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) { - ast_set_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP); - } - } else { - p->natdetected = 0; - if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { - ast_clear_flag(&p->flags[0], SIP_NAT_FORCE_RPORT); - } - if (ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) { - ast_clear_flag(&p->flags[1], SIP_PAGE2_SYMMETRICRTP); - } - } + check_for_nat(&tmp, p); if (sip_debug_test_pvt(p)) { ast_verbose("Sending to %s (%s)\n", @@ -18304,15 +18335,12 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, * are set on the peer. So we check for that here and set the peer's * address accordingly. */ + set_peer_nat(p, peer); + if (p->natdetected && ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { - ast_set_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT); ast_sockaddr_copy(&peer->addr, &p->recv); } - if (p->natdetected && ast_test_flag(&peer->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) { - ast_set_flag(&peer->flags[1], SIP_PAGE2_SYMMETRICRTP); - } - if (!ast_apply_acl(peer->acl, addr, "SIP Peer ACL: ")) { ast_debug(2, "Found peer '%s' for '%s', but fails host access\n", peer->name, of); sip_unref_peer(peer, "sip_unref_peer: check_peer_ok: from sip_find_peer call, early return of AUTH_ACL_FAILED"); @@ -30011,6 +30039,22 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_ ast_string_field_set(p, peername, ext); /* Recalculate our side, and recalculate Call ID */ ast_sip_ouraddrfor(&p->sa, &p->ourip, p); + /* When chan_sip is first loaded, we may have a peer entry but it hasn't re-registered yet. + If the peer hasn't re-registered, we have not checked for NAT yet. With the new + auto_* settings, we need to check for NAT so we do not have one-way audio. */ + check_for_nat(&p->ourip, p); + set_peer_nat(p, p->relatedpeer); + + if (p->natdetected && ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) { + ast_copy_flags(&p->flags[0], &p->relatedpeer->flags[0], SIP_NAT_FORCE_RPORT); + } + + if (p->natdetected && ast_test_flag(&p->flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) { + ast_copy_flags(&p->flags[1], &p->relatedpeer->flags[1], SIP_PAGE2_SYMMETRICRTP); + } + + do_setnat(p); + build_via(p); /* Change the dialog callid. */ -- cgit v1.2.3