A portable router to use on the go, based on OpenWrt and Raspberry Pi 5.
Network adapters:
- Dell USB-C to RJ45 DBQBCBC064 Gigabit Ethernet Adapter
- Netgear Nighthawk AXE3000 USB 3.0 WiFi Adapter (A8000)
Change Wireless country code and update EEPROM from inside Raspberry Pi OS (see this).
Download OpenWrt FACTORY (SQUASHFS) snapshot image. Flash the image, insert the microSD card into Raspberry Pi and power on.
Double check the output of uci
before running service
or reboot
commands - no errors should be displayed.
Change password for root
after first boot.
=== WARNING! =====================================
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@OpenWrt:/# passwd
Changing password for root
New password:
Retype password:
passwd: password for root changed by root
uci set system.@system[0].hostname='rocinante.lan'
uci set system.@system[0].description='When in Rome'
uci set system.@system[0].zonename='Europe/Bucharest'
uci set system.@system[0].timezone='EET-2EEST,M3.5.0/3,M10.5.0/4'
uci delete system.ntp.server
uci add_list system.ntp.server='0.pool.ntp.org'
uci add_list system.ntp.server='1.pool.ntp.org'
uci add_list system.ntp.server='2.pool.ntp.org'
uci add_list system.ntp.server='3.pool.ntp.org'
uci commit system
service system restart
Create a profile containing router IP and bitmask:
mkdir /etc/profile.d
cat << "EOF" > /etc/profile.d/$(uname -n).sh
alias ss=netstat
export router_lan_ip=172.24.42.65
export router_lan_bitmask=27
export router_dhcp_min=67
export router_dhcp_max=93
eval $(resize) # RS232
EOF
source /etc/profile.d/$(uname -n).sh
Configure LAN and WAN:
uci delete network.lan.netmask
uci set network.lan.ipaddr="${router_lan_ip}/${router_lan_bitmask}"
uci set network.lan.force_link=1
uci set network.wan=interface
uci set network.wan.proto='dhcp'
uci set network.wan.peerdns='0'
uci set network.wan.dns='1.1.1.3 1.0.0.3'
uci commit network
Update DHCP range:
uci set dhcp.lan.start=${router_dhcp_min}
uci set dhcp.lan.limit=${router_dhcp_max}
uci commit dhcp
service network restart
Upstream connection: Wi-Fi 4, 5 GHz AC AP, channel 40, channel width 40 MHz, SSID Tycho Station
.
uci set wireless.radio0.band='5g'
uci delete wireless.radio0.channel
uci set wireless.radio0.htmode='VHT40'
uci set wireless.radio0.disabled='0'
uci set wireless.default_radio0.network='wan'
uci set wireless.default_radio0.mode='sta'
uci set wireless.default_radio0.ssid='Tycho Station'
uci set wireless.default_radio0.encryption='psk2'
uci set wireless.default_radio0.key='"[...], so long as you are on the right side."'
uci commit wireless
wifi
Both phy0-sta0
and br-lan
interfaces should now have IPs assigned from an existing Wi-Fi router (the one broadcasting the ssid
configured above) and from the lan
configuration, respectively.
Test internet connection on the router:
opkg update
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NEE5AAAAIMd7CmaALG3anehAM8" > /etc/dropbear/authorized_keys
uci set dropbear.@dropbear[0].Interface='lan'
uci set dropbear.@dropbear[0].PasswordAuth='off'
uci set dropbear.@dropbear[0].RootPasswordAuth='off'
uci commit dropbear
service dropbear restart
Check configured IP and port:
root@rocinante:/# ss -tln | grep :22
tcp 0 0 172.24.42.65:22 0.0.0.0:* LISTEN
The router will be configured with the internal Wi-Fi in mode sta
- the device providing the internet -, and an external Wi-Fi adapter connected via USB in mode ap
- the device external clients will connect to. It will use the built-in Raspberry Pi ethernet adapter (eth0
) as a WAN connection and delegate its LAN membership to the USB ethernet adapter.
The sta
device configuration will change whenever location changes, the ap
device configuration will remain the same.
opkg update
opkg install usbutils pciutils ip-full grep vim-runtime vim-full git-http jq
The ethernet adapter is connected to the Raspberry Pi via a Delock USB-C to USB-A 60044 adapter.
opkg update
opkg install kmod-usb-net-rtl8152
Insert the ethernet adapter in one of the two USB 3.0 ports of the Raspberry Pi.
Change the LAN bridge br-lan
to include eth1
instead of eth0
:
uci set network.@device[0].ports='eth1'
uci commit network
service network restart
Connect an ethernet cable between a computer and Raspberry Pi via the USB adapter. The router should now be ready for connections over SSH and HTTPS, using its configured IP address (the one specified in the router_lan_ip
environment variable).
This Wi-Fi adapter does not support VAPs (multiple SSIDs).
opkg update
opkg install kmod-mt7921u
Connect the adapter; its details should automatically be added in /etc/config/wireless
.
Configure AP details:
uci set wireless.radio2.band='5g'
uci set wireless.radio2.channel='149'
uci set wireless.radio2.htmode='HE80'
uci set wireless.radio2.disabled='0'
uci set wireless.default_radio2.network='lan'
uci set wireless.default_radio2.mode='ap'
uci set wireless.default_radio2.ssid='Rocinante'
uci set wireless.default_radio2.encryption='sae'
uci set wireless.default_radio2.key='Do 3 what 3 Romans 0 Do 1'
uci commit wireless
wifi
Connect a client device to the ssid
specified above, you should be able to reach the internet.
opkg update
opkg install luci-ssl-nginx
Existing private and public key files:
/etc/nginx/conf.d/_lan.key
/etc/nginx/conf.d/_lan.crt
uci set nginx._lan.uci_manage_ssl='no' # do not allow LuCI to manage certificates
uci set nginx._lan.server_name=$(uname -n)
uci -q delete nginx._lan.listen
uci -q delete nginx._redirect2ssl.listen
uci add_list nginx._lan.listen="${router_lan_ip}:443 ssl"
uci add_list nginx._redirect2ssl.listen="${router_lan_ip}:80"
uci commit nginx
service nginx restart
Check configured IP and ports:
root@rocinante:/# ss -tln | grep -E ':80|:443'
tcp 0 0 172.24.42.65:443 0.0.0.0:* LISTEN
tcp 0 0 172.24.42.65:80 0.0.0.0:* LISTEN
Connect the router to Internet using Ethernet instead of Wi-Fi:
uci set network.wan.device='eth0'
uci commit network
service network restart
Revert to Wi-Fi:
uci delete network.wan.device
uci commit network
service network restart
uci set firewall.@defaults[0].drop_invalid='1'
uci commit firewall
service firewall restart
uci set wireless.radio0.band='5g'
uci set wireless.radio0.channel='149'
uci set wireless.radio0.htmode='VHT80'
uci set wireless.radio0.disabled='0'
uci set wireless.default_radio0.network='lan'
uci set wireless.default_radio0.mode='ap'
uci set wireless.default_radio0.ssid='Rocinante'
uci set wireless.default_radio0.encryption='psk2'
uci set wireless.default_radio0.key='Do 3 what 3 Romans 0 Do 1'
uci set wireless.radio2.band='5g'
uci delete wireless.radio2.channel
uci set wireless.radio2.htmode='VHT40'
uci set wireless.radio2.disabled='0'
uci set wireless.default_radio2.network='wan'
uci set wireless.default_radio2.mode='sta'
uci set wireless.default_radio2.ssid='Tycho Station'
uci set wireless.default_radio2.encryption='psk2'
uci set wireless.default_radio2.key='"[...], so long as you are on the right side."'
uci commit wireless
wifi
Raspberry Pi 5's internal Wi-Fi does not support WPA3
.
When connecting to an upstream Wi-Fi 7 AP on 6 GHz band:
# [...same as above...]
uci set wireless.radio2.band='6g'
uci set wireless.radio2.htmode='HE80'
# [...same as above...]
An alternative to this, which didn't work for me.
cat << "EOF" > /etc/hotplug.d/iface/93-start-nna8k
#!/bin/sh
radio=$(uci get wireless.default_radio2.device)
status=$(wifi status | jq ".${radio}.up")
lock='/tmp/nna8k.lock'
if [ "${status}" = 'false' ] && ! [ -f "${lock}" ]; then
touch "${lock}"
sleep 3
wifi
fi
EOF
*) A workaround for Netgear Nighthawk AXE3000 USB 3.0 WiFi Adapter (A8000) not activating after a reboot.
Install required packages:
opkg update
opkg install cfdisk squashfs-tools-unsquashfs losetup resize2fs
Check DEVICE
and PARTITION
are correct.
DEVICE="/dev/mmcblk0" # SD card
cfdisk "$DEVICE" # resize, then write
PARTITION="${DEVICE}p2" # should be the Linux (83) partition
FS_SIZE="$(unsquashfs -s "$PARTITION" | grep -o 'Filesystem size [0-9]* bytes' | grep -o '[0-9][0-9]*')"
FS_OFFSET="$(expr '(' "$FS_SIZE" + 65535 ')' / 65536 '*' 65536)"
LOOP_DEVICE="$(losetup -f --show -o "$FS_OFFSET" "$PARTITION")"
# fsck, resize, fsck
e2fsck -f $LOOP_DEVICE
# e2fsck 1.47.0 (5-Feb-2023)
# rootfs_data: recovering journal
# rootfs_data primary superblock features different from backup, check forced.
# Pass 1: Checking inodes, blocks, and sizes
# Pass 2: Checking directory structure
# Pass 3: Checking directory connectivity
# /lost+found not found. Create<y>? yes
# Pass 4: Checking reference counts
# Pass 5: Checking group summary information
# Feature orphan_present is set but orphan file is clean.
# Clear<y>? yes
# rootfs_data: ***** FILE SYSTEM WAS MODIFIED *****
# rootfs_data: 880/25376 files (0.5% non-contiguous), 31938/101696 blocks
resize2fs $LOOP_DEVICE
# resize2fs 1.47.0 (5-Feb-2023)
# Resizing the filesystem on /dev/loop1 to 31088448 (1k) blocks.
# The filesystem on /dev/loop1 is now 31088448 (1k) blocks long.
e2fsck -f $LOOP_DEVICE
# e2fsck 1.47.0 (5-Feb-2023)
# rootfs_data: clean, 880/7407840 files, 1888214/31088448 blocks
reboot
**) When using SquashFS only.
cat << "EOF" > /usr/libexec/get-block-list
#!/bin/sh
l1="https://v.firebog.net/hosts/AdguardDNS.txt"
l2="https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareHosts.txt"
hosts_file="/tmp/hosts/adblock.hosts"
interval=$(( 24 * 60 * 60 ))
update='yes'
if [ -f "${hosts_file}" ]; then
file_size=$(du -k ${hosts_file} | awk '{ print $1 }' )
if [ ${file_size} -gt 0 ]; then
last_modified_seconds=$(date -r ${hosts_file} +%s)
seconds_lapsed=$(( $(date +%s) - ${last_modified_seconds} ))
if [ ${seconds_lapsed} -lt ${interval} ]; then
update='no'
fi
fi
fi
if [ "${update}" = 'yes' ]; then
wget -q -O - "${l1}" "${l2}" 2> /dev/null | grep -E '^[^#|^!]' | awk -F' ' '!a[$NF]++ {gsub(/^/,"0.0.0.0 ",$NF) ; print $NF ; gsub(/^0\.0\.0\.0/,"::1",$NF) ; print $NF}' > ${hosts_file} && service dnsmasq restart > /dev/null
fi
exit 0
EOF
chmod 755 /usr/libexec/get-block-list
echo "*/3 * * * * /usr/libexec/get-block-list" | crontab - # will replace crontab
For CLI:
opkg update
opkg install sqm-scripts
Or LuCI:
opkg update
opkg install luci-app-sqm
uci set sqm.eth1.enabled='1'
uci set sqm.eth1.interface='phy0-sta0'
uci set sqm.eth1.download='12000' # kbps
uci set sqm.eth1.upload='12000' # kbps
uci set sqm.eth1.linklayer='ethernet'
uci set sqm.eth1.debug_logging='0'
uci set sqm.eth1.verbosity='0'
uci set sqm.eth1.overhead='44'
uci delete sqm.eth1.qdisc_advanced
uci delete sqm.eth1.ingress_ecn
uci delete sqm.eth1.egress_ecn
uci delete sqm.eth1.qdisc_really_really_advanced
uci delete sqm.eth1.itarget
uci delete sqm.eth1.etarget
uci commit sqm
service sqm restart
VAP support: yes.
kmod-rt2800-lib kmod-rt2800-usb kmod-rt2x00-lib kmod-rt2x00-usb
VAP support: yes.
kmod-ath9k-htc
kmod-usb-net-asix-ax88179
kmod-usb-net-rtl8152