Asterisk ss7 internals

Chan_ss7 architecture overview


This gives an overview of the architecture for the non-redundant chan_ss7 version 0.3.

Source code


The source code may be downloaded from http://www.sifira.dk/chan-ss7/. The main files are:

  • lffifo.c: This contains the implementation of a lock-free FIFO buffer. This is used to communicate between the ISUP monitor thread and the MTP thread, in a way that makes the MTP thread never have to block waiting for another thread (which could cause the MTP link to fail due to buffer underruns).

  • mtp.c: This implements the MTP thread, which handles all MTP2 (and some MTP3) logic. This thread runs at a higher realtime priority (15) than the rest of Asterisk, and never blocks waiting for another thread (at least this is the goal; there may be some malloc()/free() hidden in ast_sched_* and other APIs used by the MTP thread). This is done to prevent buffer underruns on the MTP2 link, which could cause link failures.

  • isup.c: This containts generic routines for encoding ISUP messages, and generic and specific routines for decoding all supported ISUP messages.

  • chan_ss7.c: This is the main source file. It contains the interface to Asterisk (channel driver callbacks), the ISUP monitor thread, and the ISUP message handling logic and timers.

  • asterisk_safe: A wrapper script to start Asterisk below a `watcher' and a `canary' process. The idea is that if Asterisk is started with realtime priority, and gets into an infinite loop, the canary (which runs at normal priority) will be locked out (like the rest of user space, including root terminals/logins). Then the watcher (which runs at high realtime priority, hence is not locked out by the runaway Asterisk) detects after some period of time that the canary is not responding, and forcefully remotes the realtime priority of Asterisk, thus allowing other processes to run (so that the problem may be fixed).


Thread overview


MTP thread
|
|
|
ISUP monitor
thread
/|\
/ | \
/ | \
/ | \
/ | \
/ | \
Dialplan Dialplan Dialplan ...
thread 1 thread 2 thread 3

  • The MTP thread runs in a poll() loop waiting for the signalling link zaptel fd to be read/write ready. It wakes up every two milliseconds, reads any available signalling units, and writes new signalling units to the link. It also runs any MTP timers that expired. Received ISUP signalling units are delivered to a lock-free FIFO buffer, and an alert pipe is written to wakeup the ISUP monitor thread (telling it to handle the new signalling unit). Signalling units to be sent are read from another lock-free FIFO (put there by the ISUP monitor thread); if no signalling units are available, fill-in signalling units are sent instead. The MTP thread also handles initial alignment, retransmission, and other MTP2 (and some MTP3) tasks.

  • The ISUP monitor thread runs in a poll() loop waiting for the alert pipe to signal available signalling units or an ast_sched object to indicate expired timers. ISUP signalling units are read from the lock-free FIFO written by the MTP thread. Signalling is communicated to dialplan threads using ast_queue_frame(). Outgoing ISUP messages are written to the other lock-free FIFO, to be sent by the MTP thread at the earliest opportunity.

  • Dialplan threads are started either outside of chan_ss7 by Asterisk calling ss7_requester() and ss7_call() (SS7 outging calls), or by the ISUP monitor thread upon receipt of an IAM (incoming calls). They call into channel callbacks such as ss7_read(), ss7_write(), ss7_progess(), etc. Dialplan threads finally call ast_hangup(), either because of an incoming SS7 event (REL ...) made the ISUP monitor thread call ast_softhangup(), or because of hangup at the other end of the call.


Circuit maintenance and locking


All configured circuits have an associated struct ss7_chan * holding all revevant circuit state. When a circuit is in use by an Asterisk channel, a pointer to the struct ast_channel * is stored in the owner field of this struct.

A simple locking scheme is adhered strictly to in order to prevent both races and deadlocks.

There are three types of mutexes used:

  1. A global mutex glock. This is used to protect the circuit lists and the first part of struct ss7_chan.
  2. The Asterisk mutex lock stored in struct ast_channel.
  3. A mutex lock stored for each circuit in struct ss7_chan.

The locking order must always be to first lock glock, then lock the mutex in struct ast_channel, then lock the mutex in struct ss7_chan_. If a circuit has no owner, the order is first glock, then the mutex in struct ss7_chan__. This fixed order prevents deadlocks.

Special care is needed in ss7_hangup() to make sure that all references to the struct ast_channel are gone before invalidating the channel. This is done by synchronising with the ISUP monitor thread on the glock mutex.

Chan_ss7 architecture overview


This gives an overview of the architecture for the non-redundant chan_ss7 version 0.3.

Source code


The source code may be downloaded from http://www.sifira.dk/chan-ss7/. The main files are:

  • lffifo.c: This contains the implementation of a lock-free FIFO buffer. This is used to communicate between the ISUP monitor thread and the MTP thread, in a way that makes the MTP thread never have to block waiting for another thread (which could cause the MTP link to fail due to buffer underruns).

  • mtp.c: This implements the MTP thread, which handles all MTP2 (and some MTP3) logic. This thread runs at a higher realtime priority (15) than the rest of Asterisk, and never blocks waiting for another thread (at least this is the goal; there may be some malloc()/free() hidden in ast_sched_* and other APIs used by the MTP thread). This is done to prevent buffer underruns on the MTP2 link, which could cause link failures.

  • isup.c: This containts generic routines for encoding ISUP messages, and generic and specific routines for decoding all supported ISUP messages.

  • chan_ss7.c: This is the main source file. It contains the interface to Asterisk (channel driver callbacks), the ISUP monitor thread, and the ISUP message handling logic and timers.

  • asterisk_safe: A wrapper script to start Asterisk below a `watcher' and a `canary' process. The idea is that if Asterisk is started with realtime priority, and gets into an infinite loop, the canary (which runs at normal priority) will be locked out (like the rest of user space, including root terminals/logins). Then the watcher (which runs at high realtime priority, hence is not locked out by the runaway Asterisk) detects after some period of time that the canary is not responding, and forcefully remotes the realtime priority of Asterisk, thus allowing other processes to run (so that the problem may be fixed).


Thread overview


MTP thread
|
|
|
ISUP monitor
thread
/|\
/ | \
/ | \
/ | \
/ | \
/ | \
Dialplan Dialplan Dialplan ...
thread 1 thread 2 thread 3

  • The MTP thread runs in a poll() loop waiting for the signalling link zaptel fd to be read/write ready. It wakes up every two milliseconds, reads any available signalling units, and writes new signalling units to the link. It also runs any MTP timers that expired. Received ISUP signalling units are delivered to a lock-free FIFO buffer, and an alert pipe is written to wakeup the ISUP monitor thread (telling it to handle the new signalling unit). Signalling units to be sent are read from another lock-free FIFO (put there by the ISUP monitor thread); if no signalling units are available, fill-in signalling units are sent instead. The MTP thread also handles initial alignment, retransmission, and other MTP2 (and some MTP3) tasks.

  • The ISUP monitor thread runs in a poll() loop waiting for the alert pipe to signal available signalling units or an ast_sched object to indicate expired timers. ISUP signalling units are read from the lock-free FIFO written by the MTP thread. Signalling is communicated to dialplan threads using ast_queue_frame(). Outgoing ISUP messages are written to the other lock-free FIFO, to be sent by the MTP thread at the earliest opportunity.

  • Dialplan threads are started either outside of chan_ss7 by Asterisk calling ss7_requester() and ss7_call() (SS7 outging calls), or by the ISUP monitor thread upon receipt of an IAM (incoming calls). They call into channel callbacks such as ss7_read(), ss7_write(), ss7_progess(), etc. Dialplan threads finally call ast_hangup(), either because of an incoming SS7 event (REL ...) made the ISUP monitor thread call ast_softhangup(), or because of hangup at the other end of the call.


Circuit maintenance and locking


All configured circuits have an associated struct ss7_chan * holding all revevant circuit state. When a circuit is in use by an Asterisk channel, a pointer to the struct ast_channel * is stored in the owner field of this struct.

A simple locking scheme is adhered strictly to in order to prevent both races and deadlocks.

There are three types of mutexes used:

  1. A global mutex glock. This is used to protect the circuit lists and the first part of struct ss7_chan.
  2. The Asterisk mutex lock stored in struct ast_channel.
  3. A mutex lock stored for each circuit in struct ss7_chan.

The locking order must always be to first lock glock, then lock the mutex in struct ast_channel, then lock the mutex in struct ss7_chan_. If a circuit has no owner, the order is first glock, then the mutex in struct ss7_chan__. This fixed order prevents deadlocks.

Special care is needed in ss7_hangup() to make sure that all references to the struct ast_channel are gone before invalidating the channel. This is done by synchronising with the ISUP monitor thread on the glock mutex.

Created by: knielsen, Last modification: Mon 25 of Jun, 2007 (06:20 UTC) by dbmaster
Please update this page with new information, just login and click on the "Edit" or "Discussion" tab. Get a free login here: Register Thanks! - Find us on Google+