05/07/2016

Raspberry Pi – WAN Emulation

I used WANem or netem a couple of times in the past, but sometimes I ran it on a full blown PC/server and that wasn’t so portable… In 2015 I thought it would be cool to run it on a tiny Raspberry Pi (1, 2 or 3) and after some googleing I came across disk91.com where a nice guy already did exactly this. I read through the guide, but to fit my requirements I had to make some changes as I wanted it to be transparent (for example). Here is what I did and my result…

The stuff you will need:
– Raspberry Pi 1 B, 1 B+, 2 B or 3
– RPi case (maybe)
– RPi power supply
– (micro)SD Card (depending on your RPi model) with at least 1 gig
– ideally two USB network adapters, one would also work
minibian or Raspian lite

If you don’t have the parts, you can get all of them on Amazon. Here is what I bought:
Raspberry Pi 2 Model B Project Board – 1GB RAM – 900 MHz Quad-Core CPU
Raspberry Pi 2 or B+ Case (Black), New Version – Improved fit for Pi 2!
Raspberry Pi B+ Power Supply (5v 2A) – USA
Samsung 32GB EVO Class 10 Micro SDHC up to 48MB/s with Adapter
Lenovo ThinkPad USB 3.0 Ethernet Adapter 4X90E51405

Disclaimer: Use on your own risk!! Don’t use this guide if you don’t know what you are doing. I can’t be held responsible for deleted files/data or network downtime on your side!!!!!

 

What you need to do:

I will make this rather quick (and dirty), so I’m not going to show how to download and write minibian or Raspbian lite to your SD card etc (and be aware that this will wipe your SD card) as you can google that easily 😉 I’m listing here the steps I did on a fresh installation. You can do it a different way or maybe even say that I shouldn’t do something that way, but this is just a short guide showing ONE WAY how to do it!

When booting the RPi for the first time it will try to get an IP address via DHCP on eth0. As the OS maps the name of the interface to the MAC address, I don’t connect the USB adapters at the very first boot as this COULD make the onboard adapter not be eth0 but something else. If the boot is successful, you can login via SSH to the leased IP (see the leased addresses on your DHCP server). If you don’t have a DHCP server or whatever, you can connect a monitor and keyboard and configure it locally, of course. The default login credentials are depending on the version either root with password raspberry or pi with password raspberry (you should change that of course). If it’s the second option, you can use sudo su to become root. This means that sudo is already installed and you can skip that in the next step.

 

UPDATE: The latest version of Raspian lite automatically resizes the partition at the first boot, so you might skip the raspi-config step! Check it with df -h.

The first command I ran was apt-get update, followed by apt-get install raspi-config. This is the lazy way to resize the partition 😉 Just run raspi-config and resize your partition, as minibian uses just 512 MB by default and you will run into a full disk after installing the first few packages! Select Expand filesystem and Finish – your RPi will reboot. After the reboot, you can check if your partition has been resized by running df -h and fdisk -l. As we have more space available now we are going to upgrade our system and install some more packages:

apt-get update (if you didn't do that for raspi-config)
apt-get dist-upgrade
apt-get install sudo
apt-get install bridge-utils
apt-get install lighttpd

 

Now we got everything installed that we need. Some more things we should so now:

echo 'www-data ALL= NOPASSWD: /sbin/tc' >> /etc/sudoers
ln -s /usr/lib/cgi-bin /var/www/
lighty-enable-mod cgi

This allows lighttpd to sudo /sbin/tc, links our CGI dir and enables CGI (quick and dirty)

 

Let’s do another reboot as it is the laziest way to get everything running (especially the installed bridge stuff) and also restarts lighttpd (CGI). To be on the safe side, I prefer it to do one reboot too much than troubleshoot something that could be fixed in 5 seconds…

Up and running again we have everything configured and installed. The next step is to configure /etc/network/interfaces

Mine looks as follows:

auto lo
 iface lo inet loopback

auto eth0
 iface eth0 inet dhcp

auto br0
 iface br0 inet static
 bridge_ports eth1 eth2
 bridge_stp off
 address 10.127.254.1
 broadcast 10.127.254.3
 netmask 255.255.255.252

What does this mean? Loopback is clear. eth0 is the built in interface. I want this configured by DHCP, but you can assign a static IP as well, of course. br0 is our bridge that includes our two USB LAN adapters. If you only have one USB LAN adapter, you have to bridge between that and the onboard NIC and assign the management IP to the br0 interface. I just added a dummy IP to the bridge as I don’t want the br0 to send DHCP requests and once I had problems with tc and no IP on the bridge. This might not be an issue anymore, but the IP won’t harm anybody (unless I guessed your LAN IP or something *G* – please take care!). Use whatever you want. Now I do a halt, plug in both USB LAN adapters and power on the RPi again. Normally you should reach the RPi again via his management IP and the two USB LAN adapters should transparently bridge traffic already.

 

Now we need to configure our webinterface to easily set our parameters.

cd /var/www
ls -al
rm index.lighttpd.html (or rename it)
vi index.html

 

In your new index.html, enter the following code:

<html>
<head>
<title>RPi WAN Emulation</title>
</head>

<!-- Initial idea by disk91.com with some modifications by uebi.net, like
switched instead of routed and added the packet loss parameter. -->

<body>
<br>
<font face="arial" size="5"><b>Parameters for RPi WAN emulation</b></font>
<br><br>
<font face="arial">To clear the settings, just submit with the below defaults.
<br><br><br>
<table>
<form name="setting" action="/cgi-bin/exectc" method="post">
<tr><td>Latency (ms):</td><td><input type="text" name="latency" value="0"></td></tr>
<tr><td>Jitter (ms):</td><td><input type="text" name="var" value="0"></td></tr>
<tr><td>Bandwidth (kbit/s):</td><td><input type="text" name="bw" value="100000"></td></tr>
<tr><td>Packet Loss (%):</td><td><input type="text" name="loss" value="0"></td></tr>
</table>
<br>
<input type="submit" value="Configure">
<input type="reset" value="Defaults">
</form>
</font>

</body>
</html>

 

Our webinterface to enter the values. It should look like this:

rpiwanem01

 

We also need a short bash script that forwards the submitted values and executes them. Create a file called exectc in /var/www/cgi-bin/ and enter the following:

#!/bin/bash
# Initial idea by disk91.com (THX!) with some minor changes by uebi.net
echo "Content-type: text/html"
echo ""
read
latency=`echo $REPLY | tr "&" "\n" | grep latency | cut -d "=" -f 2`
bw=`echo $REPLY | tr "&" "\n" | grep bw | cut -d "=" -f 2`
var=`echo $REPLY | tr "&" "\n" | grep var | cut -d "=" -f 2`
loss=`echo $REPLY | tr "&" "\n" | grep loss | cut -d "=" -f 2`
echo "<html><head><title>Submitted</title></head><body><br>"
if [ -z "$latency" ] || [ -z "$bw" ] || [ -z "$var" ] || [ -z "$loss" ] ; then
echo "<font face=arial><b>Result:</b><br><br>Erorr! Go back and try again!<br/><br/>"
echo "<br><br><br><a href=/>Back</a></font>"
else
lat=$(( $latency / 2 ))
loss2=$(( $loss / 2 ))
sudo tc qdisc del dev eth1 root
sudo tc qdisc del dev eth2 root
sudo tc qdisc add dev eth1 root handle 1:0 tbf rate ${bw}kbit burst ${bw}K latency 5000ms
sudo tc qdisc add dev eth2 root handle 2:0 tbf rate ${bw}kbit burst ${bw}K latency 5000ms
sudo tc qdisc add dev eth1 parent 1:1 handle 10: netem delay ${lat}ms ${var}ms loss ${loss2}
sudo tc qdisc add dev eth2 parent 2:1 handle 10: netem delay ${lat}ms ${var}ms loss ${loss2}
echo "<font face=arial><b>Result:</b><br><br>"
echo "Latency should now be <b>+${latency}ms</b><br>"
echo "Jitter should now be <b>${var}ms</b><br>"
echo "Bandwidth should now be <b>${bw}kbit</b><br>"
echo "Packet loss should now be <b>${loss}%</b><br>"
# tc qdisc | tr "\n" "#" | sed -e "s/#/<br\/>/g"
echo "<br><br><br><button><a href=/ style=text-decoration:none><font face=arial color=#000000>Back</font></a></button></font></body></html>"
fi

 

Don’t forget to run the following to set the proper owner/group and that the script is executable:

chown www-data:www-data /var/www/cgi-bin/exectc
chmod 755 /var/www/cgi-bin/exectc

 

This script executes our tc commands and feeds them with the values supplied via the webinterface. If everything was entered as intended, a message will be displayed in your browser (or an error message if you missed something). The original script did a tc qdisc, but I remarked that and my output shows the entered values. If you prefer the other way, just remark my lines and unremark the last tc qdisc line. My output looks like this:

rpiwanem02

I hope this is helpful to somebody out there and feel free to improve it. Good luck and happy emulating 🙂





 

4 thoughts on “Raspberry Pi – WAN Emulation

  • Hi,
    das ist wirklich ein cooles Projekt.
    Ich habe versucht es mit einem RPi 3 B+ (mit dem dzt. aktuellen Raspbian mit GUI desktop) nachzubauen scheitere aber an einigen Details.
    1) wenn ich echo ‚www-data ALL= NOPASSWD: /sbin/tc‘ >> /etc/sudoers eingebe, scheitert es an den Permissions
    Wenn ich allerdings fuer /etc/sudoers alle rechte freigebe ist ihm das auch nicht recht….

    2)
    Aber das wirklich grosse Problem liegt beim ausfuehren der traffic control.
    Die netem comands lassen sich anscheinend problemlos auf ein interface zB eth1 ausfuehren. Sobald ich aber eine bridge fuer diese interface konfiguriere crasht mein RPi. Dabei macht es keinen Unterschied, ob ich die bridge vor dem tc command oder nach dem tc command konfiguriere.

    Hast du dazu einen Tip?
    lg
    Mike

    • Hi Mike!

      Danke 🙂

      Zu Punkt 1:
      Ich vermute du bist nicht root? Mein oben angefuehrter Befehl geht davon aus, dass du root bist. Versuche ein sudo su oder ein „sudo echo ‚www-data ALL= NOPASSWD: /sbin/tc‘ >> /etc/sudoers“.
      Bei einem chmod 777 auf die sudoers schreit er vermutlich, da die Datei danach von jedem beschreibbar waere, was natuerlich ein Sicherheitsrisiko ist.

      Zu Punkt 2:
      Ich habe leider nicht im Besitz eines RPi 3 B+
      Auf dem RPi 2 lief es super. Falls ich einen auftreiben kann werde ich es aber versuchen. An dem USB Adapter liegt es aber nicht? Da hatte ich auf gelegentlich mal „schlechtere“…

      LG Bernd

Kommentar verfassen