Hi all!
We've two SERs balanced via DNS-SRV, and Contacts are replicated via SIP between the Registrars. Now I've implemented transparent NAT support, but there's a "small" problem :o)
Let's say UAC-1 registers at SER-1, which replicates the contact to SER-2.
Outgoing calls from UAC-1 via SER-1 or SER-2 work great, and so do calls from PSTN via SER-1 to UAC-1.
But since there's no NAT-binding for UAC-1 to SER-2, incoming calls from SER-2 to UAC-1 can't work.
Does anybody know how to address this issue?
Thanks, Andy
Andreas Granig writes:
But since there's no NAT-binding for UAC-1 to SER-2, incoming calls from SER-2 to UAC-1 can't work.
what do you mean by "there is no nat binding"?
i have noticed that if you call fix_contact() and then t_replicate(), t_replicate doesn't replicate the fixed request, but the original one. perhaps someone can confirm this.
if true, t_replicate is useless if the client is behind nat and must be replaced by t_relay or some other function.
-- juha
Hi!
Juha Heinanen wrote:
Andreas Granig writes:
But since there's no NAT-binding for UAC-1 to SER-2, incoming calls from SER-2 to UAC-1 can't work.
what do you mean by "there is no nat binding"?
Sorry for the misunderstanding. What I meant is:
UAC-1 sends a SIP-Register to SER-1, so NAT is opened for UAC-1 <-> SER-1. But since UAC-1 doesn't send something to SER-2, NAT is never opened between UAC-1 and SER-2, which is what I meant with "no NAT-binding". Because of this SER-2 can't send a SIP-Request to UAC-1 because it's blocked by the NAT-device.
Andy
Andreas Granig writes:
UAC-1 sends a SIP-Register to SER-1, so NAT is opened for UAC-1 <-> SER-1. But since UAC-1 doesn't send something to SER-2, NAT is never opened between UAC-1 and SER-2, which is what I meant with "no NAT-binding". Because of this SER-2 can't send a SIP-Request to UAC-1 because it's blocked by the NAT-device.
yes, that indeed sounds like a problem. when contact of uac-1 is nated and memory only, ser-2 should use ser-1's address as source address for requests to uac-1. the other option might be to make ser-1 to send an options request to uac-1 using ser-2's address in contact.
perhaps somebody else has better suggestions?
-- juha
the other option might be to make ser-1 to send an options request to uac-1 using ser-2's address in contact.
should have been "in via header".
-- juha
Juha Heinanen wrote:
the other option might be to make ser-1 to send an options request to uac-1 using ser-2's address in contact.
should have been "in via header".
How would you do this best?
Since the source address and the source port have to match SER-1's address, IMHO you can't just use an external script to perform this directly without SER-1, because you can't bind to SER-1's address.
If you inject the OPTIONS into SER-1 via FIFO or sipsak/sipp, the request goes thru, but SER-1 adds it's address as topmost Via, so the response always comes back to SER-1 first. Is it possible to suppress the insertion of the own Via-Header?
Or even better: is there a way to send the OPTIONS from inside the SER-Config without calling or using external scripts? Don't think so (beside writing a module)...
Suggestions?
Andy
This is a hack, but:
You can redirect the REGISTER request to the 2nd server. SER-1 receives a REGISTER and processes it with save_noreply(). It then redirects the REGISTER method to the 2nd server using sl_send_reply("301", "Moved Permamently"); The 2nd server will detect that the REGISTER has been redirected and will generate 200 OK (a parameter in the request-uri could be used for the detection).
If you set the expires to a short value (using max_expires parameter of registrar module) than the user agent can keep NAT bindins open to both servers.
Note:
1) The user agent has to support REGISTER/3xx (not all of them do) 2) I did not test this. 3) It really is a hack.
Jan.
On 02-05-2005 13:07, Andreas Granig wrote:
Juha Heinanen wrote:
the other option might be to make ser-1 to send an options request to uac-1 using ser-2's address in contact.
should have been "in via header".
How would you do this best?
Since the source address and the source port have to match SER-1's address, IMHO you can't just use an external script to perform this directly without SER-1, because you can't bind to SER-1's address.
If you inject the OPTIONS into SER-1 via FIFO or sipsak/sipp, the request goes thru, but SER-1 adds it's address as topmost Via, so the response always comes back to SER-1 first. Is it possible to suppress the insertion of the own Via-Header?
Or even better: is there a way to send the OPTIONS from inside the SER-Config without calling or using external scripts? Don't think so (beside writing a module)...
Suggestions?
Andy
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Jan Janak wrote:
You can redirect the REGISTER request to the 2nd server. SER-1 receives a REGISTER and processes it with save_noreply(). It then redirects the REGISTER method to the 2nd server using sl_send_reply("301", "Moved Permamently"); The 2nd server will detect that the REGISTER has been redirected and will generate 200 OK (a parameter in the request-uri could be used for the detection).
Nice idea, but unfortunately it isn't supported by our ATAs (Mediatrix 2102) and linphone for example. Both don't send another register to the address given in the Contact-Header.
The only solution I currently see is Juha's idea with relaying an OPTIONS via the second SER, but it also doesn't work because of the Via-Header added by SER. Isn't there a way to tell SER to NOT insert a Via when relaying the OPTIONS request?
Andy
This is exactly the same problem discussed in another thread on load balancing and scalability. Klaus Darilion described a scanario for load balancing using ser. However, we run into the same NAT issues as you have discussed in this thread: http://lists.iptel.org/pipermail/serusers/2005-April/018240.html
One option is to make sure that NAT bindings are open. This will be a hack no matter what you do as long as the client does not support using SRV with equal weigths as *concurrent* servers, i.e. the client registers with both/all servers. I believe this is not the intention of the spec.
The other option is to always use the SER server where the client registered for INVITEs to that client. Klaus suggested Contact manipulation and implementation of the Path header to accomplish this. The problem is (surprise) that such implementation can also be characterized as hacks. In the above thread, I suggested using the following algorithm to simulate Path in a registration server (I don't know if it will work...): <Quote> record_route(); if(method=="REGISTER") { if(src_ip==server1) { save("location_server1"); # Does save reply to Contact or to srcip:port? } else if(src_ip==server2) { save("location_server2"); } break; } if(INVITE for one of our clients) { if(lookup("location_server1")){ t_relay_to_udp("outbound proxy1"); } else if(lookup("location_server")){ t_relay_to_udp("outbound proxy2"); } else { sl_reply("404", "User not found"); break; } }
Thus, faking Path by using src_ip and storing the location in different tables. I'm not sure how save() is implemented (where it replies). </Quote>
Of course, you will still have dependency on a single server for all users registered on that server. Thus, you must use a Linux HA type setup (or what I would prefer: Let each public IP delivered by _sip SRV by the virtual IP of an LVS-based cluster as discussed in the above thread).
Comments? g-)
Jan Janak wrote:
This is a hack, but:
You can redirect the REGISTER request to the 2nd server. SER-1 receives a REGISTER and processes it with save_noreply(). It then redirects the REGISTER method to the 2nd server using sl_send_reply("301", "Moved Permamently"); The 2nd server will detect that the REGISTER has been redirected and will generate 200 OK (a parameter in the request-uri could be used for the detection).
If you set the expires to a short value (using max_expires parameter of registrar module) than the user agent can keep NAT bindins open to both servers.
Note:
- The user agent has to support REGISTER/3xx (not all of them do)
- I did not test this.
- It really is a hack.
Jan.
On 02-05-2005 13:07, Andreas Granig wrote:
Juha Heinanen wrote:
the other option might be to make ser-1 to send an options request to uac-1 using ser-2's address in contact.
should have been "in via header".
How would you do this best?
Since the source address and the source port have to match SER-1's address, IMHO you can't just use an external script to perform this directly without SER-1, because you can't bind to SER-1's address.
If you inject the OPTIONS into SER-1 via FIFO or sipsak/sipp, the request goes thru, but SER-1 adds it's address as topmost Via, so the response always comes back to SER-1 first. Is it possible to suppress the insertion of the own Via-Header?
Or even better: is there a way to send the OPTIONS from inside the SER-Config without calling or using external scripts? Don't think so (beside writing a module)...
Suggestions?
Andy
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Greger V. Teigre wrote:
The other option is to always use the SER server where the client registered for INVITEs to that client. Klaus suggested Contact manipulation and implementation of the Path header to accomplish this.
I've already thought about rewriting the Contact-Header with the address of the currently processing SER before replicating it, to force all calls via the same SER. But this solution will work around the redundancy won by using SRV, so it would be useless to use SRV anyway.
And I also want to avoid load balancers, heartbeat and other standby solutions.
Andy
Andreas Granig wrote:
Greger V. Teigre wrote:
The other option is to always use the SER server where the client registered for INVITEs to that client. Klaus suggested Contact manipulation and implementation of the Path header to accomplish this.
I've already thought about rewriting the Contact-Header with the address of the currently processing SER before replicating it, to force all calls via the same SER. But this solution will work around the redundancy won by using SRV, so it would be useless to use SRV anyway.
Yes, I agree. IP-based services are often loosely coupled, making it easier to create redundancy and scalability. The NAT-problem makes a tight coupling...
And I also want to avoid load balancers, heartbeat and other standby solutions.
:-) well, I don't like standby solutions either, but I like some of the characteristics of load balancing. If you have two data centers (using SRV to load balance/make redundancy on the client side), and each data center is a small LVS cluster, it is very easy to add servers to increase capacity. Anyway, it's just two different ways of achieving the same thing. However, SRV is not supported in all clients, so no matter where you turn there's a downside... I like the idea of using OPTIONS. If you find a way to solve that, it may very well become a best practice. However, you need to make sure the NAT binding is open before you send the INVITE from SER-2, so you either have to continously send OPTIONS to all your clients or you have to make sure that SER-1 sends the OPTIONS right before you send the INVITE from SER-2, which brings you full circle: you're reliant on SER-1... Maybe an extension to nathelper would be the way to go? Maxim added support for sending different types of SIP messages instead of an empty UDP. An extension to nathelper could take a flat file with IPs of your other servers and send a series of OPTIONS messages instead of one. I have no idea how this will impact the load on the server though.
g-)
Greger V. Teigre wrote:
And I also want to avoid load balancers, heartbeat and other standby solutions.
:-) well, I don't like standby solutions either, but I like some of the characteristics of load balancing. If you have two data centers (using SRV to load balance/make redundancy on the client side), and each data center is a small LVS cluster, it is very easy to add servers to increase capacity.
I haven't found a good documentation for building a SIP-Cluster using LVS, but I'm looking forward to find one on onsip.org soon ;o) Unfortunately I don't have the time to play around a little with LVS for myself.
I like the idea of using OPTIONS. If you find a way to solve that, it may very well become a best practice. However, you need to make sure the NAT binding is open before you send the INVITE from SER-2, so you either have to continously send OPTIONS to all your clients or you have to make sure that SER-1 sends the OPTIONS right before you send the INVITE from SER-2
Well, the basic idea is to send an OPTION like the following sipp-based template directly after receiving and replicating a REGISTER from SER-1 to UAC-1, which will send the reply back via SER-2 (assuming I get rid of the Via-Header of SER-1):
<send> <![CDATA[ OPTIONS sip:[service]@[remote_ip] SIP/2.0 Via: SIP/2.0/[transport] [field0]:[field1] Via: SIP/2.0/[transport] [local_ip]:[local_port] To: <sip:[service]@[remote_ip]:[remote_port]> From: <sip:foo@[remote_ip]:[remote_port]> Contact: <sip:foo@[local_ip]:[local_port]>;transport=[transport] Call-ID: [call_id] CSeq: 1 OPTIONS Content-Length: 0 ]]> </send>
where field0 is the IP of SER-2, field1 the port of SER-2.
The reply from UAC-1 to SER-2 will open the NAT, and SER-2 will be able to continuously send UDP pings for keeping the NAT opened. So SER-1 can die now without effecting UAC-1's reachability.
That's for the theory so far :o)
Of course there are some other issues like registrations while one of the SERs is down, so no NAT-bindings can be established, or the NAT-bindings will time out while one SER is down, and the UACs are unreachable for the recovered SER.
Maybe it's really time to evaluate iptelorg's SOP/SRR solution ;o)
Andy
Greger V. Teigre writes:
I like the idea of using OPTIONS. If you find a way to solve that, it
may very well become a best practice. However, you need to make sure the NAT binding is open before you send the INVITE from SER-2, so you either have to continously send OPTIONS to all your clients or you have to make sure that SER-1 sends the OPTIONS right before you send the INVITE from SER-2, which brings you full circle: you're reliant on SER-1...
assuming that somehow nat binding has been opened from the sip ua to ser-2, ser-2 will automatically keep that binding open by sending the small udp packets like it does for all clients behind nat. so you don't need to keep on sending options or relying on ser-1 once ser-2 has got access to the sip ua.
but may the options hack will not work at all even if we could hack the via header, because options reply from the sip ua to ser-2 would not necessarily come from the same source port as the register request to ser-1. my understanding is that in case of symmetric nats, each src ip/dst ip pair gets mapped by the nat to different source ports.
so ser-2 should somehow know to listed for the options reply even if it didn't send the options request and then change the destination port in its memory based location table for that sip ua. this hack would be mother of all hacks.
Maybe an extension to nathelper would be the way to go? Maxim added support for sending different types of SIP messages instead of an empty UDP. An extension to nathelper could take a flat file with IPs of your other servers and send a series of OPTIONS messages instead of one. I have no idea how this will impact the load on the server though.
sending the options request could be done one way or another, but see above.
-- juha
Greger V. Teigre writes:
The other option is to always use the SER server where the client registered for INVITEs to that client.
that would be ok for load balancing but not for redundancy, i.e., if client's ser dies, the other cannot be used as backup.
Thus, faking Path by using src_ip and storing the location in different tables.
if you like to do that with two proxies, i don't think you would need two tables, since you could check if FL_MEM flag is set for the contact and if so, forward the request to the other proxy.
Of course, you will still have dependency on a single server for all users registered on that server.
exactly.
Thus, you must use a Linux HA type setup (or what I would prefer: Let each public IP delivered by _sip SRV by the virtual IP of an LVS-based cluster as discussed in the above thread).
i need to check your thread in case you have a LVS based solution that works.
-- juha
Jan Janak writes:
You can redirect the REGISTER request to the 2nd server. SER-1 receives a REGISTER and processes it with save_noreply(). It then redirects the REGISTER method to the 2nd server using sl_send_reply("301", "Moved Permamently"); The 2nd server will detect that the REGISTER has been redirected and will generate 200 OK (a parameter in the request-uri could be used for the detection).
in addition that clients have to support 301 for register request, they would also need to retain unknown request uri params, which many don't do.
If you set the expires to a short value (using max_expires parameter of registrar module) than the user agent can keep NAT bindins open to both servers.
nat bindings could be kept open by the small udp packets that are automatically sent to clients behind nats by nathelper/mediaproxy modules.
-- juha
Jan Janak writes:
You can redirect the REGISTER request to the 2nd server. SER-1 receives a REGISTER and processes it with save_noreply(). It then redirects the REGISTER method to the 2nd server using sl_send_reply("301", "Moved Permamently"); The 2nd server will detect that the REGISTER has been redirected and will generate 200 OK (a parameter in the request-uri could be used for the detection).
the detection part could possibly be done without any r-uri params as follows: before updating contact in db location table and generating 301, ser checks from db if contact has been updated "recently" and if so it would only do memory update and would not generate 301.
-- juha
Juha Heinanen wrote:
the other option might be to make ser-1 to send an options request to uac-1 using ser-2's address in contact.
should have been "in via header".
Nice idea, never thought of this before. But I see also some problems:
1. If the client is configured to use symmetric signaling, it will send the response back to ser-1.
2. If the client sends the response back to ser-2, the port on the NAT may be different than the port for communicating with ser-1. Thus, the replicated location (port) stored in ser-2 will be different to the NAT binding for ser-2.
regards, klaus
Klaus Darilion wrote:
Juha Heinanen wrote:
should have been "in via header".
Nice idea, never thought of this before. But I see also some problems:
I was also surprised about the solutions like redirecting registers or routing options via ser-2, and they look quite promising at first glance. But unfortunately the former isn't supported by our (and a lot of others, I fear) UACs and the latter will result in quite a big hack, apart from the problems when one ser goes down and can't keep the nat binding opened. And both won't scale very well IMHO.
So maybe I'll have a look at an LVS solution after all to present only one public IP to the UACs.
Andy
Klaus Darilion writes:
- If the client sends the response back to ser-2, the port on the NAT
may be different than the port for communicating with ser-1. Thus, the replicated location (port) stored in ser-2 will be different to the NAT binding for ser-2.
yes, i figured this out too yesterday.
even if we would do what jan suggested, i.e., use 301 to make sip ua send another register request to ser-2, location table entry for this sip ua would be different in ser-1 and ser-2, because nat would not use the same port for ser-1 and ser-2.
this means that if one ser goes down and comes back up again, it cannot get its location memory populated from the other ser's location db. if it uses its own location db, chances are high that it is not up to data anymore and thus not all sip uas are anymore reachable via this ser.
in summary: a load balancing solution that at the same time is redundant is not possible using the participating sers alone. some intelligent front end help is needed and i'm not convinced yet that even that could be done.
-- juha
Juha Heinanen wrote:
in summary: a load balancing solution that at the same time is redundant is not possible using the participating sers alone. some intelligent front end help is needed and i'm not convinced yet that even that could be done.
What about this (maybe a special case):
I was just thinking about STUN, and AFAIK this relies on a NAT device not matching the source IP of a datagram, so the client opens a NAT binding to an STUN server to determine an external IP/port pair which is filled in the Contact header. The Proxy then can also send SIP requests to this external IP/port which is forwarded to the client by the NAT device. Is this basically correct?
If so, it theoretically should work with transparent NAT handling on two SERs too, if both SERs know the external IP/port of UAC-1.
I also think I know now why it didn't work for me: SER-1 got the request from UAC-1 and stored the contact IP and the received IP in the location table. SER-2 got the replicated register but stored SER-1's IP as received IP:
On SER-1 (2.2.2.2 is the external IP of UAC-1, 3.3.3.3 is the IP of SER-1):
mysql root@ser> select * from location where username='018904449'\G *************************** 1. row *************************** username: 018904449 domain: contact: sip:018904449@192.168.34.187:50600 received: sip:2.2.2.2:50600 expires: 2005-05-03 10:19:17 q: -1.00 callid: 1338383826@192.168.34.187 cseq: 2 last_modified: 20050503101557 replicate: NULL state: NULL flags: 1 user_agent: Linphone-1.0.1/eXosip 1 row in set (0.00 sec)
And on SER-2: mysql root@ser> select * from location where username='018904449'\G *************************** 1. row *************************** username: 018904449 domain: contact: sip:018904449@192.168.34.187:50600 received: sip:3.3.3.3:46236;transport=TCP expires: 2005-05-03 10:19:15 q: -1.00 callid: 1338383826@192.168.34.187 cseq: 2 last_modified: 20050503101555 replicate: NULL state: NULL flags: 1 user_agent: Linphone-1.0.1/eXosip 1 row in set (0.00 sec)
My config basically looks like this (NAT-flag is "2"):
if (method=="REGISTER") { if(is not from Peer-SER-IP) { if(!search("^Contact: *") && client_nat_test("7")) { setflag(2); fix_nated_register(); force_rport(); append_hf("Natted: yes\r\n"); }
if(!www_authorize("<auth domain>", "subscriber")) { www_challenge("<auth domain>", "0"); break; } if(!check_to()) { sl_send_reply("403", "Use To=id next time"); break; }
consume_credentials();
if(!save("location")) { sl_reply_error(); break; }
forward_tcp("<Peer-SER-IP>", 5060); } else { if(is_present_hf("Natted")) { setflag(2); fix_nated_register(); force_rport(); } save_noreply("location"); } break; }
I'll investigate the problem and report back here. Or do I miss something important?
Andy
Andreas Granig writes:
If so, it theoretically should work with transparent NAT handling on two SERs too, if both SERs know the external IP/port of UAC-1.
yes, but symmetric nat would not forward packets to uac from the ip address of the other ser even if it sends them to the external ip/port of the uac.
-- juha
Juha Heinanen wrote:
If so, it theoretically should work with transparent NAT handling on two SERs too, if both SERs know the external IP/port of UAC-1.
yes, but symmetric nat would not forward packets to uac from the ip address of the other ser even if it sends them to the external ip/port of the uac.
Yes, and also (port-)restricted cone NAT won't work...
UPnP isn't widespread enough and cascading NAT won't work too (just want to be shure to take into account all possibilities ;o) )
Ok, so the conclusion is to use a clustered solution with one public IP and some kind of heartbeat.
Well, thanks for the insights anyway.
Andy
It boils down to the following: For the 'OPTIONS with manipulated via' idea to work, we need to make sure that: a) SER-1 and SER-2 keeps separate location for UACs because symmetric NATs will give you IP:port1 and IP:port2 as NAT ports b) The only way to establish a NAT binding in a symmetric NAT is for the UAC to send a UDP packet to both SER-1 and SER-2, and each SER needs to store this info c) NAT bindings need to be kept open, but each SER can do this for its "own" UACs using nathelper or mediaproxy's ping functionality
- So, if UAC-1 registers with SER-1, SER-1 should send an OPTIONS message to UAC-1 with SER-2 in Via (as noted earlier, if UAC-1 responds to received IP:port, this will not work, but let's assume we can control this). - SER-2 should receive a replication from SER-1 and store the user and location - SER-2 should recognize the OPTIONS message as a location update to the registration and change UAC-1's location in its own database with the received IP:port from UAC-1's message. This should be encapsulated like this: if(method=="OK" && search("detect that this is a location update")) { if(!registered("location")) { reply("Fake!!"); } else { save_noreply("location"); } }
- Of course, the registration on SER-1 must include sending the faked via OPTIONS message to UAC-1. - And, as Juha pointed out, the databases are now out of sync and a server that goes down cannot get the database from another. But unless it is possible to match interface IP with the location for each UAC (so that multiple interface IP/location pairs can be stored), I cannot see a way to get around this.
Anything I have forgotten?
I have been in dialog with the maintainer of IPVS (LVS load scheduler) with regards to a call-id scheduler (similar to F5/Cisco). So far it seems promising, but somebody must implement a UDP scheduler with payload parsing. g-)
Juha Heinanen wrote:
Andreas Granig writes:
If so, it theoretically should work with transparent NAT handling on two SERs too, if both SERs know the external IP/port of UAC-1.
yes, but symmetric nat would not forward packets to uac from the ip address of the other ser even if it sends them to the external ip/port of the uac.
-- juha
Serusers mailing list serusers@lists.iptel.org http://lists.iptel.org/mailman/listinfo/serusers
Andreas Granig wrote:
Juha Heinanen wrote:
in summary: a load balancing solution that at the same time is redundant is not possible using the participating sers alone. some intelligent front end help is needed and i'm not convinced yet that even that could be done.
What about this (maybe a special case):
I was just thinking about STUN, and AFAIK this relies on a NAT device not matching the source IP of a datagram, so the client opens a NAT binding to an STUN server to determine an external IP/port pair which is filled in the Contact header. The Proxy then can also send SIP requests to this external IP/port which is forwarded to the client by the NAT device. Is this basically correct?
If so, it theoretically should work with transparent NAT handling on two SERs too, if both SERs know the external IP/port of UAC-1.
That's fine. There is no problem with STUN-traverseable NATs. But there are problems with symmetric NAT and restricted NAT.
I also think I know now why it didn't work for me: SER-1 got the request from UAC-1 and stored the contact IP and the received IP in the location table. SER-2 got the replicated register but stored SER-1's IP as received IP:
True, ser-2 does not use fix_... for REGISTERs received from ser-1.
regards, klaus
andreas,
i tested again and calling t_replicate after calling fix_contact did sent out the fixed register request.
what was the problem you had?
-- juha
Juha Heinanen wrote:
andreas,
i tested again and calling t_replicate after calling fix_contact did sent out the fixed register request.
what was the problem you had?
AFAIK fix_contact makes problems with clients which likes to see the Contact header unmodified. Thus, fix_nated_register() is more suitable. But this does not change the replacted message. Thus, is this logic possible?
if (REGISTEER && NATed) { fix_nated_register save fix_contact t_replicate }
regards, klaus