Rate limited guest WLAN on OpenWrt

Updated: 2020-07-21

I have set up a basic guest access point with limited bandwidth. There's two parts to it: setting up the AP, then setting the limits. This is a quick walkthrough, based on the OpenWrt wiki, to which I have added the instructions for a multi-device setup (ie a network configuration with one router and one or more switches or access points).

Creating a guest access point (single router)

Add a guest interface to /etc/config/network:

config interface     'guest'
  option proto       'static'
  option ipaddr      ''
  option netmask     ''

And one to /etc/config/wireless. Make sure to get the device name right, it may differ between platforms.

config wifi-iface
  option device      'radio0'
  option network     'guest'
  option mode        'ap'
  option ssid        'OpenWrt Guest Network'
  option encryption  'psk2+ccmp'
  option key         'just don't make it too easy?'

Add an entry for the DHCP server to /etc/config/dhcp. Since this is a guest WLAN, you can set a relatively low lease time:

config dhcp 'guest'
  option interface   'guest'
  option start       '2'
  option limit       '10'
  option leasetime   '2h'
  option ignore      '0'

To determine the start number, take into account the number of network devices (routers, switches, access points) requiring a static IP so the DHCP range does not conflict with any static IPs. E.g. for a router + AP setup, you'd want to set your start value to '3'.

Lastly, we implement some extra firewall rules in /etc/config/firewall. First, we block all incoming or forwarding traffic. Only outgoing traffic is allowed.

config zone
  option name         'guest'
  option list         'guest'
  option input        REJECT
  option output       ACCEPT
  option forward      REJECT

Next we give the guest AP a direct line to the WAN, and isolate it from the regular LAN:

config forwarding
  option src          'guest'
  option dest         'wan'

DNS rules:

config rule
  option src          'guest'
  option dest_port    '53'
  option proto        'tcpudp'
  option target       ACCEPT

DHCP rules:

config rule
  option src          'guest'
  option src_port     '67-68'
  option dest_port    '67-68'
  option proto        'udp'
  option target       ACCEPT

Restart the network and firewall (or, if you wish, reboot your router). Now you have a fully functional isolated guest AP.

Guest AP (two or more network devices)

If you are not familar with how VLANs work in OpenWrt, read my VLANs primer before proceeding. Here, I assume you only have one VLAN by default (VLAN ID 1). Check the present VLANs so you don't accidentally overwrite any existing ones. I will be providing the code that goes into the configuration files themselves, but the web interface is better for a comprehensive overview once you need to deal with multiple VLANs.

First, you need to configure a new VLAN on the router and all switches or access points. Unless stated otherwise, all code goes in /etc/config/network). A few rules of thumb:

  • Make sure the VLAN ID is the same across all devices.
  • On each network device, only the port(s) connecting the backbone of the network should be part of the VLAN.
  • Ports that are part of multiple VLANs should be tagged in every VLAN (I cannot stress this enough!).
  • Every VLAN should also include the CPU (tagged by default).

Schematically, in a router - access point combination, there's only one port on each device connecting to another network device. As such, the VLAN will only contain that port (here port 3) and the CPU, both tagged (indicated by the t behind the number), so in this case, our VLAN config will look like this:

config switch_vlan
  option device      'rtl8366s'
  option vlan        '2'
  option ports       '3t 5t'

Likewise, in a setup with (in that order) a router, a switch, and an AP, the router would have one port in the VLAN, the switch two, and the AP one, and so on.

Again, a warning: make sure every port part of more than one VLAN is tagged in every single VLAN it is part of. Failure to do so may render your device inoperable.

The interface stanza should look as follows. Note that we specify an interface, unlike in a single router setup.

config interface     'guest'
  option ifname      'eth0.2'
  option proto       'static'
  option ipaddr      ''
  option netmask     ''

On each access point, we replicate the same stanza, but with two substantial differences:

  • We add an interface type, namely bridge (this will put the LAN and WLAN interfaces on the AP in one network).
  • We set a different static IP (typically incremented by one).

Your config should look like this:

config interface     'guest'
  option ifname      'eth0.2'
  option type        'bridge'
  option proto       'static'
  option ipaddr      ''
  option netmask     ''

Replicate the wifi interface config on each access point (see above for the sample code). Disable the wireless on all other network devices.

These are the only changes you need to apply for a multi-device setup. The DHCP server only runs on the main router, so you do not need to edit any related settings on any switches or access points; do make sure though that the static IPs do not conflict with the DHCP range you set. As for the firewall, you should replicate all stanzas provided above on every network device.

A note on swconfig versus DSA

Up till 19.07, OpenWrt has relied on swconfig to configure internal switches. Upstream Linux, however, has been progressively implementing DSA (Distributed Switch Architecture) to handle Ethernet switches. In its effort to stay close to upstream and lighten the maintenance burden, OpenWrt plans to migrate to DSA as well. At this point (July 2020), MT7621 is one of the few OpenWrt targets in OpenWrt master with DSA support. For end users, this means a few practical things:

  • swconfig is phased out on targets which switched to DSA.
  • No more eth0/eth1/eth1.1/... interface names; ports are exposed individually and bridged, e.g. a four-port switch will now have option ifname 'lan1 lan2 lan3 lan4' instead of option ifname 'eth0.2'.
  • A tagged interface now looks like lan1.100 for a VLAN ID 100 e.g.

LuCI support for DSA is in the works, but at this stage DSA configuration is fully handled through the command line.

Limiting the guest AP's bandwidth

From Barrier Breaker (14.07) on, it is recommended to use <abbr title="Smart Queue Management">sqm</acronym>-scripts, which has also an easy LuCI frontend if you wish. Set the network device to the interface you want to control (in this case, wlan0-1). My /etc/config/sqm looks like this:

config queue 'eth1'
  option qdisc 'fq_codel'
  option script 'simple.qos'
  option qdisc_advanced '0'
  option linklayer 'none'
  option enabled '1'
  option interface 'wlan0-1'
  option download '8000'
  option upload '2000'

More info on SQM can be found here.

The instructions below for tc are kept for historical reference and are now deprecated, in favour of SQM.

Tc is a bit hackish, but it works. There's also wshaper, which depends on tc and looks a bit more user-friendly. Install tc:

# opkg install kmod-sched kmod-sched-core tc

After all the packages are installed, add the following to /etc/rc.local:

# Load the necessary modules
lsmod | grep sch_htb &> /dev/null || insmod /lib/modules/$(uname -r)/sch_htb.ko

# Traffic control for the guest network.
# First we flush the rules to make sure no cruft is left;
# Then we limit traffic to 200 KBps (despite the notation
# tc means KBps) with a 250 KBps ceiling.
/usr/sbin/tc qdisc del dev wlan0-1 root
/usr/sbin/tc qdisc add dev wlan0-1 root handle 1:0 htb default 10
/usr/sbin/tc class add dev wlan0-1 parent 1:0 classid 1:10 htb rate 200kbps ceil 250kbps prio 0
/usr/sbin/tc filter add dev wlan0-1 parent 1:0 prio 0 protocol ip handle 10 fw flowid 1:10

# For reference: the command to check the limits imposed
# tc -s -d class show dev wlan0-1