FreeSwitch is a full featured PBX capable of routing both Voice and SMS traffic. We use it to provide multi-modal applications that are capable of mixing voice, SMS, location (RRLP), and presence information. As of now, just voice and SMS are implemented.

System Diagram

In this diagram, Black links are network links (SIP) while Red links are filesystem lookups (sqlite3 db access).


FreeSwitch, being a full-featured project outside the scope of OpenBTS, has it's own wiki detailing the installation and use of the system. As such, we  defer to their wisdom. You'll need two FreeSWITCH modules to interoperate with OpenBTS. Those are  mod_python and  mod_sms. Make sure to install (and configure!) those two modules.



OpenBTS needs to route all outgoing voice and SMS messages to FreeSWITCH. FreeSWITCH provides multiple SIP endpoints, any of them will do. By default, the "internal" context is port 5060, while the "external" context is 5080. Due to a bug in FreeSWITCH, you have to specify the entire IP address. Localhost/ WILL NOT WORK. To update the required fields, change the two following config fields in /etc/OpenBTS/OpenBTS.db to your local address ( in this example).

  SIP.Proxy.Voice =
  SIP.Proxy.SMS =


OpenBTS has a very minimal SIP stack, so make sure that FreeSWITCH does not require any registration to receive SMS or Voice traffic. If you do not, FreeSWITCH will send 401 (Unauthorized) or 407 (Proxy Authentication Required) responses to your REGISTER/INVITE/MESSAGE messages.

Doing this consists of two parts. First, you have to add the OpenBTS IP address to your access control list (in autoload_configs/acl.conf.xml). This allows any traffic from these IP addresses to be accepted without registration. In this example, we added them to the "domains" ACL that already exists:

  <list name="domains" default="deny">
      <!--place your OpenBTS IP here -->
      <node type="allow" cidr=""/>
      <!-- old stuff below... -->

It's worth noting that you CANNOT use the "special" function of the domains ACL that scans all users with CIDR attributed and automatically adds them to the ACL. This doesn't work, as it assumes one IP address per user, a critical flaw when working with OpenBTS.

Following that change, you also need to modify your sip profile to accept connections without authorization. If you are using the internal profile, this is in conf/sip_profiles/internal.xml. Uncomment the following line as follows:

  <param name="apply-register-acl" value="domains"/>

You'll need to set some variables to ensure that FreeSWITCH can communicate with the other services running in the OpenBTS suite. The file conf/vars.xml needs three items defined: The subscriber registry location, and smqueue's host and port. Add these to the end of vars.xml

  <X-PRE-PROCESS cmd="set" data="openbts_db_loc=/var/lib/asterisk/sqlite3dir/sqlite3.db"/>
  <X-PRE-PROCESS cmd="set" data="smqueue_port=5063"/>
  <X-PRE-PROCESS cmd="set" data="smqueue_host="/>

Lastly, there are a few utility scripts we've written that should be made accessible to FreeSWITCH. These are located in (openbtsdir)/FreeswitchConfig/scripts/. Copy all of those to (freeswitchInstallDir)/scripts. Their functions are detailed below.

Restart FreeSWITCH and your system should be receiving and handing OpenBTS traffic.

Using FreeSWITCH with OpenBTS


There are a set of scripts included in the install which provide basic functions for communicating with FreeSWITCH. These are generally accessible from both the dialplan and the freeswitch CLI. These are detailed here:

OpenBTS_DB allows freeswitch plans to engage with the subscriber registry. Note the use of quotations, freeswitch sometimes removes 's, so we use double quotes instead. The basic commands are used as


API: <action application="python" data='OpenBTS_DB SQL_COMMAND"'/>

Example: <action application="python" data='OpenBTS_DB SELECT callerid FROM sip_buddies WHERE name="IMSI000000000000000"'/>
<!-- _openbts_ret set to 11111111 >
<action application="log" data="${_openbts_ret}



EXAMPLE: python OpenBTS_DB OpenBTS_DB SELECT callerid FROM sip_buddies WHERE name="IMSI000000000000000"

OpenBTS_Send_SMS sends an encoded SMS message to smqueue (not OpenBTS!) for storage and eventual forwarding to the client. You are able to specify the target for the SMS (phone number), return number (arbitrary number) and the actual content of the message. Messages over 160 characters are truncated.


API: <action application="python" data="OpenBTS_Send_SMS TARGET_NUM|RETURN_NUM|TEXT"/>

Example: <action application="python" data="OpenBTS_Send_SMS 1111111|1000|Hello from OpenBTS!"/>



Example: python OpenBTS_Send_SMS 1111111|1000|Hello from OpenBTS!

OpenBTS_New_User is a replacement for smqueue's "register" functionality. It inserts a new user into the subscriber registry.


API/Example: <action application="python" data="OpenBTS_New_User"/>
DOES NOT WORK IN DIALPLAN YET. Reads chatplan variables instead of inputting them. 


Example: python OpenBTS_New_User IMSI000000000000000|1111111|5062

OpenBTS_Parse_SMS munges an incoming SMS (from OpenBTS) into a format that FreeSWITCH can understand. This means it can only be run from a chatplan.


API/Example: <action application="python" data="OpenBTS_Parse_SMS"/>
DOES NOT WORK IN DIALPLAN YET. Reads chatplan variables instead of inputting them. 

This sets a number of chatplan variables, with most in Hex.

"openbts_rp_message_type" : RP Message Type
"openbts_rp_message_reference" : RP Message Reference
"openbts_rp_originator_address" : RP Originator Address
"openbts_rp_dest_address_type" : RP Destination Address Type
"openbts_rp_dest_address" : RP Destination Address (SMSC)
"openbts_tp_message_type" : TP Message Type
"openbts_tp_message_reference" : TP Message Reference
"openbts_tp_dest_address_type" : TP Destination Address Type
"openbts_tp_dest_address" : TP Destination Address (SMS target)
"openbts_tp_protocol_id" : TP Protocol ID
"openbts_tp_data_coding_scheme" : TP Data Coding Scheme
"openbts_tp_validity_period" : TP Validity Period
"openbts_tp_user_data" : TP User Data
"openbts_text" : Message Content (Text)


There are example chatplans and dialplans in (openbts_root)/trunk/openbts/FreeswitchConfig/. Here are some snippets of techniques.


  • Routing
    <extension name="local_call">
      <!-- openbts_db_loc set in vars.xml -->
      <condition field='${python(OpenBTS_DB SELECT name FROM sip_buddies WHERE callerid="${destination_number}")}' expression="IMSI\d{15}"/>
      <condition field='${python(OpenBTS_DB SELECT callerid FROM sip_buddies WHERE name="${username}"' expression="\d{7,10}">
        <action application="set" data='target=${python(OpenBTS_DB SELECT name FROM sip_buddies WHERE callerid="${destination_number}")}' />
        <action application="set" data='effective_caller_id_number=${python(OpenBTS_DB SELECT callerid FROM sip_buddies WHERE name="${username}"'/>
        <action application="bridge" data="sofia/internal/${target}@${domain}:${sip_received_port}"/>


  • Parsing: This should be done at the header of your chatplan, to ensure these variables can be used inside of it
        <!-- set all the openbts variables -->
        <extension name="openbts" continue="true">
          <condition field="to_user" expression="^smsc$">
    	<!-- first, parse SMS -->
    	<action inline="true" application="python" data="OpenBTS_Parse_SMS"/>
    	<!-- second, look up sender -->
    	<!-- freeswitch eats 's, switch them up here -->
    	<action inline="true" application="python" data='OpenBTS_DB SELECT callerid FROM sip_buddies WHERE name="${from_user}"'/>
    	<!-- result in _openbts_ret -->
    	<action inline="true" application="set" data="openbts_callerid=${_openbts_ret}"/>
  • Echo
        <extension name="echo">
          <condition field="openbts_tp_dest_address" expression="^9189$">
    	<action application="python" data="OpenBTS_Send_SMS ${openbts_callerid}|9189|${openbts_text}"/>
  • Callback: call a user back from an SMS'd number
        <extension name="callbacks">
          <condition field="openbts_tp_dest_address" expression="^919(\d)$">
    	<!-- bgapi lets us finish this without waiting for the originate -->
    	<!-- the space between the args and the dest is important, for some reason -->
    	<action application="set" data="api_result=${bgapi(originate {origination_caller_id_number=${openbts_tp_dest_address}}sofia/internal/${from}:${from_sip_port} ${openbts_tp_dest_address})}"/>
  • Forward: put at the bottom of the dialplan, catch all traffic that didn't hit and forward it to smqueue
       <!-- send any other messages onto smqueue -->
        <!-- reencode for now... though I'll probably write a "forward" script -->
        <extension name="forward">
          <condition field="openbts_tp_dest_address" expression="^(.*)$">
    	<action application="python" data="OpenBTS_Send_SMS ${openbts_tp_dest_address}|${from_user}|${openbts_text}"/>
  • Registration
        <!-- register a user in the subscriber registry -->
        <extension name="registration">
          <condition field="openbts_tp_dest_address" expression="^101$"/>
          <!-- is it a number? -->
          <condition field="openbts_text" expression="^\d{7,10}$">
    	<action application="python" data="OpenBTS_New_User"/>
    	<action application="set" data="response_text=${_openbts_ret}"/>
    	<!-- lookup new number -->
    	<action application="python" data='OpenBTS_DB SELECT callerid FROM sip_buddies WHERE name="${from_user}"'/>
    	<!-- text back the return value -->
    	<action application="python" data="OpenBTS_Send_SMS ${_openbts_ret}|101|${response_text}"/>