OpenBTS L3 Rewrite

This is a direct copy of the L3Rewrite page from the private wiki. As such, many of the links are broken.

Goals and Motivations


The current (2.6/2.7) OpenBTS L3 call control does not implement a real state machine, just a series of straight-line transactions. This produces easy-to-read code, but gives limited functionality and will become overly complex once we need to implement multiple in-progress transactions for a handset.

At this point, we have several known bugs and design shortcomings in the current CS control layer and SIP processor. The current system is not RFC-compliant for SIP, cannot process conference calls or calls on hold, cannot be readily adapted to support handover with legacy networks, and does not handle TMSIs and PTMSIs in a way that is consistent with the GSM specs. We have known for a long time that the control layer was limited and rigid and would eventually need to be rewritten. "Eventually" is here, because doing a full rewrite for a cleaner control layer will not be much more work than to continue patching the known bugs.


We want to replace the straight-line code in SIPEngine.cpp, CallControl.cpp and MobilityMsnagement.cpp with a proper state machine. This state machine will give us much greater flexibility to support new features. We also want to provide hooks, in the form of SIP private headers, to support intergation into legacy networks with yate-based VLR/MSC servers.

After this rewrite, the same control layer will be used for all circuit-switched services in both the OpenBTS GSM design and in the OpenNodeB UMTS design, with logic to select between authentication types where there are differences.

SIP Transaction Processing

By tracking SIP dialog and transaction states separately, we will be able to remove a lot of stalling behavior from the control layer.


We will start using SIP wrappers to transfer handover information. This will allow us to pass handover messages through SIP switches, allowing us to support handover with legacy networks. In normal in-network handovers, these messages will still be passed directly between OpenBTS nodes.

Multiple GSM Transactions

To support call hold, call waiting and conference calls, we need to support multiple GSM transaction on a channel.

Unification of SACCH Processing

The SACCH control layer needs to be moved our of the LogicalChannel class and into the the new state machine.

Technical Plan

General Approach

The current call control code spends most of its time in a loop called the "call processing loop". This loop will be expanded to eventually include all of the Um layer 3 functions except for paging and initial channel assignment. This loop will also handle all handset-related SIP functions except for processing of inbound initiating messages.

Every DCCH will have 2 or 3 threads:

  • DCCH (SDCCH or TCH) signaling thread
  • SACCH signaling thread
  • media thread, for TCHs, not needed for SDCCHs

All of the message handlers for GSM L3 and SIP will be implemented as methods in the TransactionEntry class. The TransactionEntry class will get two public methods for message handling:

  • dispatch SIP, which accepts a complete SIP packet and dispatches and processes it
  • dispatch GSM, which accepts a GSM L3 frame and dispatches and processes it

Handling methods for specific GSM and SIP messages are to be private or protected. Similarly, there will be a public method for checking SIP and GSM timers and taking appropriate action, but the handlers for individual timers will be private or protected.

The external transaction table database will be removed, as will the physical channel reporting database. This is all being done with SIP now.

(pat) I think the above is exactly correct.

Other Changes

Peering Over SIP

The current private protocol for peering (including handover) will be replaced with SIP MESSAGE methods. Where possible we will use GSM L3 IEs to transfer radio resource information and SDP to transfer RTP state information.

SIP 3xx Redirect

The OpenBTS SIP implementation needs to support 3xx redirect messages. This will give much greater flexibility in call routing.

No more external reporting tables

We will no longer support the external database tables for transaction and physical channel status reporting. These functionality will be replaced with private SIP headers. These will include:

  • PHY status and parameters
  • IMEI (for LUR)
  • previous LAI (for LUR)
  • current serving cell


Adding USSD? is a high priority. #48. This is best done after rewriting the current control layer.

Changes to TMSI Table

Most of the information in the TMSI table should be global, consistent across the whole network, or at least across all of the BTS units served by the same SR or VLR. So in this new rewrite, this information will be passed to and from the VLR or SR in SIP private headers, with the local TMSI table just acting as a cache. In particular:

  • TMSIs and PTMSIs will be generated by the VLR/SR and passed back in the SIP OK response.
  • Previous LAI and TMSI will be passed to the VLR/SR in the SIP REGISTER method.
  • Authentication/ciphering tuples are generated by HLR or SR and passed to OpenBTS in the SIP OK response, possibly relayed by a VLR. In any case the "real" active, authoritative set of tuples at any moment is in the VLR or SR and the OpenBTS unit must obtain those values from the VLR or SR via SIP.

Registry Access is SIP-Only

OpenBTS will not access the registry database directly. All information transfer between OpenBTS and the registry will be though SIP exchanges with the SR or VLR.

Release Schedule

The new state machine rewrite will be designated as OpenBTS 4.0. We should make at least one more 3.x series release prior to this release. (The current release is 3.0, so we at least need a 3.1 release, probably in Feb 2013.) The total schedule for the rewrite is about 4 months, starting in Jan 2013.

Coordination with SS7Ware

The OpenBTS control layer rewrite will be run in coordination with SS7Ware's update and testing of their SIP-side VLR/MSC interface. This first phase of this SS7Ware project will be the generation of a document describing specific supported SIP transactions and their associated private headers. Details of this schedule will be posted when they are available.

Stages and Milestones

OpenBTS-MSC/VLR SIP Messaging Specification

This document will be generated by SS7Ware, in cooperation with Range Networks.

Initial Rewrite

Ticket #15. This is a rewrite that replaces all existing functions of the original control layer, but does not add new functions and does not support handover.

Baseline Rewrite

Ticket #1237. This step adds handover to the state machine. Once this milestone is achieved, the new state machine can be merged into the trunk to form a new baseline.

Baseline Rewrite Status 2/4/2012

  • Branch is in openbts/features/l3rewrite. The amr2 and SIPdirect branches were integrated.
  • Generated documentation consisting of ladder diagrams and exactly where in the code base each is handled. This is throw-away, since the code is being thrown away. Document source is in file ladders.awk.
  • Only took a dozen lines of code in the GSM directory to hook all the channels (SACCH, SDCCH and FACCH) thanks to the modular design there.
  • Looks like we can keep TransactionTable and TransactionEntry mostly unchanged; just a few additions.
  • Wrote the new message dispatcher but it was trivial. It just receives all messages from everywhere and calls the handler in each running TransactionEntry. No problem allowing multiple TransactionEntries per MS.
  • Looks like the l3rewrite can be enabled by an sql switch, which will make debugging easier, and vastly reduces the risk of this whole rewrite. We will not be able to throw the switch in the middle of an ongoing procedure. (There is no way to jump into the middle of MOCController, for example.)
  • The whole point of this exercise is to allow multiple state machines to run simultaneously, but the exact internal structure of the state machines is a major design decision with many possibilities. My experience with state machines is that they are difficult to maintain and easy to desynchronise. That seems to be partly because some people find state-machines intrinsically difficult to understand, and partly because the data in a state machine may end up partitioned in such a way that it does not correspond closely to the description of the problem being solved, in other words, a change may require sub-changes in widely separated locations, with side-effects that are sometimes difficult to understand. Multiple people are going to work on this code for years, so it must be easy to maintain, and further, it is going to undergo massive changes, additions and extensions. We dont want to end up with a mess. I'm blathering, but it is important to get the initial problem partitioning correct.
  • The best solution seems to be to make the fundamental unit of statemachineness be the Procedure, where a "Procedure" means MOC, MTC, MO-SMS, MT-SMS, LocationUpdate, etc. Maybe that seems obvious, but I can think of lots of other ways to do it. The reasons are that we need such a 'Procedure' class anyway, both to identify the Procedure currently associated with the TransactionEntry (which was formerly identified implicitly by the C++ function running, eg, MOCController) and secondly because most Procedures have some private variables that are local to the Procedure, and that data has to live somewhere. In other words, there will be a (or sometimes more than one) StateMachine class per L3 Procedure, which encapsulates the code for that Procedure.
  • A StateMachineProcedure can later be sub-divided into smaller sub-procedures as finely as desired, all the way down to the individual message level, so there does not seem to be any downside to aggregating as much as makes sense. The Procedure attached to the Transaction can be changed by a state machine action, equivalent to a return-less function call. I dont think they need to be non-trivially nested.
  • Interactions between different Procedures (other than cancellation) are rare. An exception may be Call-Hold and Call-Waiting.
  • Since individual Message handling may vary per-L3 Procedure, this partitions the actions based on a Message in a natural way.
  • This state-machine organization will also end up slightly resembling the existing code, (at least, as much as a state machine can) which will make it easier to get all the million details (timers and such) correct.
  • I started out on MOC/MTC but switched to LUR because it is the both the shortest and the most complicated Procedure. It is also the first one we will need - three wins.
  • Currently puzzling about the exact way the SIP code should interact with the state machines. The answer will become obvious as I proceed. I am trying to do all this with the least disruption to existing code, which worked great in the GSM directory and even Transaction.cpp, but I'm afraid SIPEngine.cpp needs a total rewrite.
  • I'll do SMS last. I looked at the code in LogicalChannel and I think it will be easy to move into the new framework.


Add USSD support. Ticket #48.

SIP Redirect

Add support for SIP 3xx redirect responses for outbound dialogs.


Once the SR/VLR/TMSI-table rework is complete, we can add ciphering support in L1. See Ciphering Support.

Parallel Transactions

Ticket #38. Add support for multiple parallel transactions on the GSM SDCCH. Each transaction will have its own TransactionEntry object.