Simulate a slow internet connection to a Linux server

Linux snail

Objectives

I want to:

  • For test purposes; being able to simulate a slow internet connection (low download/upload speed, latency, packet loss)

The script

We're going to create a service that when started will shape incoming traffic according to our specs. We're gonna call our service netem, which will rely on Linux's tc a.k.a traffic control, which is used by the Linux kernel to configure traffic handling policies on the machine.

Of course this is a total misuse of a utility which is meant to control traffic on network interfaces, and not to drastically impair our servers by setting nutty parameters! Well, we're doing this locally and temporarily, so this won't hurt.

Let's create our script (gist):

$ vi /etc/init.d/netem

We colorized the variables that need changing:

#!/bin/bash 
# 
# tc uses the following units when passed as a parameter. 
# kbps: Kilobytes per second 
# mbps: Megabytes per second 
# kbit: Kilobits per second 
# mbit: Megabits per second 
# bps: Bytes per second 
# Amounts of data can be specified in: 
# kb or k: Kilobytes 
# mb or m: Megabytes 
# mbit: Megabits 
# kbit: Kilobits 
# To get the byte figure from bits, divide the number by 8 bit 
# 
# 
# Name of the traffic control command. 
TC=/sbin/tc 
  
# The network interface whose bandwidth should be throttled. 
IF=eth0 

# Latency 
LAT_1=200ms # Base latency 
LAT_2=50ms # Plus or minus 
LAT_3=25% # Based on previous packet % 
# Dropping packets 
DROP_1=5% # Base probability 
DROP_2=25% # Based on previous packet % 
# Bandwidth 
DNLD=128kbps # DOWNLOAD Limit 
UPLD=64kbps # UPLOAD Limit 
  
# IP address of the machine we are controlling 
IP=192.168.0.100 # Host IP 
  
# Filter options for limiting the intended interface. 
U32="$TC filter add dev $IF protocol ip parent 1:0 prio 1 u32" 
  
start() { 
  
# We'll use Hierarchical Token Bucket (HTB) to shape bandwidth. 
# For detailed configuration options, please consult Linux man 
# page. 
  
$TC qdisc add dev $IF root handle 2: netem delay $LAT_1 $LAT_2 $LAT_3 loss $DROP_1 $DROP_2 
$TC qdisc add dev $IF parent 2: handle 1: htb default 30 
$TC class add dev $IF parent 1: classid 1:1 htb rate $DNLD 
$TC class add dev $IF parent 1: classid 1:2 htb rate $UPLD 
$U32 match ip dst $IP/32 flowid 1:1 
$U32 match ip src $IP/32 flowid 1:2 
  
# The first line creates the root qdisc, and the next three lines 
# create three child qdisc that are to be used to shape download 
# and upload bandwidth. 
# 
# The 5th and 6th line creates the filter to match the interface. 
# The 'dst' IP address is used to limit download speed, and the 
# 'src' IP address is used to limit upload speed. 
  
} 
  
stop() { 
  
# Stop the bandwidth shaping. 
$TC qdisc del dev $IF root 
  
} 
  
restart() { 
  
# Self-explanatory. 
stop 
sleep 1 
start 
  
} 
  
show() { 
  
# Display status of traffic control status. 
$TC -s qdisc ls dev $IF 
  
} 
  
case "$1" in 
  
start) 
  
echo -n "Starting bandwidth shaping: " 
start 
echo "done" 
;; 
  
stop) 
  
echo -n "Stopping bandwidth shaping: " 
stop 
echo "done" 
;; 
  
restart) 
  
echo -n "Restarting bandwidth shaping: " 
restart 
echo "done" 
;; 
  
show) 
  
echo "Bandwidth shaping status for $IF:" 
show 
echo "" 
;; 
  
*) 
  
pwd=$(pwd) 
echo "Usage: tc.bash {start|stop|restart|show}" 
;; 
  
esac 
  
exit 0

The aftermath

Let's save the file, make it executable and start the service:

chmod +x /etc/init.d/netem
$ /sbin/service netem start

If you're sshing into your server, you should already notice the latency just by typing a new command. Pinging your server should give you an idea of what you've just done:

$ ping lti.local 
PING lti.local (192.16.0.100) 56(84) bytes of data. 
64 bytes from lti.local (192.16.0.100): icmp_seq=1 ttl=54 time=254 ms 
64 bytes from lti.local (192.16.0.100): icmp_seq=2 ttl=54 time=180 ms 
64 bytes from lti.local (192.16.0.100): icmp_seq=3 ttl=54 time=262 ms 
64 bytes from lti.local (192.16.0.100): icmp_seq=4 ttl=54 time=217 ms 
...
--- lti.local ping statistics --- 
59 packets transmitted, 58 received, 1% packet loss, time 58060ms 
rtt min/avg/max/mdev = 177.236/229.598/275.693/28.808 ms

Now say hello weird layout problems on your sites you hadn't noticed before! Well, hopefully this shouldn't affect your layout, but some unforeseen issue may creep its ugly head and force you to make a few adjustments. That's the whole point of testing isn't it?

Further reading