PrimiSIP v070531-aa78, a stateless SIP server in Perl

 

Emin Gabrielyan

Switzernet

2007-05-31

 

We present a stateless SIP server and we explain the basics of SIP signalling on examples of cancelled and answered call attempts.

 

PrimiSIP v070531-aa78, a stateless SIP server in Perl 1

1.     Introduction to PrimiSIP v.070531-aa78 and the download link. 1

2.     Tests between two SIP phones connected to PrimiSIP. 3

2.1.       Cancelled call 3

2.2.       Answered call 4

3.     Testing PrimiSIP with other SIP servers. 6

3.1.       Cancelled call 7

3.2.       Answered call 10

4.     Related links. 14

 

 

1.                  Introduction to PrimiSIP v.070531-aa78 and the download link

 

The features added in this version are:

- Correct call cancellation

- Processing of large stacks of Via header fields and Route header fields: correct functionality with multiple proxy servers on the path

- Support of record-route and loose-route functionality: forcing the path of in-call signalling through proxies

 

The whole script takes 300 lines only.

The user defined handling of requests must appear in the route block which looks as follows:

sub route

{

  print "$mline from $si:$sp\n";

 

  if($method eq "REGISTER")

  {

    save();

    #print_locations();

  }

 

  if(loose_route())

  {

    print "Loose Route \$du=$du\n";

    forward();

    return;

  }

 

  if($method eq "INVITE")

  {

    record_route();

  }

 

  if($method=~m/(^INVITE$|^CANCEL$|^ACK$)/)

  {

    if($method eq "INVITE")

    {

      sl_send_reply("100","Giving a try");

    }

    if($method eq "CANCEL")

    {

      sl_send_reply("200","Cancelling");

    }

 

    if($location{$ou} ne "")

    {

      my $ip=$location{$ou};

      rewritehost($ip);

      forward();

    }

    elsif($rU=~m/^\+41215509.{3}/)

    {

      rewritehost("128.179.67.76");

      forward();

    }

    else

    {

      sl_reply_error();

    }

 

  }

 

}

 

The user defined handling of replies must appear in onreply_route block, which looks as follows:

sub onreply_route

{

  print "$mline from $si:$sp\n";

}

This example only prints the request or status line of the message and the expeditor of the message (IP and port of the sender).

 

Many elements are inherited from OpenSER.

 

The following message-associated parameters are available upon the reception of each message:

my ($mb, $si, $sp, $method, $rb, $headers, $mline);

my ($ou, $tu, $ft, $fu, $du);

my ($ru, $sendhdr);

 

Most of these variables have the same signification as in OpenSER [ref]:

$mb - reference to SIP message buffer

$si - reference to IP source address of the message

$sp - reference to the source port of the message

$rb - reference to message body

$ou - reference to request’s original URI

$tu - reference to URI of ‘To’ header

$ft - reference to tag parameter of ‘From’ header

$fu - reference to URI of ‘From’ header

$du - reference to destination URI

$ru - reference to request’s URI

$method - the requested method

 

The variables below do not have their counterparts in OpenSER and contain the following values:

$headers - reference to header of the received message

$mline - the request or status line of the received message

$sendhdr - the headers of the message that will be transmitted

 

The PrimiSIP server consists of few lines of Perl script:

Download PrimiSIP v.070531-aa78 [txt], [zip]

Change the IP address at the top of the script.

Change the routing block according your requirements.

 

2.                  Tests between two SIP phones connected to PrimiSIP

 

In this section we present flow-graphs of answered and cancelled calls between two telephones connected to PrimiSIP.

 

2.1.                        Cancelled call

 

Here is a flowchart of a cancelled call from SIP phone #9999 (192.168.1.11) to SIP phone #1111 (192.168.1.10), both being connected to PrimiSIP (192.168.1.15).

 

Click on any of the 16 arrows of the graph to see the contents of corresponding SIP messages:

[pdf], [txt], [png], [ps]

 

You may look at the screenshot of the PrimiSIP console during this transaction [png].

 

Since the server is stateless it retransmits the 100-Trying reply of 192.168.1.10 backward to 192.168.1.11 according to the stack of Via headers. If you compare the incoming 100-Trying SIP message (the 3rd message on the graph) with the outgoing 100-Trying SIP message (the 4th message on the graph), you will see the disappeared Via header field in the outgoing message. Similarly is routed back the 200-OK reply of the CANCEL request. The CANCEL and ACK requests are forwarded statelessly, by adding the Via header fields (compare the incoming request with the outgoing) and by modifying the R-URI applying the same rules used for INVITE. A stateful proxy would associate the CANCEL and ACK with the transmitted INVITE, would process them in a hop-by-hop basis (so the ACK would be rather sent locally upon the reception of the 487 response) and would assign the R-URI values of the INVITE without applying the translation rules again.

 

2.2.                        Answered call

 

The figure below shows the flowchart of an answered call from SIP phone #1111 (192.168.1.10) to SIP phone #9999 (192.168.1.11), both being registered at PrimiSIP (192.168.1.15).

 

Click on any of the 27 SIP messages of the graph to see their contents:

[pdf], [txt], [png], [ps]

 

You may also wish to look at the screenshot of the PrimiSIP server’s concsole during this call [png].

 

As we see in the graph the SIP signalling messages during the entire dialog are passing through the SIP server thanks to the Record-Route header field inserted once into the INVITE message. In-call signalling comprises the ACK message sent in response to the 200-OK reply of the INVITE, the INFO requests and the BYE request with their respective replies. The INFO requests carry DTMF signals (see the configurations of SIP phones #1111 and #9999).

 

All in-call signalling requests are handled in the loose-route block (of the user Script section):

  if(loose_route())

  {

    print "Loose Route \$du=$du\n";

    forward();

    return;

  }

Since the loose-route block is before processing of INVITE requests, the R-URI of in-call requests is not modified.

 

Record route request is added for the call’s first INVITE only (the re-INVITEs, if any, will be handled in loose-route block):

  if($method eq "INVITE")

  {

    record_route();

  }

 

The details can be traced in the messages. Examine the messages by clicking on the corresponding areas of the image of the flowchart. The outgoing INVITE, compared with the incoming request have as usual a new R-URI value (due to rewritehost() function) and an additional Via header field. But it also contains a Record-Route header field. This header field is received by the end phone (192.168.1.11) which propagates this information back in its 180-Ringing and 200-OK replies. As soon as these replies are received by the originating phone (192.168.1.10), both parties know about the fact that the proxy server (192.168.1.15) wishes to stay in the signalling path.

 

Now the both phones know that all their signalling messages (during this phone call) must contain a Route header field containing the address previously received in Record-Route header (see the ACK of 192.168.1.10, the two INFO messages from both phones and the BYE from 192.168.1.11). For SIP phones the Route header field signifies first of all that the messages must be sent to the destination specified in the Route field (i.e. to the proxy) instead of being sent according to R-URI (i.e. to the phone’s direct address learned from its Contact field). As a result our proxy server receives all in-call signalling messages. When such a message is received the proxy removes the top most Route header field. If there is another Route header field the proxy will forward the message according to this field. If there are no more Route field, the proxy will route the message according to R-URI (meaning that this was the last proxy in the signalling path, as in the current flowchart). More about Record-Route is available on our US or Swiss mirrors sites [us], [ch].

 

In any case, while the SIP server is removing the Route header fields, it always adds the Via header field for ensuring the backward passage of replies. Compare the incoming in-call requests with their outgoing counterparts to see the modifications carried out by the proxy server.

 

3.                  Testing PrimiSIP with other SIP servers

 

PrimiSIP is tested in a configuration with two other SIP servers lying on the signalling path. The two additional SIP servers are running OpenSER v1.2.0.  Thereby, apart the two end phones; the signalling path now comprises three SIP servers:

 

Click on the five boxes of the image below for viewing their configurations:

[192.168.1.10] – [192.168.1.15] – [128.179.67.76] – [192.168.1.16] – [192.168.1.11]

 

With this configuration we aim at testing the interoperability of PrimiSIP with OpenSER and particularly the routing of requests by stacks of multiple “Route” header fields as well as the routing of replies by stacks of “Via” header fields.

 

All IP addresses are directly routable and all hosts see each other. Therefore we have no NAT issues in this configuration.

 

3.1.                        Cancelled call

 

The figure below shows the flowchart of a cancelled call from SIP phone #1111 (192.168.1.10), registered at PrimiSIP (192.168.1.15), to SIP phone #9999, registered at OpenSER (192.168.1.16).

 

Click on any of the 23 SIP messages, represented by arrows on the graph, to see their contents:

[pdf], [txt], [png], [ps]

 

Compare three INVITE messages: the one received by PrimiSIP, the one transmitted by PrimiSIP to 128.179.67.76, and the one transmitted by 128.179.67.76 to 192.168.1.16. Each proxy adds its Record-Route header field and its Via header field. The 180 status propagates the stack of all three Record-Route header fields back to the originating phone. The replies of the transaction (in particularly the 180 one) are routed back thanks to Via header fields.

 

The observed difference between the stateless PrimiSIP and the two other stateful proxies is in the processing of 100-Trying, 200-Cancelling, and ACK. The stateful proxies process them hop-by-hop, i.e. 100-Trying and 200-Cancelling are (a) generated locally and (b) upon reception of such replies, they are treated locally without re-transmission; ACK is also transmitted locally and is not re-transmitted upon reception from the previous hop. Our stateless proxy also generates 100-Trying and 200-Cancelling locally with its own header server field (Server: PrimiSIP), but it retransmits back the replies of the next hop proxy (Server: OpenSER (1.2.0-notls (i386/linux))). The ACK to 487-Cancelled transmitted by PrimiSIP is not generated locally, but is the re-transmission of the ACK coming from the originating phone (192.168.1.10).

 

The figure below shows the flowchart of a cancelled call in inverse direction. The INVITE comes from SIP phone #9999, registered at OpenSER (192.168.1.16), toward SIP phone #1111 (192.168.1.10), registered at PrimiSIP (192.168.1.15).

 

Click on any of the 23 SIP messages (the arrows) of the graph to see their contents:

[pdf], [txt], [png], [ps]

 

When the INVITE message arrives to PrimiSIP, it already passed through two OpenSER proxies and contains two Record-Route headers of preceding servers. PrimiSIP adds its own Record-Route header on top of the stack and retransmits the INVITE to the called SIP phone. Replies are routed back thanks to the four Via header fields:

        Via: SIP/2.0/UDP 192.168.1.15;branch=z9hG4bK2273.54df7e96.0

        Via: SIP/2.0/UDP 128.179.67.76;branch=z9hG4bK2273.54df7e96.0

        Via: SIP/2.0/UDP 192.168.1.16;branch=z9hG4bK2273.bf3ecbb.0

        Via: SIP/2.0/UDP 192.168.1.11;branch=z9hG4bK359123681ed6082c

Before sending the reply to its next hop (i.e. the previous hop of the request), PrimiSIP removes its own topmost Via field.

 

You may wish to have a look at the screenshot of the PrimiSIP during these two cancelled calls [png].

 

3.2.                        Answered call

 

The figure below shows the flowchart of a call from SIP phone #1111 (192.168.1.10), registered at PrimiSIP (192.168.1.15), to SIP phone #9999, registered at OpenSER (192.168.1.16).

 

Click on any of the 36 SIP messages of the graph to see their contents:

[pdf], [txt], [png], [ps]

 

In the first transaction the destination phone was not yet registered at the target SIP server 192.168.1.16. The target server 192.168.1.16 is configured to return 500-Server-Error when the user is not found (though there is a more appropriate answer). Then we see the registration of the calling phone. Apparently the called phone also gets registered in the mean time, since the second invite was replied by 180-Ringing followed by 200-Answered. The ACK message received by PrimiSIP (from the originating phone) contains three Route header fields:

        Route: <sip:192.168.1.15;lr=on;ftag=07e219d1f2a2e2f2>

        Route: <sip:128.179.67.76;lr=on;ftag=07e219d1f2a2e2f2>

        Route: <sip:192.168.1.16;lr=on;ftag=07e219d1f2a2e2f2>

The PrimiSIP server removes the topmost Route field and routes the ACK according to the next Route header field. In all proxies this ACK is processed in the loose-route section:

 

The loose-route section in PrimiSIP 192.168.1.15:

  if(loose_route())

  {

    print "Loose Route \$du=$du\n";

    forward();

    return;

  }

 

The loose-route section in OpenSER 128.179.67.76 and in OpenSER 192.168.1.16:

  if (loose_route())

  {

    xlog("L_NOTICE","$CrxLoose Route$Cxx\n");

    t_relay();

    xlog("L_INFO","ou=$ou\n");

    xlog("L_INFO","ru=$ru\n");

    xlog("L_INFO","du=$du\n");

    exit;

  };

 

Regardless whether the proxy is stateless or stateful the ACK of an answered call must be retransmitted from the originating phone to the called phone. An intermediary stateful proxy is not permitted to generate an ACK locally in response to a 200-Answered reply.

 

The phone call is terminated by a BYE transaction initiated by the called phone. BYE arrives to the originating phone 192.168.1.10 with four Via header fields. The 200-OK reply to BYE of the originating phone contains the same four Via fields (which will be consumed as the reply travels toward its destination, i.e. toward the initiator of the request).

 

You can check the screenshots of the transit OpenSER at 128.179.67.76 [png], the screenshot of the target OpenSER at 192.168.1.16 [png], and the flowchart of messages in the target OpenSER [png] during the same period of time. In the flowchart of the target proxy we see the registration exchange of the called phone (192.168.1.11) and we also see that before this registration the incoming INVITE request was rejected by a 500-Server-Error reply (more appropriate reply would be  404-User-not-found) and only the post-registration INVITE was answered.

 

The figure below shows the flowchart of an answered call in inverse direction. The INVITE comes now from SIP phone #9999, registered at OpenSER (192.168.1.16), toward SIP phone #1111 (192.168.1.10), registered at PrimiSIP (192.168.1.15).

 

Click on any of the 34 message arrows of the graph to open the corresponding SIP message contents:

[pdf], [txt], [png], [ps]

 

During the shown phone call, there are two in-call transactions, before the BYE request. These transactions transmit DTMF signals via SIP INFO requests. The INFO requests are treated in loose-route blocks of user configuration files running on the proxies. Similarly to all loose-route requests, the INFO requests are routed through the chain of proxy servers thanks to Route header fields (added by end phones to each in-call request). During loose-routing the R-URI is not modified. Examine with the help of links of the flow-graph the R-URI and the header fields of these requests while they are passing through the three proxies. The INFO request of the first transaction (from the called phone to the originating phone) arrives to the PrimiSIP with three Route header fields (because PrimiSIP appeared to be the closest to the sender of the INFO request and the Route header fields are not yet consumed). In the second transaction the INFO request hits PrimiSIP with only one Route header field (because this time PrimiSIP is the last server in the chain).

 

4.                  Related links

 

- The earliest release of PrimiSIP

- Understanding SIP exchanges by experimentation

- The path of SIP signalling messages

- This document [doc], [htm]

 

*   *   *