Internet @ Meetings: Traffic Shaping

Yes, the Internet at meetings topic keeps me interested and I keep refining my setup. The major problem of hotel or meeting room Wi-Fi is definitely the instability when too many client devices use the network simultaneously. But even if the local network doesn’t crash when it sees 80 to 100 devices simultaneously there is usually another side effect, the network becomes very slow.

This is because it doesn’t take a lot for the network to become congested. If the network is unmanaged, two or three devices using the network continuously over longer periods to upload or download data at the full bandwidth the network is capable of is all it takes to significantly slow down communication for everyone. Especially uplink congestion slows down TCP acknowledgment packets for downlink data to reach the server on the Internet in time which makes it impossible to make use of the full downlink bandwidth available.

To tackle uplink and downlink congestion I had a look if there is any way to shape uplink and downlink data streams on the router. Shaping individual services or streams is of little use as the majority of meeting participants use a VPN and thus, all communication flows over the same TCP or UDP stream. I was therefore looking for a way to shape all traffic to and from individual IP addresses to ensure that adequate uplink and downlink resources remain available even if several users transmit data simultaneously over a prolonged amount of time.

After quite a bit of research I found out how to perform traffic shaping the way I want on DD-WRT. Below’s the script I’ve come up with to shape traffic on a per-IP address basis with different maximum throughput speeds for up- and downlink transmissions. The uplink traffic is shaped on the WAN Ethernet interface while the downlink traffic is shaped on the LAN Ethernet interface for devices connected via a cable to the router and also on the two Wi-Fi Interfaces (2.4 and 5 GHz Wi-Fi) for the majority of devices that communicate wirelessly. Consequently, uplink traffic is only shapped on one port while the downlink traffic needs to be shaped on three interfaces.

In practice, my Netgear 3700 router with a 680 MHz processor can shape an aggregate traffic of around 40 MBit/s. In the vast majority of circumstances, thats much more than what the backhaul link provides anyway. In addition to limiting each IP address to a given maximum throughput, each IP address also gets its own IP transmit queue and packets are sent from all queues in a round-robin fashion. That means that even if the link gets congested, nobody needs to queue up behind a long queue of IP packets of heavy users as is the case in a default setup without any shaping with a single queue per interface.

And here’s the script:

#!/bin/sh

set -x

#the devices on which downlink shaping can be performed
DEV=eth0
DEV2=eth1
DEVWIFI0=wifi0
DEVWIFI1=wifi1

DOWN_SPEED=5000kbit
UP_SPEED=1500kbit

#The /8 IP subnet to use this script on
IP_NET=192.168.2.

tc qdisc del dev $DEV root
tc qdisc add dev $DEV root handle 1: cbq avpkt 1000 bandwidth 100mbit

tc qdisc del dev $DEV2 root                                          
tc qdisc add dev $DEV2 root handle 1: cbq avpkt 1000 bandwidth 100mbit 

tc qdisc del dev $DEVWIFI0 root                                          
tc qdisc add dev $DEVWIFI0 root handle 1: cbq avpkt 1000 bandwidth 100mbit

tc qdisc del dev $DEVWIFI1 root                                          
tc qdisc add dev $DEVWIFI1 root handle 1: cbq avpkt 1000 bandwidth 100mbit

#start with x.x.x.2 ip address (.1 is the gateway)
l=2

while test $l -le 253
do
    #
    # limit downlink bitrate
    tc class add dev $DEV parent 1: classid 1:$l cbq rate $DOWN_SPEED
         allot 1500 prio 5 bounded isolated
    tc filter add dev $DEV parent 1: protocol ip prio 16 u32
           match ip dst $IP_NET$l flowid 1:$l

    tc class add dev $DEVWIFI0 parent 1: classid 1:$l cbq rate $DOWN_SPEED
         allot 1500 prio 5 bounded isolated
    tc filter add dev $DEVWIFI0 parent 1: protocol ip prio 16 u32
           match ip dst $IP_NET$l flowid 1:$l

    tc class add dev $DEVWIFI1 parent 1: classid 1:$l cbq rate $DOWN_SPEED
         allot 1500 prio 5 bounded isolated
    tc filter add dev $DEVWIFI1 parent 1: protocol ip prio 16 u32
           match ip dst $IP_NET$l flowid 1:$l

    # limit uplink bitrate
    tc class add dev $DEV2 parent 1: classid 1:$l cbq rate $UP_SPEED allot 1500 prio 5 bounded isolated
    tc filter add dev $DEV2 parent 1: protocol ip prio 16 u32 match ip src $IP_NET$l flowid 1:$l
    

    l=$(($l+1))
done