[Kamailio-Users] [sr-dev] Inbound NAT detection and fix_nated_register() very poorly documented

Alex Balashov abalashov at evaristesys.com
Thu Nov 26 14:45:25 CET 2009


I sent this message to K-users and sr-dev, and only saw it appear on 
sr-dev.  Was it delivered to K-users?  Also, I wrote a reply to myself 
following up and it appeared on neither list.

Alex Balashov wrote:

> Greetings,
> 
> I was faced with the following problem in Kamailio 1.5.2:  I was using 
> nathelper + rtpproxy + registrar and needed to relay media for calls 
> going inbound to a NAT'd registrant through rtpproxy - only if the 
> registrant is NAT'd.
> 
> Here is the scenario:
> 
>    Media gateway          Kamailio
>    (on public IP)  ---->  registrar      -----> NAT'd registrant
>                           (w/nathelper)
> 
> I was handling registrations from NAT'd endpoints like this:
> 
>     if(nat_uac_test("1")) {
>           force_rport();
>           fix_nated_contact();
>     }
> 
> I was aware of fix_nated_register() but was not clear on its 
> relationship to the 'received_param' parameter stored in 'location' and 
> set as a modparam to both the 'registrar' and 'nathelper' modules, so I 
> was not using it.
> 
> Very quickly I ran into an obvious problem:  if the contact stored in 
> the 'contact' column in 'location' is already fixed up with the received 
> IP:port, there is no way to know that the endpoint to which the call is 
> going is behind NAT -- when lookup() is called, the RURI is set to the 
> public IP:port.  There are no flags of any kind that one can set during 
> save() that persist while the contact binding is present for that AOR 
> and can be resurrected on lookup().
> 
> Anyway, I eventually figured out how to fix this problem by empirical 
> means, with no clear help from the nathelper documentation.  It turns 
> out that if I set the 'received_param' as a modparam to 'nathelper' and 
> 'registrar' and handle registrations from NAT'd endpoints with 
> fix_nated_register() instead, it will magically work.
> 
> I made the following discoveries to arrive at this conclusion, both of 
> which are not documented.  This is why it will work:
> 
> 1) When the original (RFC1918) 'contact' is stored in the 'location' 
> table (if using DB, which I am), the 'received' parameter is stored 
> alongside it and contains the public IP:port.
> 
> When lookup() is called, the RURI domain is set to private address, e.g.
> 
>    if(!lookup("location")) {
>          # Error handling here
>          exit;
>    }
> 
>    xlog("L_INFO", "[R-2:$ci] -> Registration resolved to RURI: $ru\n"):
> 
> $ru here will contain something like this: sip:s at 10.1.0.2:5060, the 
> original and unmodified contact supplied by the UAC.
> 
> But somehow, magically, when t_relay() is called the request will be 
> relayed to a different RURI - one with the 'received' IP:port 
> substituted in the domain portion.  So, the request goes end up going to 
> the correct place.
> 
> This is, of course, because of the way the 'received' parameter is 
> supposed to work when appended to a Contact URI.  But the point is that 
> it is not documented;  nowhere in the documentation for lookup() or 
> t_relay() does it say that this will transpire, and I have no way of 
> knowing it except by observation.
> 
> 2) For some reason, nat_uac_test("1") returns a positive result after 
> the lookup() and confirms that the destination is NAT'd.
> 
> This allows me to set a flag and then mangle the SDP in the reply to use 
> rtpproxy for NAT traversal of media as well, e.g.
> 
> route[2] {
> 
>     ...
> 
>     if(!lookup("location")) {
>           # Error handling here
>           exit;
>     }
> 
>     if(nat_uac_test("1"))
>           setflag(9);
> 
>     ...
> 
>     t_on_reply("1");
> 
>     if(!t_relay())
>           sl_reply_error();
> 
>     ...
> }
> 
> ...
> 
> onreply_route[1] {
>     if(t_check_status("(180|183|200)")) {
> 
>         if(nat_uac_test("5"))
>             fix_nated_contact();
> 
>         if(search("Content-Type: application/sdp")) {
>              if(isflagset(9)) {
>                  set_rtp_proxy_set("1");
>                  force_rtp_proxy();
>              }
>          }
> 
>      }
> }
> 
> But if you examine carefully the documentation for nat_uac_test(), it 
> says the following for bit flag 1:
> 
>      * 1- Contact header field is searched for occurrence of RFC1918 
> addresses.
> 
> To me this means that nat_uac_test() should not work after the lookup() 
> above.  The only Contact header value that is present in the inbound 
> INVITE handler is the Contact URI of the media gateway, which is a 
> public IP address!  Yet for some reason it works, as if by magic; 
> apparently it is somehow implicit that a hidden "received" attribute of 
> the RURI is also part of this check.
> 
> This is also not documented, and is completely counterintuitive.
> 
> Is there any possibility of clearing up documentation as to this point? 
>  Perhaps I have done something wrong here unknowingly;  I have no idea. 
>  I know that my solution works but I cannot justify why in terms of the 
> documentation.  Is it very much to ask that the documentation be 
> explicit about hidden but critically important mysteries like this?
> 
> Thanks!
> 
> -- Alex
> 


-- 
Alex Balashov - Principal
Evariste Systems
Web     : http://www.evaristesys.com/
Tel     : (+1) (678) 954-0670
Direct  : (+1) (678) 954-0671




More information about the sr-users mailing list