Raspberry Pi LTE router, wwan0 to eth0
Recently there was maintenance in our area by our broadband service provider which meant tethering to our phones to enable us to work from home. Me being me I thought, “I know how I can solve this for the future – build an LTE router with a Raspberry Pi”. What follows is the outcome of a painful two months of tinkering on and off.
1. The kit
First things first, you’ll obviously need a spare Raspberry Pi. If you don’t already have one, good luck with that. Thankfully I had the Pi 4 that I’d built as a Retropi machine that, well, we all know how that goes: build it, set it up, tinker, store it in a drawer.
For the LTE modem, I initially bought the Waveshare SIM7600 USB dongle, for reasons unknown. After getting severely hacked off with it seemingly recognising the dongle at random, either directly plugged in or using the extension lead, I decided to… double down and buy the hat version. This connected without fail.
2. Basic setup
To set the pie up, I took the opportunity to test out the network install feature that is now part of the Pi bootloader. To sort that out it was nessessary for me to put a basic Lite image on an SD card, update the bootloader and set the boot order to Network Boot
in the Advanced Options
menu of raspi-config
.
With this set up, its the usual dance of updating and upgrading as soon as you log into your Pi for the first time:
:~ $ sudo apt update && sudo apt upgrade -y
Then a brief soujorn into raspi-config
to set up the serial interace:
1 Interface Options > I6 Serial Port > Login shell accessible over SSH: No > Serial port hardware enabled: Yes
Once done I moved on to setting up the folder and file for the public SSH key:
:~ $ mkdir ~/.ssh
:~ $ touch ~/.ssh/authorized_keys
:~ $ chmod 700 ~/.ssh
:~ $ chmod 600 ~/.ssh/authorized_keys
:~ $ ls -lna ~/.ssh
drwx------ .
-rw------- authorized_keys
After adding my public key to the authorized_keys
file, I edited the /etc/ssh/sshd_config
file – probably overkill in this instance, though good practice all the same:
Port $[ssh-port-number]
AddressFamily inet
[…]
LoginGraceTime 20
PermitRootLogin no
[…]
MaxAuthTries 3
[…]
PubkeyAuthentication yes
[…]
PasswordAuthentication no
PermitEmptyPasswords no
[…]
ChallengeResponseAuthentication no
[…]
KerberosAuthentication no
[…]
GSSAPIAuthentication no
[…]
AllowAgentForwarding no
[…]
X11Forwarding no
[…]
PermitUserEnvironment no
[…]
PermitTunnel no
Although some guides recommend it, setting AllowTcpForwarding
to no
will mean you can’t connect via Visual Studio Code.
Once configured, the service needed to be restarted:
:~ $ sudo systemctl restart sshd
Ordinarily at this point I would have installed then configured UFW and Fail2Ban however, as most mobile carriers take care of firewalls, this was unnecessary.
The last step was disabling and uninstalling ModemManager
which will by default try to manage the LTE modem:
:~ $ sudo systemctl unmask ModemManager.service # may not need this
:~ $ sudo systemctl disable ModemManager.service
:~ $ sudo apt remove modemmanager -y
3. Setting up the LTE modem
Getting the LTE modem to be recognised by the Pi is relatively hard work and thankfully the lovely people of the Raspberry Pi forums have solved this. In combination with a great article by Jeff Geerling, I was able to get the LTE interface up and running fairly painlessly, on boot.
There were three checks to do to make sure the modem was being seen by the Pi. The first was to check that it appeared as a USB device:
:~ $ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 1e0e:9001 Qualcomm / Option SimTech, Incorporated
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
The second was to check that it was appearing as a CDC device, cdc-wdm0
:
:~ $ ls /dev/cdc*
/dev/cdc-wdm0
The last check is to make sure it was appearing as a network interface, at wwan0
:
:~ $ ip a
[…]
4: wwan0: […]
[…]
With the checks complete, I could move on to installing the necessary software:
:~ $ sudo apt install libqmi-utils udhcpc -y
Once done, I quickly checked to make sure the modem was online:
:~ $ sudo qmicli -d /dev/cdc-wdm0 --dms-set-operating-mode='online'
After this, it was necessary to set the modem’s protocol to raw_ip
:
:~ $ sudo ip link set wwan0 down
:~ $ echo 'Y' | sudo tee /sys/class/net/wwan0/qmi/raw_ip
:~ $ sudo ip link set wwan0 up
If you need to confirm the setting, you can run the following:
:~ $ sudo qmicli -d /dev/cdc-wdm0 --wda-get-data-format
[…]
Link layer protocol: 'raw-ip'
[…]
Most guides now recommend you run a commnand to set the APN, username and password. For some reason the GiffGaff sim I was using did not require this. What was required was actually starting the device:
:~ $ sudo qmi-network /dev/cdc-wdm0 start
This brought me to the first moment of truth – trying to connect the device to the GiffGaff network. Running the following gave the LTE modem it’s default IP and route:
:~ $ sudo udhcpc -q -f -i wwan0
If everything was successful, running ip a
again should display an IP address for wwan0
, probably in the private 10.x.x.x
range.
The connection was then tested by pinging Google:
:~ $ ping -I wwan0 www.google.com -c 3
[…]
--- www.google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 31.199/46.540/58.827/8.962 ms
As all of the above modem config is necessary after a reboot, the easiest way to have it come online automatically was to set up a config file in /etc/network/interfaces.d
. From a lot (a lot) of reading, I gather that this is one of about 25 million ways of configuring networks on Linux, and not necessarily the most recent one. But whatever, it worked for me:
:~ $ sudo nano /etc/network/interfaces.d/wwan0
auto wwan0
iface wwan0 inet manual
pre-up ifconfig wwan0 down
pre-up echo Y > /sys/class/net/wwan0/qmi/raw_ip
pre-up for _ in $(seq 1 10); do /usr/bin/test -c /dev/cdc-wdm0 && break; /bin/sleep 1; done
pre-up for _ in $(seq 1 10); do /usr/bin/qmicli -d /dev/cdc-wdm0 --nas-get-signal-strength && break; /bin/sleep 1; done
pre-up /usr/bin/qmi-network /dev/cdc-wdm0 start
pre-up udhcpc -i wwan0
post-down /usr/bin/qmi-network /dev/cdc-wdm0 stop
On rebooting, the LTE modem worked successfully, getting an IP address, and allowing pings to Google. This worked flawlessly for the hat, but this was always the stage where the dongle would randomly crap out – I never did figure out why.
4. Sharing the LTE connection with the ethernet port
This is where it got complicated, for two reasons. Firstly most guides are old, for pre-Buster versions of RaspberryPi OS. Secondly, most guides assume you want to share the Wifi connection, wlan
, with the ethernet port, eth0
, not wwan0
to eth0
as I was trying to do. Simply replacing wlan
with wwan
didn’t seem to work when I tried.
Thankfully I stumbled across a StackExchange post with a link to a script on Github that did work.
The first thing to do is install dnsmasq
:
:~ $ sudo apt-get install dnsmasq
Having already configured the wwan0
interface, I just needed to configure the wlan
and eth0
interfaces. The reason for configuring wlan
was because I later went rogue and turned off dhcpcd
as I was doing everything through /etc/network/interfaces.d/
.
For the wlan0
interface:
:~ $ sudo nano /etc/network/interfaces.d/wlan0
allow-hotplug wlan0
iface wlan0 inet static
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
metric 0
address $[ip-address]
netmask $[subnet]
network $[ip]
router $[router-ip-address]
Then for the eth0
interface:
:~ $ sudo nano /etc/network/interfaces.d/eth0
auto eth0
iface eth0 inet static
metric 200
address 192.168.2.1
netmask 255.255.255.0
network 192.168.2.0
broadcast 192.168.2.255
It was also necessary to enable packet forwarding, by uncommenting net.ipv4.ip_forward=1
:
:~ $ sudo nano /etc/sysctl.conf
[…]
net.ipv4.ip_forward=1
[…]
Downloading the script and making it executable with chmod +x
was the last step – I placed the script in my home directory, updated the variables to use capitals, and the commands in it to use ip
instead of ifconfig
:
NETMASK="24"
[…]
sudo ip link set dev $ETH down
sudo ip link set dev $ETH up
sudo ip addr add $IP_ADDRESS/$NETMASK dev $
With all of that done, it was time to run the script and plug the laptop into the Pi ethernet port. Nervously I turned Wifi off, and… success – I was able to browse the web via the LTE connection on my Pi. Speeds were not great, but usable, though turning off iCloud Private Relay seemed to improve them. The big test will be plugging this into my network setup – we’ll see how that goes.