Skip to content

Instantly share code, notes, and snippets.

@fbraz3
Last active July 7, 2025 17:39
Show Gist options
  • Save fbraz3/3db625adf81e2ea335968d933e6d61b1 to your computer and use it in GitHub Desktop.
Save fbraz3/3db625adf81e2ea335968d933e6d61b1 to your computer and use it in GitHub Desktop.
[OpenWRT] Shell Script to Create a Fully Isolated Guest Network with Bandwidth Control
#!/bin/sh
#
# FOR USE IN OPENWRT
# This script creates a guest network fully isolated from the main one.
# Tested on a Xiaomi AX3000T router; should work on any OpenWRT-powered router.
#
# - Ensure the Wi-Fi interfaces retain their default names (radio0 and radio1).
# - For enable download/upload limits, you MUST install the sqm-scripts package on your OpenWRT router.
# - For enable roaming (aka wifi mesh):
# - You must install the xxd package on your OpenWRT router.
# - Ensure the wireless interface supports 802.11r and 802.11k.
# - All router must have the same roaming domain
#
# Info about OpenWRT support on AX3000T:
# https://openwrt.org/inbox/toh/xiaomi/ax3000t
#
# Guest interface configuration
GUEST_INTERFACE='guest'
GUEST_NETWORK='192.168.6.1/24'
GUEST_DNS_SERVER='8.8.8.8,8.8.4.4' # DNS server for the guest network, split by commas if multiple (e.g. 8.8.8.8,8.8.4.4)
# The interface which serves as the internet gateway for the guest network; it could be your WAN or LAN interface depending on your setup.
DEST_INTERFACE='wan' # Change this to 'lan' if you want the guest network to access the local LAN.
DEST_RANGE='192.168.1.1/24' # The range of the destination interface, its needed for the firewall rules to isolate the guest network.
# Wi-Fi SSID and Password
RADIO_SSID='my-guest-wifi' # Choose your Wi-Fi network name here
RADIO_KEY='my-guest-password' # BE SURE TO CHANGE THE PASSWORD!!!
RADIO1_ENABLE=1 # Set to 1 to enable radio1; comment out to use only the radio0 interface.
# Reminder: In order to use bandwidth control, you must install the sqm-scripts package on your OpenWRT router.
SQM_ENABLE=1 # Set to 1 to enable SQM (Smart Queue Management) for bandwidth control; comment out to disable.
SQM_DOWNLOAD_LIMIT='10000' # Download limit in Kbps, e.g. 10000 for 10 Mbps
SQM_UPLOAD_LIMIT='10000' # Upload limit in Kbps, e.g. 10000 for 10 Mbps
# If you want to open ports on your WAN/LAN from the guest network, just add them below
ALLOW_PORTS='53,tcp 53,udp' # Allow DNS port, change or comment out if you don't want to allow requests from the guest network.
# Reminder: you must install the xxd package and all routers must have the same roaming domain.
ROAMING_ENABLE=1 # Set to 1 to enable roaming (aka wifi mesh); comment out to disable.
ROAMING_DOMAIN='a1c2' # MUST be a 4 character hexadecimal string, e.g. 'abcd'.
#
# Configuration below this line should not require modification.
#
# Define radio interfaces
RADIO0_IFACE='radio0' # radio0 is the default interface for the 2.4GHz band
RADIO1_IFACE='radio1' # radio1 is the default interface for the 5GHz band
# Configure network bridge
uci -q delete network.${GUEST_INTERFACE}_dev
uci set network.${GUEST_INTERFACE}_dev='device'
uci set network.${GUEST_INTERFACE}_dev.type='bridge'
uci set network.${GUEST_INTERFACE}_dev.name="br-${GUEST_INTERFACE}"
# Configure network interface
uci -q delete network.${GUEST_INTERFACE}
uci set network.${GUEST_INTERFACE}='interface'
uci set network.${GUEST_INTERFACE}.proto='static'
uci set network.${GUEST_INTERFACE}.device="br-${GUEST_INTERFACE}"
uci set network.${GUEST_INTERFACE}.ipaddr="${GUEST_NETWORK}"
uci commit network
service network restart
# Configure dhcp server
uci -q delete dhcp.${GUEST_INTERFACE}
uci set dhcp.${GUEST_INTERFACE}='dhcp'
uci set dhcp.${GUEST_INTERFACE}.interface="${GUEST_INTERFACE}"
uci set dhcp.${GUEST_INTERFACE}.start='100'
uci set dhcp.${GUEST_INTERFACE}.limit='150'
uci set dhcp.${GUEST_INTERFACE}.leasetime='1h'
uci add_list dhcp.${GUEST_INTERFACE}.dhcp_option="6,${GUEST_DNS_SERVER}"
uci commit dhcp
service dnsmasq restart
# Add firewall group
uci -q delete firewall.${GUEST_INTERFACE}
uci set firewall.${GUEST_INTERFACE}='zone'
uci set firewall.${GUEST_INTERFACE}.name="${GUEST_INTERFACE}"
uci set firewall.${GUEST_INTERFACE}.network="${GUEST_INTERFACE}"
uci set firewall.${GUEST_INTERFACE}.input='REJECT'
uci set firewall.${GUEST_INTERFACE}.output='ACCEPT'
uci set firewall.${GUEST_INTERFACE}.forward='REJECT'
if [ ! -z "${ALLOW_PORTS}" ]; then
for ITEM in $ALLOW_PORTS; do
PORT=$(echo $ITEM | cut -d',' -f1)
PROTO=$(echo $ITEM | cut -d',' -f2)
[ "$PROTO" != "tcp" ] && [ "$PROTO" != "udp" ] && PROTO="tcp"
RULE_NAME="${GUEST_INTERFACE}_open_${PORT}_${PROTO}"
uci -q delete firewall.${RULE_NAME}
uci set firewall.${RULE_NAME}='rule'
uci set firewall.${RULE_NAME}.name="${RULE_NAME}"
uci set firewall.${RULE_NAME}.src="${GUEST_INTERFACE}"
uci set firewall.${RULE_NAME}.dest="${DEST_INTERFACE}"
uci set firewall.${RULE_NAME}.dest_port="${PORT}"
uci set firewall.${RULE_NAME}.proto="${PROTO}"
uci set firewall.${RULE_NAME}.target='ACCEPT'
done
fi
# FW Rule: Allow DHCP
uci -q delete firewall.${GUEST_INTERFACE}_dhcp
uci set firewall.${GUEST_INTERFACE}_dhcp='rule'
uci set firewall.${GUEST_INTERFACE}_dhcp.name="${GUEST_INTERFACE}_dhcp"
uci set firewall.${GUEST_INTERFACE}_dhcp.src="${GUEST_INTERFACE}"
uci set firewall.${GUEST_INTERFACE}_dhcp.dest_port='67-68'
uci set firewall.${GUEST_INTERFACE}_dhcp.target='ACCEPT'
uci set firewall.${GUEST_INTERFACE}_dhcp.proto='tcp udp'
# FW Rule: Isolate network from local LAN
uci -q delete firewall.${GUEST_INTERFACE}_iso
uci set firewall.${GUEST_INTERFACE}_iso='rule'
uci set firewall.${GUEST_INTERFACE}_iso.name="${GUEST_INTERFACE}_iso"
uci set firewall.${GUEST_INTERFACE}_iso.src="${GUEST_INTERFACE}"
uci set firewall.${GUEST_INTERFACE}_iso.dest="${DEST_INTERFACE}"
uci set firewall.${GUEST_INTERFACE}_iso.dest_ip="${DEST_RANGE}"
uci set firewall.${GUEST_INTERFACE}_iso.target='REJECT'
# Add forwarding to lan
uci -q delete firewall.${GUEST_INTERFACE}_forward
uci set firewall.${GUEST_INTERFACE}_forward='forwarding'
uci set firewall.${GUEST_INTERFACE}_forward.src="${GUEST_INTERFACE}"
uci set firewall.${GUEST_INTERFACE}_forward.dest="${DEST_INTERFACE}"
uci commit firewall
service firewall restart
# Create radio0 wifi network
uci -q delete wireless.${GUEST_INTERFACE}
uci set wireless.${GUEST_INTERFACE}=wifi-iface
uci set wireless.${GUEST_INTERFACE}.device="${RADIO0_IFACE}"
uci set wireless.${GUEST_INTERFACE}.mode='ap'
uci set wireless.${GUEST_INTERFACE}.network="${GUEST_INTERFACE}"
uci set wireless.${GUEST_INTERFACE}.ssid="${RADIO_SSID}"
uci set wireless.${GUEST_INTERFACE}.encryption='sae-mixed'
uci set wireless.${GUEST_INTERFACE}.key="${RADIO_KEY}"
uci set wireless.${GUEST_INTERFACE}.isolate='1'
# Create radio1 wifi network
if [ ! -z "${RADIO1_ENABLE}" ]; then
uci -q delete wireless.${GUEST_INTERFACE}_1
uci set wireless.${GUEST_INTERFACE}_1=wifi-iface
uci set wireless.${GUEST_INTERFACE}_1.device="${RADIO1_IFACE}"
uci set wireless.${GUEST_INTERFACE}_1.mode='ap'
uci set wireless.${GUEST_INTERFACE}_1.network="${GUEST_INTERFACE}"
uci set wireless.${GUEST_INTERFACE}_1.ssid="${RADIO_SSID}"
uci set wireless.${GUEST_INTERFACE}_1.encryption='sae-mixed'
uci set wireless.${GUEST_INTERFACE}_1.key="${RADIO_KEY}"
uci set wireless.${GUEST_INTERFACE}_1.isolate='1'
fi
if [ ! -z "${ROAMING_ENABLE}" ]; then
NAS_ID=$(echo "${GUEST_INTERFACE}" | awk '{print toupper($0)}')
NAS_ID="${NAS_ID}_$(head -c 1 /dev/urandom | xxd -p -l 12 | awk '{print toupper($0)}')"
R1_KEY_HOLDER=$(head -c 6 /dev/urandom | xxd -p -l 12)
uci set wireless.${GUEST_INTERFACE}.ieee80211r='1'
uci set wireless.${GUEST_INTERFACE}.nasid="${NAS_ID}"
uci set wireless.${GUEST_INTERFACE}.mobility_domain="${ROAMING_DOMAIN}"
uci set wireless.${GUEST_INTERFACE}.ft_over_ds='0'
uci set wireless.${GUEST_INTERFACE}.r1_key_holder="${R1_KEY_HOLDER}"
uci set wireless.${GUEST_INTERFACE}.pmk_r1_push='1'
uci set wireless.${GUEST_INTERFACE}.ieee80211k='1'
uci set wireless.${GUEST_INTERFACE}.time_advertisement='2'
uci set wireless.${GUEST_INTERFACE}.time_zone='<-03>3'
uci set wireless.${GUEST_INTERFACE}.bss_transition='1'
uci set wireless.${GUEST_INTERFACE}.ocv='0'
if [ ! -z "${RADIO1_ENABLE}" ]; then
NAS_ID="${NAS_ID}_1"
R1_KEY_HOLDER=$(head -c 6 /dev/urandom | xxd -p -l 12)
uci set wireless.${GUEST_INTERFACE}_1.ieee80211r='1'
uci set wireless.${GUEST_INTERFACE}_1.nasid="${NAS_ID}"
uci set wireless.${GUEST_INTERFACE}_1.mobility_domain="${ROAMING_DOMAIN}"
uci set wireless.${GUEST_INTERFACE}_1.ft_over_ds='0'
uci set wireless.${GUEST_INTERFACE}_1.r1_key_holder="${R1_KEY_HOLDER}"
uci set wireless.${GUEST_INTERFACE}_1.pmk_r1_push='1'
uci set wireless.${GUEST_INTERFACE}_1.ieee80211k='1'
uci set wireless.${GUEST_INTERFACE}_1.time_advertisement='2'
uci set wireless.${GUEST_INTERFACE}_1.time_zone='<-03>3'
uci set wireless.${GUEST_INTERFACE}_1.bss_transition='1'
uci set wireless.${GUEST_INTERFACE}_1.ocv='0'
fi
fi
uci commit wireless
wifi reload
# Configure SQM (Smart Queue Management) for bandwidth control
if [ ! -z "${SQM_ENABLE}" ]; then
uci del sqm.${GUEST_INTERFACE}
uci set sqm.${GUEST_INTERFACE}=queue
uci set sqm.${GUEST_INTERFACE}.enabled='1'
uci set sqm.${GUEST_INTERFACE}.interface="br-${GUEST_INTERFACE}"
uci set sqm.${GUEST_INTERFACE}.download="${SQM_UPLOAD_LIMIT}" # upload from the guest network to the internet
uci set sqm.${GUEST_INTERFACE}.upload="${SQM_DOWNLOAD_LIMIT}" # download from internet to the guest network
uci set sqm.${GUEST_INTERFACE}.debug_logging='0'
uci set sqm.${GUEST_INTERFACE}.verbosity='5'
uci set sqm.${GUEST_INTERFACE}.qdisc="cake"
uci set sqm.${GUEST_INTERFACE}.script='piece_of_cake.qos'
uci set sqm.${GUEST_INTERFACE}.linklayer='none'
uci commit sqm
service sqm restart
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment