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.
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 $
|