« Back to all articles

JavaScript is calling

Written by Jeroen van Veen on 4th April 2017

Calling from a browser to a GSM using your own private GSM network, an Odroid, Asterisk, OpenBTS, WebRTC, and SIP.js

About Software Defined Radio

I first read about the OpenBTS project and its appliance at the Burning Man festival some years ago. I found it an interesting idea that anyone would be able to create his own decentralized mobile network. OpenBTS relies on Software Defined Radio (SDR). The OpenBTS book describes SDR as following:

“This means that radio hardware is no longer designed around a specific application; it is completely up to the host to define the implementation, allowing anyone to create radio applications purely in software.” – Harvind Samra, Cofounder of the OpenBTS project, cofounder and CTO of Range Networks

Instead of having hardware that dictates a radio protocol (Bluetooth, WiFi, GSM, etc.), an SDR allows anyone to dynamically shape radio protocols with nothing but software. SDR’s have been in use by the military for about 20 years, but are becoming more widespread in the consumer market. To get an idea of what an SDR can do, check out some interesting products/Kickstarters, like the USRP B200, BladeRF or the LimeSDR.

It wasn’t until a few months ago that I stumbled upon the much lower-cost USRP B200 SDR device. The USRP B200 can be used as a backend for OpenBTS. OpenBTS is comprised of several services and is basically a bridge between the raw GSM radio traffic and VoIP. It uses Asterisk as its VoIP backend.

One of my personal learning goals at Spindle this year is to learn more about VoIP and Asterisk, so I figured that building my own BTS would be a great opportunity to get started with both Asterisk and OpenBTS!

Ordering hardware

So I ordered the following hardware from my Spindle study budget:

  • USRP B200 SDR Kit 1×1 (70 MHz – 6GHz) with USB3 Cable
  • USRP 24w Power Supply – ETTUS
  • Steel Enclosure Accessory For USRP
  • VERT900 Vertical Antenna (824-960 MHz, 1710-1990 MHz) Dualband (2x)

Mounting the USRP in the steel enclosure was quite easy. The only bump was that the caps on the connectors were very difficult to remove. OpenBTS can be used without special SIM cards (using ‘open registration’), but probably the best thing to do when starting with OpenBTS is to order some preprogrammed custom SIM cards for OpenBTS and a SIM programmer + adapter to roll your own SIM’s once you’re up-and-running with a dozen phones. I ordered the following from a Dutch SDR supplier:

  • SIM card preprogrammed standard (4x)
    SIM card programmed for use with OpenBTS with standard settings MCC=001, MNC=01, IMSI starts with 00101. A label is attached to each SIM with all programmed settings, including ki.
  • SIM programmer
    Full size SIM card programmer. You can use this to program the SIM’s while they are still embedded in the full size holder (credit card sized). You have to place the SIM back into the cardholder or an adapter to use this programmer to (re)program SIM’s after they have been taken from the full size cardholder and have been used in a phone.
  • SIM ADAPTER 1 MmN-F
    Mini/micro/nano SIM card to full size adapter card. Full size adapter card with slots to put in a minisim (standard size) microsim or nanosim (3FF, 4FF) Professional SIM card adapter (plug-in, mini, micro, nano SIM to full size) This is a professional adapter card to insert ID-001 (plug-in, 2FF) SIM cards or micro SIM (3FF) or nano SIM (4FF) cards into a full sized card reader slot.

Notice that you should check the SIM format of your test phones before ordering.

Another thing I needed was a device to run the whole stack on. I started out with my laptop, but I wanted the BTS to be up and running all the time without needing my laptop around. The USRP B200 has a USB3 interface, which pumps a lot of data around. My first thought was to get an Odroid XU4, which has two USB3 ports and enough performance to run Asterisk and OpenBTS. So I ordered the following from a German supplier:

  • ODROID-XU4, 2GHz Cortex A15 & A7
  • ODROID-XU4, Gehäuse, transparent
  • ODROID-XU3/XU4 eMMC-Modul, 64GB

The eMMC module already has Ubuntu preinstalled on it, which makes it quicker to get up and running. If I would have had more spare time, I would rather setup Archlinux Arm.

Installing software

This is not a precise installation manual, because it mostly depends on the environment you want to run it in. It only describes some of the troubles I went through, so you won’t have to 🙂

System packages

This is not a curated list, but you definitely need the following packages:

Archlinux

pacman -S gcc5 python2-pyzmq rsyslog sqlite zeromq readline gcc5 screen systemctl enable rsyslog.service

Ubuntu

apt-get install sqlite3 libzmq3-dev libzmq3 libsqlite3-dev libreadline-dev libfftw3-3 libfftw3-dev libosip2-dev uuid-dev libjansson-dev libxml2-dev libncurses5-dev libvorbis-dev xmlstarlet libpjproject-dev libsrtp0-dev libssl-devLibUHD

LibUHD

At first, you need to have a driver for the B200 itself. The driver is called libUHD, and supports several SDR’s. On my Archlinux laptop it was quite easy to build and it worked straightforward. Setup was like:

mkdir ~/projects/radio cd ~/projects/radio mkdir workarea-uhd cd workarea-uhd git clone https://github.com/EttusResearch/uhd cd uhd git checkout release_003_009_005 cd host mkdir build cd build cmake . sudo make install sudo ldconfig # Add `export LD_LIBRARY_PATH=/usr/local/lib` to .zshrc/.bashrc if it’s not there yet. sudo echo "/usr/local/lib" >> /etc/ld.so.conf

On the Odroid, I had some trouble with libUHD not being able to find the device after compiling. This looked to me like an usb-related issue (despite the udev rules being there). I had more luck installing straight from the Ettus ppa:sudo add-apt-repository:

sudo add-apt-repository ppa:ettusresearch/uhd sudo apt-get update sudo apt-get install libuhd-dev libuhd003 uhd-host

Once the driver is installed, you should perform the following actions to check whether the device is recognized properly:

# Download the UHD FPGA images once sudo uhd_images_downloader  # Install udev rules for usb connectivity once sudo cp /usr/local/lib/uhd/utils/uhd-usrp.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules sudo udevadm trigger # Check connectivity after connecting the device to a USB3 port, uhd_usrp_probe

Everything is fine when you see some device statistics passing by.
Congratulations! You just installed the SDR driver.

OpenBTS

OpenBTS is a bit picky about the libraries it requires. Let’s create a working directory for its dependencies first:

cd ~/projects/radio mkdir -p workarea-openbts/libs

Then check out and compile liba53. This is a stream cipher that takes care of encrypting GSM traffic:

cd workarea-openbts/libs git clone git@github.com:RangeNetworks/liba53.git make sudo make install

Now let’s build ORTP. This library handles audio streams. You need version 0.18.0. Using a newer version either results in a compile-error or ends with an OpenBTS crash at the moment when you start a RTP stream by picking up the phone:

cd workarea-openbts/libs git clone git://git.linphone.org/ortp.git cd ortp git checkout 0.18.0 sh autogen.sh # No need for the libsrtp dependency ./configure --with-srtp=none sudo make install cd ..

OpenBTS and its participants, smqueue and sipauthserve, use an old debugger library that produces coredumps. This library builds fine on Intel chipsets:

cd workarea-openbts/libs git clone git@github.com:RangeNetworks/libcoredumper.git cd libcoredumper wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/google-coredumper/coredumper-1.2.1.tar.gz tar zxf coredumper-1.2.1.tar.gz cd coredumper-1.2.1 patch -p0 < ../fix_from_scratch_build.patch ./configure make sudo make install

You need to remove libcoredumper from the source instead, when building for the Odroid/ARM. Libcoredumper doesn’t work at all on ARM chipsets. In either case, you would need to checkout OpenBTS anyway, so here we go:

cd workarea-openbts/ git clone git@github.com:RangeNetworks/openbts.git cd openbts git submodule init git submodule update

Then for ARM builds, we are going to remove libcoredumper from OpenBTS and its dependencies. Grep for `WriteCoreDump` and `coredump` and manually remove the references from:

CommonLibs/UnixSignal.cpp apps/Makefile.in  apps/Makefile CommonLibs/Makefile.in CommonLibs/Makefile.am

For the Intel chipset build, we could decide to use the default transceiver software that comes with OpenBTS. The transceiver is the part that sits between the radio and the UDP traffic. There is also an alternative transceiver, which can be used together with OpenBTS; it’s called OsmoTRX. This transceiver seems to have more active development going on and has SIMD support for ARM chipsets. This is somewhat of a requirement to keep up with the massive amount of data that comes from the SDR. So we use the OsmoTRX transceiver either way instead of the default one:

cd workarea-openbts/libs git clone git://git.osmocom.org/osmo-trx cd osmotrx autoreconf -i # Only use --with-neon on ARM to get SIMD support.  ./configure --with-neon sudo make install

Finally, we can build OpenBTS itself. Notice the CXX and CC flags that force the usage of GCC version 5. On Archlinux GCC6 is the default, but OpenBTS fails to build with version 6, so we force the usage of GCC5. On Ubuntu, GCC-5 seems to be the default, so you probably don’t need these flags there. The PKG_CONFIG_PATH was needed on Archlinux, otherwise it wouldn’t find libUHD:

cd workarea-openbts/openbts ./autogen.sh CXX=g++-5 CC=gcc-5 PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure --with-uhd sudo make install sudo ldconfig sudo mkdir /var/lib/OpenBTS cd /etc/OpenBts sudo sqlite3 -init ./OpenBTS.example.sql /etc/OpenBTS/OpenBTS.db ".quit"

SIPAuthServe

“SIP Authorization Server (SIPAuthServe) is an application that processes SIP REGISTER requests that OpenBTS generates when a handset attempts to join the mobile network.”

So, without SIPAuthServe, a mobile phone wouldn’t be able to join the new network. We install it with:

cd workarea-openbts/ git clone git@github.com:RangeNetworks/subscriberRegistry.git cd subscriberRegistry git submodule init git submodule update

On ARM, we need to remove the references to coredumper (WriteDump + header + linker info) again. Grep for it and remove the references.

Then proceed with:

sh autogen.sh CXX=g++-5 CC=gcc-5 ./configure sudo make install # The service expects this file to exist. It comes from liba53. sudo ln -s /usr/local/sbin/comp128 /OpenBTS/comp128

SMQueue

“SIP MESSAGE Queue (SMQueue) is an application that processes SIP MESSAGE requests that OpenBTS generates when a handset sends an SMS. It stores the messages, schedules them for delivery in the network, and reschedules them if the target handset is unavailable.”

To install:

cd workarea-openbts/ git clone git@github.com:RangeNetworks/smqueue.git  git submodule init git submodule update

On ARM, we need to remove the references to coredumper (WriteDump + header + linker info) again. Grep for it and remove the references.

Then proceed with:

sh autogen.sh CXX=g++-5 CC=gcc-5 ./configure sudo make install sudo mkdir /var/lib/OpenBTS

Asterisk

You could probably just use the default Asterisk version that comes with your repo, instead of custom building Asterisk. The OpenBTS docs say that OpenBTS is known to work with Asterisk 11, but so far I’ve tested successfully using both Asterisk 11 and Asterisk 13, using the chan_sip driver. Asterisk 13 has better WebRTC support, so we just pick Asterisk 13. To make a custom build with Asterisk 13, we need to ensure that all required dependencies are in place:

cd workarea-openbts/libs git clone git://github.com/asterisk/pjproject pjproject cd pjproject ./configure --prefix=/usr --enable-shared --disable-sound --disable-resample --disable-video --disable-opencore-amr CFLAGS='-O2 -DNDEBUG' make dep sudo make install sudo ldconfig cd workarea-openbts git clone https://github.com/asterisk/asterisk.git cd asterisk git checkout 13.14 # `--without-libedit` fixes an annoying CLI utf8 typing issue. ./configure --without-libedit

Also, there is a handy make menu, which you can use to see which modules Asterisk is going to build:

make menuselect

The default menu selection should be fine. I didn’t check for things like VP8/Opus support for now, which WebRTC is capable of using. Your best bet is probably to modify/select it from make menuselect, before actually compiling. Then proceed with:

sudo make install

This should build and install Asterisk. It could be complaining about missing dependencies, in which case you would need to install those first using your repo’s packages. What I do most of the times when I get a compile error like that, is to internet search for the referenced header file, which most of the times reveals the accompanying package. After building, install the default example configuration:

sudo make samples sudo make config

Tip: this is not mandatory, but if you would like to run Asterisk as another user:

useradd -d /var/lib/asterisk asteriskpbx vim /etc/sudoers # Uncomment: %wheel ALL=(ALL) ALL vim /etc/group # Add asteriskpbx group to wheel: wheel:x:10:root,asteriskpbx chown -R asteriskpbx:asteriskpbx /etc/asterisk/ chown -R asteriskpbx:asteriskpbx /var/lib/asterisk/ chown -R asteriskpbx:asteriskpbx /var/spool/asterisk/ chown -R asteriskpbx:asteriskpbx /var/log/asterisk/ chown -R asteriskpbx:asteriskpbx /var/run/asterisk/

Next, we are going to generate TLS/DTLS keys for our WebRTC support. Replace the IP with your own LAN IP. Use a dummy pw, like ‘foobar’ when asked for it:

sudo mkdir /etc/asterisk/keys cd contrib/scripts ./ast_tls_cert -C 123.123.123.123 -O "somename" -d /etc/asterisk/keys/

To allow WebRTC, we need to enable SIP over websockets and STUN support, edit the following files:

vim /etc/asterisk/rtp.conf

[general]
rtpstart=10000
rtpend=20000
icesupport=yes
stunaddr=stun.l.google.com:19302
vim /etc/asterisk/res_stun_monitor.conf [general] stunaddr=stun.l.google.com:19302 stunrefresh = 30 vim /etc/asterisk/http.conf [general] enabled=yes bindaddr=0.0.0.0 bindport=8088 tlsenable=yes tlsbindaddr=0.0.0.0:8089 tlscertfile=/etc/asterisk/keys/asterisk.pem tlsprivatekey=/etc/asterisk/keys/asterisk.pem

Next, we are going to add extensions that are allowed on Asterisk. We are going to add two mobile phones that will be connected through OpenBTS and two softphones that will connect through a browser:

vim /etc/asterisk/sip.conf [general] udpbindaddr = 0.0.0.0:5060 realm = 123.123.123.123 ; replace with your Server's Public IP Address transport = udp,ws,wss externaddr = 123.123.123 ; replace with your Server's Public IP Address websocket_enabled = true [optionsBTS](!) type=friend context=from-internal dtmfmode=rfc2833 canreinvite=no qualify=no ; openbts do not support OPTION insecure=port,invite allow=gsm host=dynamic [optionsWebRTC](!) type=friend host=dynamic context=from-internal videosupport=yes encryption=yes avpf=yes force_avp=yes icesupport=yes directmedia=no disallow=all allow=ulaw allow=alaw allow=gsm allow=opus allow=vp8 dtlsenable=yes dtlsverify=fingerprint dtlscertfile=/etc/asterisk/keys/asterisk.pem dtlscafile=/etc/asterisk/keys/ca.crt dtlssetup=actpass nat=force_rport,comedia [1000](optionsWebRTC) secret=12345 dial = SIP/1000 [2000](optionsWebRTC) secret=12345 dial = SIP/2000 001011772956952 [IMSI123456787890123](optionsBTS) callerid=6055551231 [IMSI123456787890124](optionsBTS) callerid=6055551232

Now you have two WebRTC clients defined and two phones that can connect through OpenBTS. Next we’ll setup a simple dial plan, which allows Asterisk to dial in on the defined extensions:

vim /etc/asterisk/extensions.conf [from-internal] ; For Testing Audio exten => 1111,1,Answer() same => n,Playback(demo-thanks) same => n,Hangup() ; For Testing Audio exten => 6003,1,Answer() same => n,Progress() same => n,MusicOnHold() exten => 6055551231,1,Dial(SIP/IMSI123456787890123@localhost:5062) exten => 6055551232,1,Dial(SIP/IMSI123456787890124@localhost:5062) exten => 1000,1,Dial(SIP/1000) exten => 2000,1,Dial(SIP/2000)

(!) Notice that you must replace the mobile phone’s IMSI with the ones from your OpenBTS registration in both sip.conf and extensions.conf. The best way to check the IMSI of connecting phones is in the OpenBTS console, but we’ll get to that later. First we’re going to fire up OpenBTS and its minions! I’m going to use screen for this to keep it simple for now:

sudo -s  screen -S osmotrx osmotrx -f <ctrl a+d (escapes out of the screen)> screen -S openbts cd /OpenBTS ./OpenBTS <ctrl a+d (escapes out of the screen)> screen -S sipauthserve sipauthserve <ctrl a+d (escapes out of the screen)> screen -S smqueue smqueue <ctrl a+d (escapes out of the screen)> screen -S asterisk asterisk -cvvvvv <ctrl a+d (escapes out of the screen)> screen -list # shows all screens

So, now we should basically be running the whole stack.

Operating OpenBTS

First let’s attach to the OpenBTS CLI:

screen -r openbts # reattach back to the openbts screen

Transmission of radio is regulated by the Agentschap Telecom in the Netherlands. The upper 5 MHz of the 1800 MHz band is available (1875 – 1879,9 MHz) here to use without license for applications like a small picocell/BTS. Maximum output is 200 mW. GSM uses so-called ARFCN’s as channels. We need to pick a channel of which the downlink falls within these boundaries, in order to comply with local legislation. The ARFCN’s that can thus be used are: 861-885.

It’s important that you check your country’s legislation concerning radio broadcasting first. Most countries require a license for the GSM band. In our case, we can set the GSM band to unlicensed space and set a network name:

OpenBTS> config GSM.Radio.Band 1800  GSM.Radio.Band changed from "900" to "1800" WARNING: GSM.Radio.C0 (51) falls outside the valid range of ARFCNs 512-885 for GSM.Radio.Band (1800) GSM.Radio.Band is static; change takes effect on restart OpenBTS> OpenBTS> config GSM.Radio.C0 870 GSM.Radio.C0 changed from "51" to "870" GSM.Radio.C0 is static; change takes effect on restart OpenBTS> OpenBTS> config GSM.Identity.ShortName MyBTSName

Now we’re going to add new subscribers, so a mobile phone will be able to connect to OpenBTS. From the OpenBTS console, you can see which mobile phones were trying to subscribe, like:

OpenBTS> tmsis IMSI TMSI IMEI AUTH CREATED ACCESSED TMSI_ASSIGNED  123456787890123 - 3210987654321 0 96h 6h 0  123456787890124 - 4210987654321 0 95h 95h 0

 

Now you know the IMSI, you can use a tool called nmcli.py to add the subscribers:

cd workarea-openbts/openbts/NodeManager python2 ./nmcli.py sipauthserve subscribers create "phone1" IMSI123456787890123 6055551231 8430f55ba77243bf91cd0212bcbad52a python2 ./nmcli.py sipauthserve subscribers create "phone2" IMSI123456787890124 6055551231 edb70d6c162146bb87e366c0f72bcc9a

The first parameter is a unique name to identify the subscriber, the second is the IMSI code, which the tmsis CLI command listed. The IMSI can also be found on the SIM cardholder if you ordered OpenBTS preprogrammed SIM’s. The third parameter (MSISDN) is a unique arbitrary number within the network. We use the preferred phone number here. The last parameter is the Ki-code. This is a secret code stored on the SIM. It is used to encrypt GSM calls with using liba53. The Ki-code is also listed on the SIM cardholder if you ordered preprogrammed SIM’s. Calls are not encrypted if you omit this value, but you will be able to register.

Edit both sip.conf and extensions.conf and change the IMSI to the real values and restart Asterisk.

Testing mobile calls

Let’s see if two test mobiles can connect to OpenBTS (fingers crossed). Check your phone’s mobile operators setup and see if the network name (the short code) or a different unusual name (like testplmn) is listed between the operators. Pick the name and see if the phone can register on the network. Let’s test if we can send an SMS to the phone from the OpenBTS console:

sendsms 123456787890123 6055551232 Hi there!!! sendsms 123456787890124 6055551231 Hi there!!!

Now check if an SMS arrives from one mobile to the other. Keep an eye on the Asterisk console (using verbose logging). See if you can call 6055551232 from 6055551231. You now have your own BTS receiving and accepting calls with an Asterisk backend that routes your calls. In my current setup on an open workspace, the acceptable range seems to be about 10/20 meters. There are some settings which allows you to tweak the radio output, which can result in better radio traffic, but this is out of the scope of this article.

Browser calling

Now, let’s move on to the last part; calling from a browser to a mobile phone. I made an example SIP.js VoIP client so you don’t have to hack it yourself. Install like:

cd workarea-openbts/ git clone https://github.com/wearespindle/dialerjs cd dialerjs npm install gulp js scss watch

Change the host variable in the source JavaScript at /src/index.js, so it connects to the right host. Open Chrome or Firefox on your LAN IP at the following port: https://123.123.123.123:8999/. Accept the self-signed insecure certificate. Also, check out if Asterisk’s HTTPS server is running on https://123.123.123.123:8089/httpstatus. Accept the certificate here. You will get an insecure response error in the browser if you try to connect to the Asterisk HTTPS/Websocket server if you don’t accept the certificate first!

The default password should match this article’s settings. Try to login as 1000 and see if you can make a call to 1111 or 6003. You should hear some audio. 6003 may require some additional music-on-hold settings in Asterisk. Watch the Asterisk console while you’re testing. Now try to call a mobile phone like 6055551231 or another browser client that’s logged in as 2000.

The browser uses SIP over a websocket to negotiate a DTLS-SRTP connection to Asterisk. You can also run the VoIP client in Electron if you have it installed:

npm run start

Now you also have a simple desktop VoIP client! It uses a setting to ignore self-signed certificates, so there is no need to accept the certificate here. SIP.js can also run in Node.js, so you can also create server-side VoIP clients if you would like to.

Lessons learned

So, that’s a wrap! I wouldn’t have thought that a browser could connect to a GSM over my own BTS when I got started! I hope you found this article useful. It sure was a lot of fun to hack on and a quite learningful experience to me! I learned some things during the process:

  • SDR is how radio protocols will be shaped in the (near) future.
  • OpenBTS seems a bit outdated, but it’s stable to run your own BTS on. I would also definitely dive into the Osmocom project to learn more about BTS software.
  • I barely scraped the surface of new terms and knowledge that involves running OpenBTS. Especially when it comes to running multiple OpenBTS instances, handovers, etc..
  • OpenBTS interacts nicely with out-of-the-box Asterisk.
  • Asterisk is a versatile software project, able to connect several sources together in a flexible manner, as long it talks SIP and RTP.
  • SIP.js is a very flexible and mature SIP client and makes it a breeze to make a WebRTC and websocket powered VoIP client.

I also didn’t know that after SIP negotiation over a websocket, the browser fires up a DTLS-SRTP connection straight to Asterisk. I always thought that the browser would indirectly be connected by using a websocket transport to some gateway to transfer the media to the final destination. To learn more about radio and SDR, I would highly recommend installing GNURadio:

cd ~/projects/radio mkdir workarea-gnuradio cd workarea-gnuradio git clone --recursive https://github.com/gnuradio/gnuradio cd gnuradio git checkout v3.7.10.1 mkdir build cd build cmake ../ make sudo make install sudo ldconfig

 

I made an example project from other examples that’s using GNURadio to fire up a FM radio. Reception isn’t that good, but that’s probably because the antenna’s are optimized for GSM bandwidth.

 

Your thoughts

No comments so far

Devhouse Spindle