import site.body

Simulating latency under Linux

Sometimes it is handy to be able to simulate a high latency environment for testing of web-services under Linux. Luckily for us this is fairly easy to do and even more so to automate. Included is a script to build a virtual network with 100mS of latency for testing.

Where this script is really handy is for testing what a website may be like to access from the other side of the planet or too artificially induce latency for testing how a game reacts as the ping time goes up.

There are other tricks we can do such as inserting jitter, packet size variable delays (for simulating an IP network over ATM Cells) as well as packet loss and reordering. information on how to do this is available in the man page for tc's netem device (man tc-netem).

If you are writing a game, packet reordering and packet loss may be worth looking into. However for my simple case (webapps) latency is the most important due to my site being optimized to push out nearly all its content in the initial congestion window.

#!/bin/bash

# Fail hard and fast if any intermediate command pipeline fails
set -e

NETNS=latency-network
SERVER_IF=veth-server
CLIENT_IF=veth-client
# The delay in uSecs
DELAY=100000

function help {
    echo "$0 <Server IP> <Client IP> [CMD [ARGS ....]]"
    echo ""
    echo "    Server IP: IP address assigned to the server"
    echo "    Client IP: IP address assigned to the client, Must be"
    echo "               in the same /24 subnet as the server"
    echo "    CMD/ARGS:  The command/server to execute at the other"
    echo "               end of the high latency link, DEFAULT=/bin/sh"
}

if [ "$1" == "-h" ]; then
    help
    exit 0
fi

if [ "$1" == "" ]; then
    echo "Error: Please specify an IPv4 Address for the server"
    echo
    help
    exit 1
fi
if [ "$2" == "" ]; then
    echo "Error: Please specify an IPv4 Address for the client"
    echo
    help
    exit 1
fi
SERVER_IP=$1
CLIENT_IP=$2
shift 2

if [ "$1" == "" ]; then
    echo "No command specified, using /bin/sh"
    CMD=/bin/sh
    ARGS=""
else
    CMD=$1
    shift
    ARGS=$*
fi

# Create the networking pair
ip li add $SERVER_IF type veth peer name $CLIENT_IF
# Automatically clean up interfaces on script exit
trap "ip li del $CLIENT_IF" EXIT

# Add a 100ms Delay
tc qdisc add dev $CLIENT_IF root netem delay $DELAY
tc qdisc add dev $SERVER_IF root netem delay $DELAY

# Assign the requested IP addresses
ip ad add $CLIENT_IP/24 dev $CLIENT_IF

# Bring the interfaces up in the correct order
ip li set $CLIENT_IF up

# Create a net namespace and set it up with the server interface
ip netns add $NETNS
trap "ip li del $CLIENT_IF; ip netns del $NETNS" EXIT
ip li set $SERVER_IF netns $NETNS

# Set IP networking in the container
ip netns exec $NETNS ip ad add $SERVER_IP/24 dev $SERVER_IF
ip netns exec $NETNS ip li set $SERVER_IF up
ip netns exec $NETNS ip ro add default via $CLIENT_IP

# Execute the command in the namespace
ip netns exec $NETNS $CMD $ARGS

To use this script, simply run it as high-latency.sh 10.100.0.1 10.100.0.2 my-server-command.py --deamon, where high-latency.sh is the name of the script and my-server-command.py is the name of your program and --daemon is the argument to that program. This script will need to be run as root.

you can instead run your client program instead of my-server-command. As some servers bind to all interfaces by default, running your server via the command forces it into its own protected network ensuring that its not listening on a public Internet facing address

if you cant use IP addresses or your script is hard codded to specific Domain names then consider adding the entries to /etc/hosts to alias the domain name to the IP address specified as the first argument, eg:

10.100.0.1  gameserver.example.org

Currenly this is being used by myself to test pyshipcommand (forum, repo) as well as my own website and came out of my research for asylum