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@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 wrote:
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.
I just tested with an endpoint on a public IP and found that this test was still returning a positive result, which further adds to the mystery as there is no RFC1918 address in the Contact URI on either side (request or reply) now. Please advise.
The documentation appears to be very much out of touch with fact here.
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:
- 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@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.
- 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
Hello,
I got your emails and the archive on the web shows them: http://lists.kamailio.org/pipermail/users/2009-November/date.html
But was a bit too long for me to read and reply at the time I got it ... will do it soon.
Cheers, Daniel
On 26.11.2009 14:45 Uhr, Alex Balashov wrote:
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:
- 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@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.
- 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
Daniel-Constantin Mierla wrote:
But was a bit too long for me to read and reply at the time I got it ... will do it soon.
My fundamental question can be distilled to something very simple:
If I have a REGISTER from a NAT'd endpoint, what can I do (in terms of existing module functionality, e.g. fix_nated_register(), save()) to "store" the fact that the endpoint is NAT'd in such a way that this can be detected again on a lookup() for an inbound call and special measures can be taken?
I suppose there are many things that can be done - special AVPs stored in the DB, custom SQL queries, manually checking if the RURI domain is an RFC1918 address string etc., but I want to know what the canonical way to do this in terms of the existing modules is. After all, it is a common need to know that your call is going to a NAT'd endpoint prior to receiving a reply for that request.
My secondary concern is that the way lookup() works when received_param is used is not documented at all.
-- Alex
On 27.11.2009 15:28 Uhr, Alex Balashov wrote:
Daniel-Constantin Mierla wrote:
But was a bit too long for me to read and reply at the time I got it ... will do it soon.
My fundamental question can be distilled to something very simple:
If I have a REGISTER from a NAT'd endpoint, what can I do (in terms of existing module functionality, e.g. fix_nated_register(), save()) to "store" the fact that the endpoint is NAT'd in such a way that this can be detected again on a lookup() for an inbound call and special measures can be taken?
I suppose there are many things that can be done - special AVPs stored in the DB, custom SQL queries, manually checking if the RURI domain is an RFC1918 address string etc., but I want to know what the canonical way to do this in terms of the existing modules is. After all, it is a common need to know that your call is going to a NAT'd endpoint prior to receiving a reply for that request.
set a branch flag for register. After lookup() it is restored so you can test it. See default config file, it uses this mechanism.
My secondary concern is that the way lookup() works when received_param is used is not documented at all.
This is the name of the parameter appended to contact uri, you will see it in location and in the reply. If you want to use it, then you have to do it in the config, I haven't used it so far.
Cheers, Daniel
Daniel-Constantin Mierla wrote:
set a branch flag for register. After lookup() it is restored so you can test it. See default config file, it uses this mechanism.
Oh, hey. I did not notice this before:
http://www.kamailio.org/dokuwiki/doku.php/utils:flags
"branch flags (NEW) are saved also in transaction, but per branch; also they will be saved in usrloc (per contact)."
That was the ticket I was looking for. Thanks!