How To Run Your Own Cell Tower

This post is a collection of notes from about 2 years ago. I keep meaning to post them, but keep getting busy and not having time to get around to editing them for clarity and/or currency, but I keep wanting to reference them, so I’m finally just going to dump them here as-is.

NOTE: doing this may be illegal in your jurisdiction. Specifically, using the radio frequencies required for this to work could potentially be a federal offense (in the US). Be sure you understand the legalities before following this guide, etc.

Also note: this is specifically for GSM network cell traffic. If you are looking for something other than GSM, things are going to be different.

And with that all said, the following basically takes you from “I just installed Ubuntu” to “I have cell phones calling/texting each other through my ‘tower'” in roughly 10 minutes using a BladeRF and a laptop.

Equipment

  • [ 1 ] of Lenovo Y50-70 Laptop: http://shop.lenovo.com/us/en/laptops/lenovo/y-series/y50/
  • [ 1 ] of BladeRF x40: https://www.nuand.com/blog/product/bladerf-x40/
  • [ 1 ] of BladeRF case: https://www.nuand.com/blog/product/bladerf-case/
  • [ 2 ] of Superbat 5dbi 700-2600Mhz 4G LTE Omni Directional Antenna: http://amazon.com/Superbat-700-2600Mhz-Directional-Antenna-Connector/dp/B00FE7KMYS

NOTE: OS used for this project was Linux Mint 17.3 – KDE Spin

$ cat /etc/lsb-release
DISTRIB_ID=LinuxMint
DISTRIB_RELEASE=17.3
DISTRIB_CODENAME=rosa
DISTRIB_DESCRIPTION="Linux Mint 17.3 Rosa"

$ uname -srp
Linux 3.19.0-32-generic x86_64

Getting BladeRF Running

  • Add your username to the plugdev and dialout groups
$ sudo usermod -a -G plugdev <username>
$ sudo usermod -a -G dialout <username>
  • Install the bladeRF PPA and required software packages
$ sudo add-apt-repository ppa:bladerf/bladerf
$ sudo apt-get update
$ sudo apt-get install bladerf libbladerf-dev bladerf-firmware-fx3 bladerf-fpga-hostedx40
  • Create a ~/.Nuand/bladeRF directory
  • Store the FPGA image in the bladeRF directory so it can be autoloaded
$ mkdir -p ~/.Nuand/bladeRF
$ cd ~/.Nuand/bladeRF
$ wget http://hoopycat.com/bladerf_builds/latest/artifacts/hostedx40.rbf

*NOTE: since we installed the FPGA from the bladeRF PPA, you could also just link to that from your bladeRF directory instead:

$ ln -s /usr/share/Nuand/bladeRF/hostedx40.rbf ~/.Nuand/bladeRF
  • Grab the latest firmware image
$ wget http://hoopycat.com/bladerf_builds/latest/artifacts/firmware.img
  • Plug the bladeRF in
  • Verify the system sees the device
$ dmesg | grep usb
...
[  992.744233] usb 2-2: New USB device found, idVendor=1d50, idProduct=6066
[  992.744235] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  992.744237] usb 2-2: Product: bladeRF
[  992.744239] usb 2-2: Manufacturer: Nuand
[  992.744240] usb 2-2: SerialNumber: d1c8b59ff5d36d39d43af24eaca005a3
...
  • Verify the system recognizes the bladeRF (using bladeRF-cli):
$ bladeRF-cli -p

  Backend:        libusb
  Serial:         d1c8b59ff5d36d39d43af24eaca005a3
  USB Bus:        2
  USB Address:    2

$ bladeRF-cli -i
[INFO @ version_compat.c:116] FPGA version (v0.4.1) is newer than entries in libbladeRF's compatibility table. Please update libbladeRF if problems arise.

bladeRF> info
  Serial #:                 d1c8b59ff5d36d39d43af24eaca005a3
  VCTCXO DAC calibration:   0x8ea1
  FPGA size:                40 KLE
  FPGA loaded:              yes
  USB bus:                  2
  USB address:              5
  USB speed:                SuperSpeed
  Backend:                  libusb
  Instance:                 0
  • Update the firmware on the bladeRF
$  cd /usr/share/Nuand/bladeRF/
$  bladeRF-cli --flash-firmware ./firmware.img

Install Yate

  • Checkout the source code from SVN and compile
$ mkdir svn
$ cd svn/
$ svn checkout http://voip.null.ro/svn/yate/trunk yate
$ cd yate/
$ ./autogen.sh
$ ./configure
$ sudo make install
$ which -a yate-config
/usr/local/bin/yate-config

Install YateBTS

  • Checkout the source code from SVN and compile
$ cd ..
$ svn checkout http://voip.null.ro/svn/yatebts/trunk yatebts
$ cd yatebts/
$ ./autogen.sh
$ ./configure
$ sudo make install
  • Pickup the new libraries
$ sudo ldconfig

Configure YateBTS

  • Setup the GSM band information by setting the following values in the configuration file:
$ sudo vi /usr/local/etc/yate/ybts.conf
...
Radio.Band=1800
Radio.C0=512
...
Radio.PowerManager.MaxAttenDB=40
Radio.PowerManager.MinAttenDB=40

Run YateBTS

  • Run the yate binary as root
$ sudo yate -vvvvvv
Yate (23748) is starting Tue Oct 20 14:03:52 2015
2015-10-20_14:03:52.041354 <ALL> Plugin::Plugin("filetransfer",false) [0x7f0204349880]
Loaded module File Transfer
2015-10-20_14:03:52.041441 <ALL> Plugin::Plugin("tonedetect",false) [0x7f0204139300]
Loaded module ToneDetector
2015-10-20_14:03:52.041509 <ALL> Plugin::Plugin("fileinfo",false) [0x7f0203f30c40]
Loaded module FileInfo
2015-10-20_14:03:52.041603 <ALL> Plugin::Plugin("extmodule",false) [0x7f0203d259c0]
Loaded module ExtModule
2015-10-20_14:03:52.041916 <ALL> Plugin::Plugin("javascript",true) [0x7f0203b11640]
Loaded module Javascript
2015-10-20_14:03:52.041998 <ALL> Plugin::Plugin("moh",false) [0x7f0203694440]
Loaded module MOH
2015-10-20_14:03:52.042148 <ALL> Plugin::Plugin("isaccodec",false) [0x7f020348a780]
Loaded module iSAC floating point - based on WebRTC iSAC library version 4.3.0 (SPL version 1.2.0)
2015-10-20_14:03:52.042348 <ALL> Plugin::Plugin("iax",false) [0x7f020324e5c0]
Loaded module YIAX
2015-10-20_14:03:52.042522 <ALL> Plugin::Plugin("yrtp",false) [0x7f0203012280]
Loaded module YRTP
2015-10-20_14:03:52.042643 <ALL> Plugin::Plugin("cdrfile",true) [0x7f0202deb240]
Loaded module CdrFile
2015-10-20_14:03:52.042750 <ALL> Plugin::Plugin("gsmcodec",false) [0x7f0202be6180]
Loaded module GSM - based on libgsm-1.0.10
2015-10-20_14:03:52.042836 <ALL> Plugin::Plugin("regexroute",false) [0x7f02027d1f80]
Loaded module RegexRoute
2015-10-20_14:03:52.042914 <ALL> Plugin::Plugin("callgen",false) [0x7f02025c58c0]
Loaded module Call Generator
2015-10-20_14:03:52.042975 <ALL> Plugin::Plugin("pbx",false) [0x7f02023ba540]
Loaded module PBX
2015-10-20_14:03:52.043061 <ALL> Plugin::Plugin("conf",false) [0x7f02021b34c0]
Loaded module Conference
2015-10-20_14:03:52.043150 <ALL> Plugin::Plugin("rmanager",false) [0x7f0201fa4900]
Loaded module RManager
2015-10-20_14:03:52.043240 <ALL> Plugin::Plugin("wave",false) [0x7f0201d949c0]
Loaded module WaveFile
2015-10-20_14:03:52.043326 <ALL> Plugin::Plugin("analyzer",false) [0x7f0201b83480]
Loaded module Analyzer
2015-10-20_14:03:52.043401 <ALL> Plugin::Plugin("stun",false) [0x7f0201978400]
Loaded module YSTUN
2015-10-20_14:03:52.043652 <ALL> Plugin::Plugin("sip",false) [0x7f020176d980]
Loaded module SIP Channel
2015-10-20_14:03:52.043754 <ALL> Plugin::Plugin("callfork",false) [0x7f02015084c0]
Loaded module Call Forker
2015-10-20_14:03:52.043824 <ALL> Plugin::Plugin("dumb",false) [0x7f02012fd200]
Loaded module DumbChannel
2015-10-20_14:03:52.043895 <ALL> Plugin::Plugin("cdrbuild",false) [0x7f02010f63c0]
Loaded module CdrBuild
2015-10-20_14:03:52.043978 <ALL> Plugin::Plugin("gvoice",false) [0x7f0200ee94c0]
Loaded module GVoice
2015-10-20_14:03:52.044048 <ALL> Plugin::Plugin("enumroute",false) [0x7f0200ce3240]
2015-10-20_14:03:52.044810 <ALL> Plugin::Plugin("jingle",false) [0x7f0200adbac0]
Loaded module YJingle
2015-10-20_14:03:52.044949 <ALL> Plugin::Plugin("msgsniff",false) [0x7f0200646540]
Loaded module MsgSniffer
2015-10-20_14:03:52.045010 <ALL> Plugin::Plugin("cdrcombine",false) [0x7f02004409c0]
Loaded module CdrCombine
2015-10-20_14:03:52.045128 <ALL> Plugin::Plugin("ilbccodec",false) [0x7f020023a080]
Loaded module iLBC - based on iLBC reference library
2015-10-20_14:03:52.045223 <ALL> Plugin::Plugin("socks",true) [0x7f0200017a80]
Loaded module YSOCKS
2015-10-20_14:03:52.045369 <ALL> Plugin::Plugin("ilbcwebrtc",false) [0x7f01ffe00600]
Loaded module iLBC - based on WebRTC iLBC library version 1.1.1
2015-10-20_14:03:52.045465 <ALL> Plugin::Plugin("tone",false) [0x7f01ffbe8680]
Loaded module ToneGen
2015-10-20_14:03:52.045484 <tone:ALL> Building comfort noise at level -10
2015-10-20_14:03:52.045537 <tone:ALL> Building tone of 1336 + 941 Hz
2015-10-20_14:03:52.046160 <tone:ALL> Building tone of 1209 + 697 Hz
2015-10-20_14:03:52.046751 <tone:ALL> Building tone of 1336 + 697 Hz
2015-10-20_14:03:52.047344 <tone:ALL> Building tone of 1477 + 697 Hz
2015-10-20_14:03:52.047936 <tone:ALL> Building tone of 1209 + 770 Hz
2015-10-20_14:03:52.048536 <tone:ALL> Building tone of 1336 + 770 Hz
2015-10-20_14:03:52.049135 <tone:ALL> Building tone of 1477 + 770 Hz
2015-10-20_14:03:52.049737 <tone:ALL> Building tone of 1209 + 852 Hz
2015-10-20_14:03:52.050321 <tone:ALL> Building tone of 1336 + 852 Hz
2015-10-20_14:03:52.050906 <tone:ALL> Building tone of 1477 + 852 Hz
2015-10-20_14:03:52.051496 <tone:ALL> Building tone of 1209 + 941 Hz
2015-10-20_14:03:52.052086 <tone:ALL> Building tone of 1477 + 941 Hz
2015-10-20_14:03:52.052677 <tone:ALL> Building tone of 1633 + 697 Hz
2015-10-20_14:03:52.053268 <tone:ALL> Building tone of 1633 + 770 Hz
2015-10-20_14:03:52.053867 <tone:ALL> Building tone of 1633 + 852 Hz
2015-10-20_14:03:52.054458 <tone:ALL> Building tone of 1633 + 941 Hz
2015-10-20_14:03:52.055048 <tone:ALL> Building tone of 2000 + 125 Hz
2015-10-20_14:03:52.056957 <tone:ALL> Building tone of 2000 modulated by 1000 Hz
2015-10-20_14:03:52.059324 <tone:ALL> Building tone of 2010 Hz
2015-10-20_14:03:52.059486 <tone:ALL> Building tone of 1780 Hz
2015-10-20_14:03:52.059799 <ALL> Plugin::Plugin("mux",true) [0x7f01ff9d8640]
Loaded module MUX
...

Use Yate Telnet Interface

  • Telnet to port 5038 on the host running YateBTS
telnet 127.0.0.1 5038
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
YATE 5.5.1-devel1 r6056 (http://YATE.null.ro) ready on localhost.
  • Get a list of registered devices
nib list registered
IMSI            MSISDN
--------------- ---------------
  • Get a list of rejected devices
nib list rejected
IMSI            No attempts register
--------------- ---------------
001010000000002    2
  • Observe rejections in the yate debug output:
2015-10-20_14:18:33.424894 <ybts-signalling:INFO> Received [0xcd0240]
-----
Primitive: PhysicalInfo
Info: 0
Connection: 3

<PhysicalInfo>TA=0 TE=3.000 UpRSSI=1 TxPwr=33 DnRSSIdBm=-83 time=1445365112.943</PhysicalInfo>
-----
2015-10-20_14:18:33.425026 <ybts-signalling:INFO> Received [0xcd0240]
-----
Primitive: L3Message
Info: 0
Connection: 3

<MM>

  <SkipIndicator>0</SkipIndicator>
  <NSD>0</NSD>
  <Message type="LocationUpdatingRequest">
    <LocationUpdatingType>
      <FOR>false</FOR>
      <LUT>normal-location-updating</LUT>
    </LocationUpdatingType>
    <CipheringKeySequenceNumber>no-key/reserved</CipheringKeySequenceNumber>
    <LAI>
      <PLMNidentity>00101</PLMNidentity>
      <LAC>fffe</LAC>
    </LAI>
    <MobileStationClassmark>
      <RFPowerCapability>class1</RFPowerCapability>
      <RevisionLevel>GSM-phase2</RevisionLevel>
      <Flags>ES-IND</Flags>
    </MobileStationClassmark>
    <MobileIdentity>
      <IMSI>001010000000002</IMSI>
    </MobileIdentity>
  </Message>
</MM>
-----
2015-10-20_14:18:33.425160 <ybts-signalling:ALL> Added connection (0x7f01d4001330,3) [0xcd0240]
2015-10-20_14:18:33.425180 <ybts-mm:ALL> Handling LocationUpdatingRequest conn=3: ident=IMSI/001010000000002
LAI=00101_fffe [0xcd0620]
2015-10-20_14:18:33.425194 <ybts-mm:ALL> Added UE (0x7f01d4002b10) TMSI= IMSI=001010000000002 [0xcd0620]
2015-10-20_14:18:33.425206 <ybts-signalling:ALL> Connection 3 set UE (0x7f01d4002b10) TMSI= IMSI=001010000000
002 [0x7f01d4001330]
2015-10-20_14:18:33.425233 <ybts-signalling:INFO> Sending [0xcd0240]
-----
Primitive: L3Message
Info: 0
Connection: 3

<MM>
  <Message type="IdentityRequest">
    <IdentityType>IMEI</IdentityType>
  </Message>
</MM>
-----
2015-10-20_14:18:33.456519 <gsmtrx:ALL> ARFCN[0]: Slot 0. Excessive TOA error=-3 peak/mean=3.21735 count=1 [0
x7f01d8016150]
2015-10-20_14:18:33.796915 <gsmtrx:ALL> ARFCN[0]: Slot 6. Excessive TOA errors 8 [0x7f01d8016150]
2015-10-20_14:18:33.875678 <gsmtrx:ALL> ARFCN[0]: Slot 6. Excessive TOA error=-5 peak/mean=3.09984 count=1 [0
x7f01d8016150]
2015-10-20_14:18:34.045461 <gsmtrx:ALL> ARFCN[0]: Slot 5. Excessive TOA error=-5 peak/mean=3.8067 count=12 [0
x7f01d8016150]
2015-10-20_14:18:34.116751 <gsmtrx:INFO> ARFCN[0]: Slot 0. Receiver clipping 1.81203 dB (FN=189757) count=17
[0x7f01d8016150]
2015-10-20_14:18:34.130972 <ybts-signalling:INFO> Received [0xcd0240]
-----
Primitive: PhysicalInfo
Info: 0
Connection: 3

<PhysicalInfo>TA=3 TE=-1.000 UpRSSI=1 TxPwr=30 DnRSSIdBm=-83 time=1445365112.931</PhysicalInfo>
-----
2015-10-20_14:18:34.131057 <ybts-signalling:INFO> Received [0xcd0240]
-----
Primitive: L3Message
Info: 0
Connection: 3

<MM>
  <SkipIndicator>0</SkipIndicator>
  <NSD>1</NSD>
  <Message type="IdentityResponse">
    <MobileIdentity>
      <IMEI>358300080209030</IMEI>
    </MobileIdentity>
  </Message>
</MM>
-----
2015-10-20_14:18:34.131165 <ybts:ALL> Started location updating thread for (0x7f01d4002b10) TMSI= IMSI=001010
000000002 [0x7f01d4001b70]
2015-10-20_14:18:34.131317 <nib:INFO> Got user.register for imsi='001010000000002', tmsi=''
2015-10-20_14:18:34.131425 <ybts:ALL> Location updating thread for (0x7f01d4002b10) TMSI= IMSI=00101000000000
2 terminated [0x7f01d4001b70]
2015-10-20_14:18:34.131439 <ybts-mm:ALL> UE (0x7f01d4002b10) TMSI= IMSI=001010000000002 register failed [0xcd
0620]
2015-10-20_14:18:34.131469 <ybts-signalling:INFO> Sending [0xcd0240]
-----
Primitive: L3Message
Info: 0
Connection: 3

<MM>
  <Message type="LocationUpdatingReject">
    <RejectCause>location-area-not-allowed</RejectCause>
  </Message>
</MM>
-----
2015-10-20_14:18:34.131520 <ybts-signalling:ALL> Releasing connection (0x7f01d4001330,3) [0xcd0240]
2015-10-20_14:18:34.131530 <ybts-signalling:INFO> Sending [0xcd0240]
-----
Primitive: ConnRelease
Info: 0
Connection: 3
-----

Setting Wildcard Subscriber (IMSI Catcher Mode)

  • Edit /usr/local/etc/yate/subscribers.conf and set the following:
      * country_code=1 ; 1 for US, 44 for UK
      * regexp=.* ; catch all the things!

  • Start yate

$ yate -v
  • Verify registered devices using telnet interface
$ telnet 127.0.0.1 5038
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
YATE 5.5.1-devel1 r6056 (http://YATE.null.ro) ready on localhost.

nib list registered
IMSI            MSISDN
--------------- ---------------
001010000000002   10000002

SIM card setup

  • Install the pre-requisite packages
$ sudo apt-get install python-setuptools swig python-dev libpcsclite-dev pcsc-tools
  • Run pcsc_scan, then plug the device in, and insert a SIM card
$ pcsc_scan
PC/SC device scanner
V 1.4.23 (c) 2001-2011, Ludovic Rousseau <ludovic.rousseau@free.fr>
Compiled with PC/SC lite version: 1.8.11
Using reader plug'n play mechanism
Scanning present readers...
Waiting for the first reader...
Scanning present readers...
0: MSI StarReader SMART [Smart Card Reader Interface] (20070818000000000) 00 00
Tue Oct 20 19:26:25 2015
Reader 0: MSI StarReader SMART [Smart Card Reader Interface] (20070818000000000) 00 00
  • Download and compile pyscard
$ wget 'http://downloads.sourceforge.net/project/pyscard/pyscard/pyscard%201.7.0/pyscard-1.7.0.tar.gz?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fpyscard%2F%3Fsource%3Dtyp_redirect&ts=1445382598&use_mirror=iweb'
$ tar zxvf pyscard-1.9.0.tar.gz
$ cd pyscard-1.9.0
$ sudo python setup.py build_ext install
  • Clone the pySim repository
$ git clone git://git.osmocom.org/pysim pysim

Setting up SIP

  • Install Asterisk
$ sudo apt-get install asterisk asterisk-core-sounds-en* asterisk-moh-opsound-* asterisk-mp3 asterisk-mysql mysql-server postgresql postgresql-cont
rib asterisk-voicemail asterisk-doc libmyodbc oidentd
  • Install Kamailio SIP proxy software
$ sudo apt-get install kamailio kamailio-geoip-modules
  • Acquire a SIP account with a provider, ideally with a DID
      * I used voip.ms, and created 2 DIDs, both routed to a single SIP URI (the main account I created when registering)
      * Make sure you set up e911 registration so if anyone on your tower dials 911 the call gets routed and 911 data populated.