YXA SIP software
 
 

YXA architecture

YXA is both an Erlang SIP stack, and a collection of SIP applications. It should be possible to create a UAS/UAC/B2BUA with this code as well, but that has not been our primary interest as of yet.

Stack

RFC3261 specifies a clearly layered model to handle SIP transactions. The first attempt of transaction support in YXA did not follow this model, but it became apparent that it was a bad idea to not follow the RFC-model. Hence, YXA is now transaction stateful with module names and functions named very intuitively.

layers

The transport layer receives requests and responses from the network and pass them on to the transaction layer. It also receives requests to send requests and responses to the network. These requests come from the transaction layer, except in special cases where they might come directly from the transaction user (or application). These special cases are 2xx responses to INVITE, and ACKs of these 2xx responses to INVITE.

The most complicated part of a SIP-stack (besides parsing SIP messages) is the transaction layer. It filters out resends received from the transport layer, and takes care of delivering requests and responses reliably even over unreliable transports (like UDP). This requires a rather complicated state machine, lots of timers and other things.
There are two different types of transactions :

  • Server transactions are created automatically when new requests are received from the transport layer. If the request is an INVITE, the server transaction sends out a 100 Trying response (to make the other side not resend the INVITE) but otherwise it just sits there until the application comes up with a response to send, or the transaction layer receives a CANCEL matching the server transaction. Details about the operations of the server transactions can be read in RFC3261 #17.2.
  • Client transactions are outbound requests and are always created by the application. For details about what is a client transaction is, see RFC3261 #17.1. Basically, a client transaction is an erlang process that handles resending of a request until a response is returned or the client transaction times out. If the client transaction times out, this is signalled to the transaction user as if a '503 Service Unavailable' was received.

    An application (or transaction user) on the other hand isn't very complicated, as long as the stack is operating transaction stateful (the YXA stack is).
    The application receives new requests from the transaction layer and may send responses in return by passing them to the transaction layer. The application is spared of all the hassles with resends, and may start any number of client transactions and then just sit around waiting for them to result in responses, with which the application then may do as it pleases.

Request processing details

Click here for a sequential diagram of how the YXA SIP-stack receives an INVITE (note: this diagram is a bit outdated since Poseidon was unusable for modifying an existing diagram, and I haven't had time to re-do the diagram in another editor).

A number of assumptions are made about the diagram :

  • The application is incomingproxy.
  • This is the first request to arrive after the application started, so there exists no prior transaction nor transport state.
  • The request is received over TCP.
  • The request is an INVITE.
  • There is nothing syntactically incorrect about the request.
  • The request has a Request-URI that matches one of our users, and that user has exactly one location registered in the location database.
  • The users location in the location database may be reached through an outbound TCP connection.

As you can see by the yellow note in the diagram, the application has a central role but is a really small part of the process of proxying an INVITE to a users registered location.

The annoying mythological image in the background will go away once I re-do the drawing in an sequential diagram editor for which I actually have a license.

Between transport and transaction layer

Before a request or response is passed to the transaction layer, a number of checks are performed to ensure some basic level of sanity (and thereby avoiding excessive error checking later on).
    Request checks :
    • Append received= to Top Via if necessary
    • Record source port in Top Via rport if present
    • Check if URI scheme is one we support (currently only sip:)
    • Check we can parse From: and To:
    • Check for valid CSeq
    • Check for looping request
    • Rewrite Request-URI if received from a strict router
    • Remove Route header matching this proxy

    Response checks:
    • Check we can parse From: and To:
    • Drop packet if Top Via does not match me

Application

All current YXA applications (incomingproxy, outgoingproxy, pstnproxy and appserver) are really just function librarys. The idea is that you should only have to write a small application, and the YXA stack should handle everything at the transport and transaction layer for you.

Your application must export the following three functions :

%% Standard YXA SIP-application exports
-export([init/0,
         request/3,
         response/3]).

Your application should have an application specification file (example : your-application.app) that specifies

{mod, {sipserver, [your-application]}}
This makes erlang run the sipserver:start/1 function with your-application as argument. Sipserver:start() will start the basic YXA processes seen here in the OTP supervisor tree.
If you are thinking 'what is OTP?' then follow this link to the Erlang OTP design principles for more information the Open Telecom Platform.

YXA startup stable state

In this figure, squares represent OTP supervisors and circles OTP worker processes.

transaction_layer and tcp_dispatcher has double edges because they track exit signals from their linked processes (both of them maintain a list of their children, and need to remove data from these lists when the child processes die/exit.

directory is the LDAP client. If you haven't configured an LDAP server then this process will just sit there.

logger is the log file writing process.

As you (hopefully) can see, there might be multiple tcp_listener processes. Each one does accept() on a different socket. TCP sockets can be of different protocols (SIP socket protocols) :

  • tcp (TCP over IPv4)
  • tcp6 (TCP over IPv6)
  • tls (TLS over TCP over IPv4)
  • tls6 (TLS over TCP over IPv6)

$Id: architecture.html,v 1.9 2007/12/12 07:40:59 ft Exp $

Parts of logo