Natalie's Nonsense Nook

Adventures in Cell

Published on

“What if we made our own FirstNet but worse?”

“Ooh, we should call it LastNet!”

This is about how the conversation went when my friends and I decided to set up our own cell network for fun. Since I recently broke my leg, I have a lot of time in bed to work on projects, so I figured now is as good a time as any to figure this out. We decided on using CBRS as it’s license-free* and relatively well supported. Overall the setup was not too hard, and should be fairly similar for even big boy Nokia base stations. I pieced together a few different guides from the internet, this is more or less going through the steps I took to set the whole system up, meant to serve as a living and changing playbook and guide for how I manage my demo LTE network. Thus, it may change often. When that happens, the changelog at the bottom will be updated.

* I am aware no wireless service is truly “license-free”, as in many cases the licensing process is handled by someone else, e.g. WiFi or cheap walkie-talkies. The operator does not need to apply for a license, rather the manufacturer obtains it and includes the license with the product. I am aware of the semantics but more people understand “license free” than “well actually…” so don’t come at me plz

Prerequisites:

  • eNB / CBRS base station (Baicells, Sercomm/FreedomFi)
  • Linux server
  • Blank SIM cards and a smartcard reader (any generic one will do)
  • Google Cloud account (for SAS)
  • A phone that supports band 48

eNB (base station)

So to get started the first thing I did was order my base station. I picked a FreedomFi One unit, which is OEM by Sercomm. FreedomFi is now defunct, so they are effectively worthless except to strange nerds who can reconfigure them. The only downside is that the web UI comes disabled by default - so we need to open it. Doing this is actually trivial. The units support standard TR-069 provisioning, so we can spoof a head-end to open the web UI.

I cloned https://github.com/xddxdd/freedomfi-cbrs-enable-webui/ and ran the script on my workstation. This is the spoofed head-end allowing the web UI to be enabled. Then, we just need to DNS spoof the existing acs.freedomfi.com domain to return our workstation’s IP. The easiest way I’ve found to handle this is to use DHCP to hand out the IP of my workstation running Bind9, but the specific implementation I will leave up to the reader.

Once the unit is powered, it should start attempting to phone home. If you see XML scrolling through the terminal, it’s working - the web UI should be open.

TR-069

We can repurpose TR-069 to provision these in a fleet, similar to VoIP phones. I am working on a simple framework to handle provisioning and status of these endpoints.

SAS setup

TBD

Open5GS Install + Configuration

TBD

SIM flashing

In order to connect to a cell network, you need a SIM card. SIM cards are not magic, contrary to popular belief - they are actually versions of smart cards for e.g. signing into secure workstations (think CAC/PIV card or similar). In fact the same interface is used by most chip credit cards. In order to allow a phone onto the network, we need to provision a SIM card that contains the network info so that the end user device can connect.

Not All SIMs are Equal

SIM cards come in all shapes and sizes. Many of the generic kits on Amazon or Aliexpress will work at a base level but won’t support more advanced features like VoLTE or eUICC. For that, you want some of the fancy Sysmocom SIM cards.

Printing on a SIM

Most SIM cards come in a plastic carrier that allows them to be slotted into a standard smartcard reader for programming, then snapped out for usage in a phone. Conveniently, this is the same size card that can be printed with a commodity badge printer. However most SIM cards are textured and not completely flat or glossy like most ID card blanks are. Thus, the print quality is pretty bad when printed on a normal thermal printer (ribbon or retransfer). I suspect sublimation would help here, and I’ve also read that low speed and high heat on a retransfer printer help. I’ve yet to try these though.

eSIM Support

I haven’t seen any info regarding the use of eSIM (technically eUICC) with a private or small-scale LTE network. It looks like this is mainly because in order to “host” an eSIM service, you need a certificate from GSMA or one if its affiliates. Pricing on this is unclear.

Final Routing

So by now your phone should be connected to the base station, and you should get a signal indicator on your phone. However your internet traffic won’t be able to get anywhere. We need to fix this. The official guides don’t mention changing the subnetting nor how to have another router handle NAT, so I’ll go through that here as well.

First we need to identify what v4 and v6 subnets we want to use here. I picked a /56 of v6 from my assignment, and a /22 of internal v4 (RFC1918) that can come from anywhere. Really these subnet sizes don’t matter, you’ll want to size them off of what IP space you have available and how many user devices you want to support. In this example I am using 10.135.0.0/22.

First you’ll want to edit the smf.yaml and upf.yaml config files. Find the subnet/gateway blocks and adjust it to match the IP blocks you picked earlier. For example:

  session:
    - subnet: 10.135.0.0/22
      gateway: 10.135.0.1

This will now assign an IP from this block to the end-user device. But, this won’t be able to route anywhere. We need a few more steps to make that happen. First enable IP forwarding in sysconfig. Create the file /etc/sysctl.d/90-open5gs.conf:

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Now we need to set the tun interface address to match our new user network. Edit /etc/systemd/network/99-open5gs.network, and adjust the subnets to match your configured user network.

Next enable IP forwarding for the user IP blocks:

sudo iptables -A FORWARD -i ogstun -o osgtun -s 10.135.0.0/22 -d 0.0.0.0/0 -j ACCEPT

This will forward any traffic from the user network needing to get to the internet to the core’s default router.

Reboot the core VM.

Static Routing

While these next steps vary depending on your router manufacturer, they should all be fairly similar and portable in nature. We need to define two pathways: out of the user network, and into it. Out of it mostly exists already since we configured that on the core VM, however we will need to add some outbound NAT rules to allow the private range internet access. In my case, I was able to create an outbound nat rule from 10.135.0.0/22 to 0.0.0.0/0 via my WAN interface utilizing an IP I had chosen just for the core. Now the traffic can get out to the internet, and back to the router, but the router won’t know how to get that traffic back to the core and thus the phones. To remedy this we will just need to add a static route in the router for the user network - again, in my case I created a static route for 10.135.0.0/22 via the internal IP of my core VM. Once the config was applied, I was able to get internet access on my test phone.

BGP Routing

I quickly decided to use BGP to handle my UPF routes, as managing a whole host of static routes is not sustainable. As the core VM is serving as the gateway between the LTE RAN and wider internet, it only makes sense for it to speak BGP like any other router. In order to do this I installed the bird routing package for Linux. This will allow the core VM to speak BGP (or OSPF etc). Once installed, we just need to configure bird to advertise the local RAN routes over to my border router. My bird.conf file looks like this (v6 sections omitted for brevity):

protocol direct direct_v4 {
        ipv4 {
                export all;
        };
        interface "ogst*";
}

protocol bgp uplink_v4 {
       description "BGP Uplink v4";
       local 1.2.3.4 as 12345;
       neighbor 1.2.3.4 as 12345;
       hold time 90;

       ipv4 {
               import all;
               export all;
       };
}

I filtered the direct protocol to any ogstun devices, thereby excluding the local network(s) the VM resides on.

Then I had to create the neighbor configuration on the border router and turn up the BGP sessions. At this point, the BGP sessions are now up:

bird> sho proto all uplink_v4
Name       Proto      Table      State  Since         Info
uplink_v4  BGP        ---        up     2025-09-19    Established   
  Description:        BGP Uplink v4
  Created:            2025-09-19
  BGP state:          Established
[...]

From here the configuration at the border router is equivalent to static routing - CGNAT for v4 and rules for v6 are set up the same as any other way.

Word of Warning

While testing I discovered that opnsense really does NOT like it when you give it a full table. I was unable to delete the static routes I had created earlier, but the gateway was changed to a different IP, meaning my traffic was effectively getting blackholed. I manually reloaded routing from the CLI, but this made it worse, and I had to reboot the router to make it pass traffic again.

Changelog

Date Updates
2025-09-20 Initial rev