[Home]

Summary:ASTERISK-11965: Asterisk MUST add VIA "received" parameter always and send replies there even if "nat=no"
Reporter:Iñaki Baz Castillo (ibc)Labels:
Date Opened:2008-05-03 06:26:32Date Closed:2008-07-22 18:06:22
Priority:MajorRegression?No
Status:Closed/CompleteComponents:Channels/chan_sip/Interoperability
Versions:Frequency of
Occurrence
Related
Issues:
Environment:Attachments:( 0) chan_sip.c.patch
Description:Hi, Asterisk SIP doesn't respect RFC 3261 section "18.2.1 Receiving Requests":

18.2.1 Receiving Requests

  When the server transport receives a request over any transport, it
  MUST examine the value of the "sent-by" parameter in the top Via
  header field value.  If the host portion of the "sent-by" parameter
  contains a domain name, or if it contains an IP address that differs
  from the packet source address, the server MUST add a "received"
  parameter to that Via header field value.  This parameter MUST
  contain the source address from which the packet was received.


But if Asterisk listening in a public IP receives a message like this:

 # U 211.55.130.197:60262 -> ASTERISK_PUBLIC_IP:5060
 REGISTER sip:asterisk.domain.com SIP/2.0
 Via: SIP/2.0/UDP 192.168.0.129;rport;branch=z9hG4bKnfztfitc

Then Asterisk will reply to 211.55.130.197 **just** in case that peer is defined as "nat=yes". If not Asterisk will reply to the private IP 192.168.0.129.

This is completely wrong since replying to the public source IP is mandatory as it's explained above.

I understand that the "nat=yes" can be useful to enable comedia mode for a peer so it doesn't matter if it behind NAT and indicates a private IP in the SDP. Also it's valid for sending in-dialog messages to the source public IP instead of the uri indicated in "Contact" header than would be private so unreachable by Asterisk. And also it can ve useful to assume "rport" and sending the transaction reply to the port of the public source IP instead of the port indicated in "sent-by" field.

But for sending a transaction response to the received public IP it MUSN'T be neccesary at all to set "nat=yes", it's completely RFC 3261 non-compliant.

****** ADDITIONAL INFORMATION ******

RFC 3261 - Section "18.2.2 Sending Responses" tells that a UAS must examine (in case of UDP) the Via header and send the reply to "received" IP (that UAS added when received the request):

18.2.2 Sending Responses


  The server transport uses the value of the top Via header field in
  order to determine where to send a response.  It MUST follow the
  following process:

     o  If the "sent-protocol" is a reliable transport protocol such as
        TCP or SCTP, or TLS over those, the response MUST be sent using
        the existing connection to the source of the original request
        that created the transaction, if that connection is still open.
        This requires the server transport to maintain an association
        between server transactions and transport connections.  If that
        connection is no longer open, the server SHOULD open a
        connection to the IP address in the "received" parameter, if
        present, using the port in the "sent-by" value, or the default
        port for that transport, if no port is specified.  If that
        connection attempt fails, the server SHOULD use the procedures
        in [4] for servers in order to determine the IP address and
        port to open the connection and send the response to.

     o  Otherwise, if the Via header field value contains a "maddr"
        parameter, the response MUST be forwarded to the address listed
        there, using the port indicated in "sent-by", or port 5060 if
        none is present.  If the address is a multicast address, the
        response SHOULD be sent using the TTL indicated in the "ttl"
        parameter, or with a TTL of 1 if that parameter is not present.

     o  Otherwise (for unreliable unicast transports), if the top Via
        has a "received" parameter, the response MUST be sent to the
        address in the "received" parameter, using the port indicated
        in the "sent-by" value, or using port 5060 if none is specified
        explicitly.  If this fails, for example, elicits an ICMP "port
        unreachable" response, the procedures of Section 5 of [4]
        SHOULD be used to determine where to send the response.


NOTE: Also RFC 3581 (rport) is neccesary to handle NAT scenarios so the reply would go to the public source IP indicated in "received" parameter (added by UAS itself) and public source port (if "rport" parameter exists in the Via request).
About this I don't know if Asterisk should always assume "rport" even if it's in request Via header (as OpenSer can do with "force_rport" function), or if Asterisk should reply to public port only in case of "nat=yes" for that peer.
But anyway replying to public source IP ("received" parameter) MUST be always done.
Comments:By: Iñaki Baz Castillo (ibc) 2008-05-03 06:35:51

Sorry, in the last phrase I mean:

About this I don't know if Asterisk should always assume "rport" even if there is NOT "rport" in request Via header (as OpenSer can do with "force_rport" function), or if Asterisk should reply to public port only in case of "nat=yes" for that peer.


The standard behaviour is:

* If a client behind NAT adds "rport" parameter in Via header then the UAS transport layer should add in received request Via header:
- "received=PUBLIC_SOOURCE_IP"
- "rport=PUBLIC_SOURCE_PORT"
And the reply must go there.

* If a client behind NAT sends the request without adding "rport" in Via header then the UAS transport layer should add in Via:
- "received=PUBLIC_SOOURCE_IP"
In this second case replies from UAS would go to PUBLIC_SOURCE_IP:Port_indicated_in_original_sent-by_or_5060_by_default so it would be lost in the NAT device, this is why RFC 3581 exists but a client MUST add "rport" parameter first case to work. If not, mayeb the UAS forces it (OpenSer "force_rport" or Asterisk "nat=yes").

By: Iñaki Baz Castillo (ibc) 2008-05-03 08:01:49

It's curious: I see in the chan_sip code that the behaviour should be the correct:

---------------------------------------
/* We should *always* add a received to the topmost via */
  snprintf(new, sizeof(new), "%s;received=%s%s%s",
     leftmost, ast_inet_ntoa(p->recv.sin_addr),
     others ? "," : "", others ? others : "");
---------------------------------------

But it seems not to work, does it?

By: Iñaki Baz Castillo (ibc) 2008-05-03 08:18:51

It seems that Asterisk ALWAYS adds "received" parameter to Via header but later only uses it to send the reply if "nat=yes". Maybe the problem is here?


/*! \brief The real destination address for a write */
static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
{
       return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? &p->recv : &p->sa;
}

By: Iñaki Baz Castillo (ibc) 2008-05-03 09:08:40

Yeah, it's seems working just by modifying *sip_real_dst:


SVN rev 115275:
---------------------------------------------------------
static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
{
       if (p->outboundproxy)
               return &p->outboundproxy->ip;

       return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? &p->recv : &p->sa;
}
---------------------------------------------------------


Modified:
---------------------------------------------------------
static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
{
       if (p->outboundproxy)
               return &p->outboundproxy->ip;

       // It MUST always use "received" to reply,
       // even if NAT or not (ASTERISK-11965):
       return &p->recv;
}
---------------------------------------------------------



By: Olle Johansson (oej) 2008-07-01 07:40:51

We have another bug report that says that we should always respond to the sender's address.

By: Iñaki Baz Castillo (ibc) 2008-07-01 08:18:42

Yes, true, I didn't realize of it.
Also, it seems that the patch in that report is better than mine.

By: Kevin P. Fleming (kpfleming) 2008-07-22 18:06:19

This is clearly a duplicate of issue 8855; let's keep all the discussion there, instead of having it in three places.