Using an Raspberry-Pi as UMTS/LTE gateway

06 May 2020 - tsp
Last update 29 May 2022
Reading time 19 mins

Introduction

Since a friend required this - this is a short write-up on how to use your RaspberryPi (with FreeBSD) with an UMTS/LTE modem to provide internet connectivity to your local network. Note that I still do not believe that it’s a good idea to use mobile networks as any way of internet connectivity (to be fair I don’t see them as being internet connections in most cases at all). But there might be reasons to do this - for example if you’re really running a mobile node (If you’re running a system at a fixed location you should really use a fixed landline - no matter what medium - instead of a radio based system).

First off. This is of course an do-it-yourself solution and there are plenty of buy-able ready to use devices out there. The main advantage of doing such things for by oneself is the flexibility one gains. One can run arbitrary software on the RaspberryPi (as long as it compiles on the OS that’s going to be used), one can do fancy network configurations, etc. And one can keep it up to date by oneself.

I’m going to use FreeBSD as the operating system even on the RaspberryPi. I’m choosing this over Linux mainly because of personal preference - I like the long term consistency, the development approach and the adherence to Unix specifications when implementing base commands. And I like the simplicity of FreeBSD compared with other platforms - even on the RaspberryPi where the boot process is done using U-Boot.

Required hardware

Note: Links provided are Amazon affiliate links. Qualified purchases will lead to some profit for the author of this page (it doesn’t change the price for you).

Installing FreeBSD on your SD-Card

First one has to install the operating system. One can download the latest FreeBSD release. One can locate the release on the FreeBSD download page. Be sure to use either RPI-B, RPI2 or RPI3 depending on the hardware you’re using.

First simply download the image. It comes as an .xz archive that has to be uncompressed first. On FreeBSD you can do this with

unxz IMAGE.xz

for the 12.1 Release one could for example simply do

fetch https://download.freebsd.org/ftp/releases/arm64/aarch64/ISO-IMAGES/12.1/FreeBSD-12.1-RELEASE-arm64-aarch64-RPI3.img.xz
unxz FreeBSD-12.1-RELEASE-arm64-aarch64-RPI3.img.xz

On Windows one might use use a tool like 7-Zip. On Mac OS X one also has the unxz command (or xz -d IMAGE.xz) command available.

After that one has to flash the image onto the SD card. On most Unices this is done using dd. This might require root privileges since raw disk access (i.e. use sudo) is required because dd destroys potential file system data on the SD card. Be sure to write to the correct disk. On Unices you can look into dmesg output, insert the SD card and immediately look at dmesg again. You’ll see the device name of the newly attached device (for example adaN or similar). Now one can use dd to write the image to the disk:

dd if=IMAGE.img of=/dev/adaN

for the previously mentioned image that would be

dd if=FreeBSD-12.1-RELEASE-arm64-aarch64-RPI3.img of=/dev/adaN

On Windows a tool like Win32DiskImager might be used to write onto the specific SD card. Be sure to select the right one again!

On any OS - beware that if you write onto the wrong disk there is no way of recovering your data!

Now you can insert the SD card into your RasberryPi and do the remaining configuration either with a local monitor and keyboard or via a (trusted) network. If you initially boot your RaspberryPi using FreeBSD as operating system it will use DHCP to get an IP address if this is supported on your network and start sshd to allow access using either

The default root password (who is not allowed to login via SSH) is normally set to root

Operating system configuration

Passwords

If you do setup via network please set all passwords to non default values using the passwd command first (this is also a good idea when setting up without a network connection). Once for the user you log in with:

$ passwd

And once for root after you’ve elevated into the root account:

$ su
$ passwd
$ exit

Setting current date and time

The most simple way of setting date and time is using the network time protocol. This step is required as soon as some data is downloaded from the network and signatures or an revision control system is used. If your system has been attached to an ethernet based network configured via DHCP you can directly use:

ntpdate pool.ntp.org

If note one can set the current date and time using date (note that the raspberry will loose this information on every reboot since it doesn’t contain a RTC. It’s possible of attaching one but as long as you’re using it as internet gateway one can also use ntpdate automatically on boot and then it’s a good idea to track the current time using ntpd anyways)

To use the NTP daemon one has to edit /etc/ntpd.conf. A typical minimal configuration would look like the following:

tos minclock 3 maxclock 6

server 0.at.pool.ntp.org
server 1.at.pool.ntp.org
server 2.at.pool.ntp.org
server 3.at.pool.ntp.org

restrict default limited kod nomodify notrap noquery nopeer
restrict source  limited kod nomodify notrap noquery

restrict 127.0.0.1
restrict ::1

leapfile "/var/db/ntpd.leap-seconds.list"

After that one can launch ntpd using

/etc/rc.d/ntpd onestart

One can also make that change permanent by adding to /etc/rc.conf:

ntpd_enable="YES"

Note that ntpd only adjusts the time if the offset is small enough (i.e. it only compensates clock drift). To run ntpdate on bootup after networks have been configured one can also add

ntpdate_enable="YES"

to ones rc.conf. This should be done on RaspberryPi without an external realtime-clock since ntpd won’t adjust the initial time due to too large time differences. Note that this requires one to have an network connection up and running already.

If one wants to set the date and time during normal operation using ntpdate one has to stop a running ntpd:

/etc/rc.d/ntpd stop
ntpdate
/etc/rc.d/ntpd start

Also note that that ntpd is loading a kernel module to deploy a mandatory access control module that allows ntpd to set the date and time without running with root privileges at any time.

Connecting to networks

Now it’s a good idea to connect yourself to a network. How you do this depends on your current networking situation. If you have a wired network with internet connectivity available it might be convenient to use the existing internet connection to perform the setup steps. If you’ve connected your PaspberryPi to your network during bootup and your network performs auto configuration it should already be configured using DHCP (IPv4, DNS servers) and SLAAC (IPv6). If not we should first setup the required LTE connection. In already connected cases you can of course do this later so that you don’t have to mess around with default routes to perform setup via your faster landline.

LTE Connection

Preparing your USB dongle

Some USB modems like the one I’m using are announcing themselves as USB CD devices to allow “easy” installation of drivers on some operating systems and are required to be reinitialized so that they can be used as a modem permanently. This can be done using the usb_modeswitch utility. To install that utility one has to have some kind of network connection before doing the UMTS/LTE setup. Then one can install the tool using

$ su
$ pkg install sysutils/usb_modeswitch

After installation and attachment of the modem one can use usb_modeswitch to permanently force the device into modem mode instead of virtual CD-ROM mode - there is also the possibility of doing this on every attachment by adding usb_modeswitch into devd or devfs rules but I won’t cover this here.

To switch the device into modem mode for Huawei modems temporarily one can use

$ usb_modeswitch -v 12d1 -p 1c0b -M '55534243123456780000000000000011062000000100000000000000000000'

Note that the product and vendor ID might be different. One can list all attached usb devices using usbconfig

Instead of using some cryptic command one can also look into /usr/local/share/usb_modeswitch/ using ls -l - if there is a matching vendor and product ID for one’s own USB modem one can simply use

$ usb_modeswitch -v 12d1 -p 1c0b -c /usr/local/share/usb_modeswitch/12d1:1c0b

to permanently disable virtual CD mode.

On my systems I’m currently using a short rc.init script - i.e. a file in /usr/local/etc/rc.d/usbmodeswitch that performs the modeswitch on every boot:

#!/bin/sh

# PROVIDE: usbmodeswitch
# REQUIRE: FILESYSTEMS
# BEFORE: ppp

# usb modeswitch script for Huawei modem
# with sysutils/usb_modeswitch

. /etc/rc.subr

name="usbmodeswitch"
start_cmd="usbmodeswitch_start"
stop_cmd=":"
rcvar="usbmodeswitch_enable"

usbmodeswitch_start()
{
        /usr/local/sbin/usb_modeswitch -v 0x12d1 -p 0x1f01 -V 0x12d1 -P 0x14dc -M "55534243123456780000000000000011063000000100010000000000000000"
        /bin/sleep 10
}

load_rc_config $name
run_rc_command "$@"

This is enabled by the following /etc/rc.conf entry:

usbmodeswitch_eable="YES"
Setting up PPP

To setup the LTE connection one has to configure pppd. This daemon is capable of connecting to various PPP services (not through PPTP for DSL Modems, this can be done using mpd5 as described in one of my previous articles).

Note that you have to do most modifications with root privileges so first elevate yourself to root:

$ su

First one has to determine which device one is going to use. All devices that are used to communicate with modems (any many more) will be listed in the device filesystem at /dev/. To list the currently available files one can use

ls /dev/

Since we know that our device will be emulating an UART device via USB we can look only at cuaU devices.

ls /dev/cuaU*

Most of the USB 3G/4G/5G modems expose multiple serial interfaces for different functionality so one might have to do some experimentation. In my case it’s the /dev/cuaU0.0 device that is going to be used (and my modem also exposes the interfaces cuaU0.1 and cuaU0.2 for short message system and voice communications). Normally these files should already belong to the uucp user and dialer group with read and write permissions for user and group.

The configuration of pppd happens primarily via /etc/ppp/ppp.conf. It’s segmented into various profiles. To edit the file one can use one of the many editors available (vi for more advanced users, ee might be a better choice for beginners).

Then you can simply use the editor to edit PPP configuration:

$ ee /etc/ppp/ppp.conf

On most setups I’m currently using a default configuration like the following:

default:
	set log Phase Chat Connect LCP IPCP CCP command Warning Error Alert tun
	ident user-ppp VERSION

Without going into to much default this configures the steps taken during buildup of a PPP session and describes which optional features are supported and should be used. Now there is a single profile for every network provider that one’s going to use. For example I’ve configured one for Drei in Austria using the following settings:

drei:
	disable ipv6cp
	disable mppe lqr deflate deflate24 acfcomp vjcomp pred1 protocomp
	set device /dev/cuaU0.0
	# set speed 921600
	set speed 115200
	set timeout 300
	nat enable yes
	set dial "ABORT BUSY ABORT \"NO\\sCARRIER\" ABORT \"ERROR\" TIMEOUT 10 \
		\"\" \
		AT OK-AT-OK \
		AT+CFUN=1 OK-AT-OK \
		AT+CMEE=2 OK-AT-OK \
		AT+CSQ OK \
		AT+CGDCONT=1,\\\"ip\\\",\\\"drei.at\\\" OK \
		ATD*99***1# CONNECT"
	enable dns
	resolv writable
	set ifaddr 10.0.0.1/0 10.0.0.2/0 255.255.255.255 0.0.0.0
	add default HISADDR

This configuration is rather complex:

After this configuration has been saved one can edit /etc/rc.conf and add appropriate lines:

ppp_profile="drei"
ppp_nat="YES"
ppp_mode="auto"

If one wants to run pppd as root:

ppp_user="root"

After saving rc.conf and leaving the editor one can test the PPP connection using

$ /etc/rc.d/ppp onestart

If one can connect to the outside world (for example trying to ping www.google.com) and everything works one can make the startup of pppd permanent and launch it on boot by adding the following to /etc/rc.conf

ppp_enable="YES"

Now the internet connection is working.

Static ethernet configuration (if needed)

If the gateway box will also be the DHCP server one need to configure the ethernet interface with a static address. This is again done in /etc/rc.conf. To set a static IP address of 10.5.0.1 with an 24 bit netmask (255.255.255.0) one might include:

ifconfig_ue0="inet 10.5.0.1 netmask 255.255.255.0"

If one also has an static IPv6 address one should add this (of course with the correct IPv6 prefix specified):

ipv6_ifconfig_ue0="FFFF:FFFF:FFFF:FFFF::1 prefixlen 64"

Providing DHCP service to the local network (IPv4)

If one wants to use the RaspberryPi also as DHCP server (i.e. the server that distributed IP addresses and configures DNS servers) to the local network one should run isc-dhcpd. This is provided in the net/dhcpd package and can be installed using

$ su
$ pkg install net/dhcpd

The configuration is rather simple and happens in /usr/local/etc/dhcpd.conf

The main options I’m normally setting is:

For example the configuration might look like the following:

option domain-name "example.com";
option domain-name-servers 10.5.0.1;

default-lease-time 1440;
max-lease-time 10080;

ddns-update-style none;
authoritative;

log-facility local7;

subnet 10.5.0.1 netmask 255.255.255.0 {
	range 10.5.0.100 10.5.0.200;
	option routers 10.5.0.1;
	option subnet-mask 255.255.255.0;
	option ntp-servers 86.59.113.124;
	option time-offset 2;
	option broadcast-address 10.5.0.255;
}

As one can see the sample configuration not only configures the range from which DHCP addresses are assigned but also the default gateway (routers) as well as an ntp-server and an time-offset (i.e. Timezone). There are of course many more options one might set - depending on ones own network (automatic configuration for SMTP servers, domain names, etc.).

One can also add static host declarations for hosts that always should get the same address assigned (for example when they’re providing servers). It’s a nice idea to do this using DHCP too so one only has one central configuration store:

host hostwithsamplename {
	hardware ethernet AA:BB:CC:DD:EE:FF:00;
	fixed-address 10.5.0.201;
}

Note that the fixed-address has not to be included in the DHCP range specified above! The hardware-ethernet address is the MAC address of the device that should be assigned that static address. If one’s also running an own DNS zone (might be interesting too) one can also use symbolic names instead of IP addresses for the host configuration - and of course also for NTP servers, etc.

Then one can add automatic launch of dhcpd to /etc/rc.conf:

dhcpd_enable="YES"
dhcpd_flags="-q"
dhcpd_conf="/usr/local/etc/dhcpd.conf"
dhcpd_ifaces="ue0"
dhcpd_withumask="022"
dhcpd_chuser_enable="YES"
dhcpd_withuser="dhcpd"
dhcpd_withgroup="dhcpd"
dhcpd_chroot_enable="YES"
dhcpd_devfs_enable="YES"
dhcd_rootdir="/var/db/dhcpd"

Advanced configuration

See for example

If I get some more time I’ll also add either here or (more likely) as separate posts:

This article is tagged:


Data protection policy

Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)

This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/

Valid HTML 4.01 Strict Powered by FreeBSD IPv6 support