Solving the problem of broken Re-INVITEs of PortaSIP server
Emin Gabrielyan
2007-05-23
Solving
the problem of broken Re-INVITEs of PortaSIP server
1......... Description
of the architecture
3......... Description
of the problem
4......... Configuration
files before the solution was found
7......... An
old fixed issue: The Record-Route header bug in PortaSIP
We have an OpenSER server running on two interfaces: 212.249.15.3 and 195.129.125.73.
Our OpenSER server communicates with Verizon via interface 195.129.125.73 for sending outgoing calls and for receiving our range of 20’000 numbers (021550abcd and 022550abcd) from PSTN.
The OpenSER communicates with PortaSIP via the interface 212.249.15.3. The IP address of the PortaSIP server is 128.179.67.35.
Separation of the interfaces is dictated by the policy of IPSec connection with Verizon.
When we receive an incoming call (from Verizon) and connect it (through PortaSIP), the call gets disconnected after about 10 minutes (between 5 to 15 minutes). The call is disconnected with a BYE message sent by PortaSIP.
PortaSIP sends Re-INVITEs every 30 seconds. It disconnects the call if it does not receive 200 OK replies to its Re-INVITE messages. The problem is that once 200 OK is received PortaSIP forgets to send the ACK [ref].
The Re-INVITE messages of PortaSIP are routed to Verizon. Verizon sends the corresponding 200 OK replies. Since there is no ACK from PortaSIP, Verizon retransmits the 200 OK messages several times. After a certain number of Re-INVITEs with plenty of 200 OK replies, but no ACKs, Verizon detects a wrong behaviour and enters into an erroneous state. The Re-INVITEs of the PortaSIP are not replied with 200 OK messages anymore. As a consequence, PortaSIP closes the connection by sending a BYE message.
These files are running on 212.249.15.4:
Configuration files by Christian Lathion:
- The heavy version [b25]
- The cleaned version [b29]
- The version with automatic location of the outbound interface [b30]
The configuration file by Emin Gabrielyan [b28]
With all above listed configuration files, the call is terminated within a period of 5 to 15 minutes (the call is terminated faster with b28 file and may last a little longer with b25 file).
In order to make sure that the erroneous behaviour of PortaSIP is not related with Verizon, we decided to send inbound calls from another source. A Budge Tone-100 is configured to have a from URI equal to '+41216939299' and to add a '+' prefix to dialled numbers, similarly to the way we receive calls from Verizon. The phone is also set up to send calls without registration.
We discovered that PortaSIP does not accept calls from the SIP phone. It sends 401-Unathorized reply to the INVITE messages sent by the Budge Tone phone. We compared the INVITE messages of the Budge Tone with those received from Verizon.
Transmissions of manually modified messages via a Perl script demonstrated that the only difference is in the “User-Agent” header field. PortaSIP accepts the call only if the header filed looks as follows: “User-Agent: Cisco-SIPGateway/IOS-12.x”.
Since the SIP phone alone cannot change its User-Agent header field, we make the modifications in an intermediary OpenSER server:
if (method=="INVITE")
{
record_route();
if(is_present_hf("Authorization"))
{
xlog("L_INFO","Request with authorization is not
transmitted\n");
exit;
}
remove_hf("User-Agent");
#without the following line PortaSIP does not accept the call!!!
#append_hf("User-Agent: Cisco-SIPGateway/IOS-12.x\r\n");
append_hf("User-Agent: Cisco-SIPGateway/IOS-xx.x\r\n");
rewritehost("195.129.125.74");
#rewritehost("212.249.15.4");
t_relay();
exit;
};
Without such modification, the PortaSIP rejects the call even if the Budge Tone tries to connect through two OpenSER servers.
- A Perl Script for sending an INVITE followed by a CANCEL and an ACK [pe37] (on 212.249.15.3)
- The last version of the Perl script comparing the INVITEs of Verizon with that of a Budge Tone [pe53] (on 212.249.15.3)
- The OpenSER configuration file permitting calls from a Budge Tone phone to PortaSIP [a32] (on 212.249.15.5)
A call made from a Budge Tone toward 021550 number of PortaSIP proved that PortaSIP does not sends ACK messages after reception of 200 OK replies to its Re-INVITEs. From the other side, PortaSIP understands very well the 200 OK replies, since in case of their absence it terminates the call with a BYE message.
The solution is to reply to the INVITE messages locally from the OpenSER without forwarding the Re-INVITEs to Verizon:
if(loose_route())
{
if(method=="INVITE")
{
xlog("L_NOTICE","$var(peers) Re-INVITE is replied locally
(without retransmission)\n");
sl_send_reply("100","Your Re-INVITE is received");
sl_send_reply("200","OK");
exit;
}
xlog("L_NOTICE","$var(peers) Loose Route\n");
t_relay();
exit;
}
This configuration file is very stable (with respect to established incoming calls).
The stable configuration file runs currently on 212.249.15.4 [b41]
PortaSIP does not understand a single header field format with multiple Record-Route values (which is an RFC 3261 compliant format). This affects the outgoing calls. PortaSIP ignores the 200 OK reply to its INVITE, since the 200 OK of Verizon propagates back the Record-Route information in the single line format. The following script inserted in the reply handler of the OpenSER fixes the problem:
if($hdr(Record-Route[0])!="") $var(rr0)=$hdr(Record-Route[0]);
else $var(rr0)="0";
if($hdr(Record-Route[1])!="") $var(rr1)=$hdr(Record-Route[1]);
else $var(rr1)="0";
if($hdr(Record-Route[2])!="") $var(rr2)=$hdr(Record-Route[2]);
else $var(rr2)="0";
if($hdr(Record-Route[3])!="") $var(rr3)=$hdr(Record-Route[3]);
else $var(rr3)="0";
if($hdr(Record-Route[4])!="") $var(rr4)=$hdr(Record-Route[4]);
else $var(rr4)="0";
if(
$var(rr0)!="0" && $var(rr1)=="0" )
{
$var(rr00)=$(var(rr0){s.select,0,,});
$var(rr01)=$(var(rr0){s.select,1,,});
$var(rr02)=$(var(rr0){s.select,2,,});
$var(rr03)=$(var(rr0){s.select,3,,});
$var(rr04)=$(var(rr0){s.select,4,,});
remove_hf("Record-Route");
if($var(rr00)!="")
append_hf("Record-Route: $var(rr00)\r\n");
if($var(rr01)!="")
append_hf("Record-Route: $var(rr01)\r\n");
if($var(rr02)!="")
append_hf("Record-Route: $var(rr02)\r\n");
if($var(rr03)!="")
append_hf("Record-Route: $var(rr03)\r\n");
if($var(rr04)!="")
append_hf("Record-Route: $var(rr04)\r\n");
}
* * *