|
#!/bin/bash |
|
# |
|
# slow |
|
# |
|
# Simulates a low bandwidth, high-latency network connection |
|
# |
|
# Requires a Linux operating system with the 'tc' traffic control tool |
|
# |
|
# Author: Richard Bullington-McGuire <richard@moduscreate.com> |
|
# Author: Mike Schwartz <mike@moduscreate.com> |
|
# |
|
# Copyright 2012 Modus Create, Inc. |
|
# |
|
# Permission is hereby granted, free of charge, to any person obtaining |
|
# a copy of this software and associated documentation files (the |
|
# "Software"), to deal in the Software without restriction, including |
|
# without limitation the rights to use, copy, modify, merge, publish, |
|
# distribute, sublicense, and/or sell copies of the Software, and to |
|
# permit persons to whom the Software is furnished to do so, subject |
|
# to the following conditions: |
|
# |
|
# The above copyright notice and this permission notice shall be included |
|
# in all copies or substantial portions of the Software. |
|
# |
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
|
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
# |
|
# |
|
# Changelog |
|
# --------- |
|
# |
|
# 0.1: first version |
|
# 0.2: implementation packet loss by aloysius |
|
# 0.3: add ability to use own veth device for offline testing |
|
# |
|
# Current url: https://gist.github.com/plq/53b4099df8e4a531593991dadc06c223 |
|
# |
|
|
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #" |
|
|
|
VERSION=0.3 |
|
netns=slow |
|
device=slow1 |
|
bandwidth=100kbit |
|
latency=1350ms |
|
packetloss=0% |
|
command=slow |
|
retcode=0 |
|
PATH=/sbin:/bin:/usr/sbin:/usr/bin |
|
|
|
echorun() { |
|
echo -E '#' "$@" |
|
"$@" |
|
} |
|
|
|
if ! which tc > /dev/null; then |
|
echo "Aborting: No 'tc' iptables firewall traffic conditioner found" 1>&2 |
|
echo "This requires the Linux iptables firewall." 1>&2 |
|
exit 1 |
|
fi |
|
|
|
if [ $EUID -ne 0 ]; then |
|
echo "Aborting: you must run this as root." |
|
exit 2 |
|
fi |
|
|
|
|
|
# Option processing courtesy of Stack Overflow question |
|
# http://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options/7680682#7680682 |
|
# Questioner: gagneet http://stackoverflow.com/users/35416/gagneet |
|
# Answer: http://stackoverflow.com/a/9899366 |
|
# Answerer: http://stackoverflow.com/users/375889/jakesandlund |
|
|
|
|
|
while test $# -gt 0 |
|
do |
|
case $1 in |
|
|
|
# Normal option processing |
|
-h | --help) |
|
# usage and help |
|
command=help |
|
break |
|
;; |
|
-v | --version) |
|
command=version |
|
break |
|
;; |
|
-n | --netns) |
|
shift |
|
netnse=$1 |
|
;; |
|
-d | --device) |
|
shift |
|
device=$1 |
|
;; |
|
-b | --bandwidth) |
|
shift |
|
bandwidth=$1 |
|
;; |
|
-l | --latency) |
|
shift |
|
latency=$1 |
|
;; |
|
-p | --packetloss) |
|
shift |
|
packetloss=$1 |
|
;; |
|
# ... |
|
|
|
# Special cases |
|
--) |
|
break |
|
;; |
|
--*) |
|
# error unknown (long) option $1 |
|
;; |
|
-?) |
|
# error unknown (short) option $1 |
|
;; |
|
# |
|
# Shortcuts |
|
AMPS|amps) |
|
command=slow |
|
bandwidth=14kbit |
|
latency=250ms |
|
;; |
|
EDGE|edge|2.5G|GPRS|gprs) |
|
command=slow |
|
bandwidth=50kbit |
|
latency=200ms |
|
;; |
|
3G|3g) |
|
command=slow |
|
bandwidth=1000kbit |
|
latency=200ms |
|
;; |
|
4G|4g) |
|
command=slow |
|
bandwidth=10000kbit |
|
latency=100ms |
|
;; |
|
modem-0.1k|modem-110) |
|
command=slow |
|
bandwidth=110bit |
|
latency=350ms |
|
;; |
|
modem-0.3k|modem-300) |
|
command=slow |
|
bandwidth=300bit |
|
latency=300ms |
|
;; |
|
modem-1.2k|modem-1200) |
|
command=slow |
|
bandwidth=1200bit |
|
latency=280ms |
|
;; |
|
modem-2.4k|modem-2400) |
|
command=slow |
|
bandwidth=2400bit |
|
latency=250ms |
|
;; |
|
modem-9.6k|modem-9600) |
|
command=slow |
|
bandwidth=9600bit |
|
latency=200ms |
|
;; |
|
modem-14.4k|modem-14400) |
|
command=slow |
|
bandwidth=14400bit |
|
latency=150ms |
|
;; |
|
modem-28.8k|modem-28800) |
|
command=slow |
|
bandwidth=28800bit |
|
latency=150ms |
|
;; |
|
modem-56k|modem-56000) |
|
command=slow |
|
bandwidth=56kbit |
|
latency=120ms |
|
;; |
|
56k) |
|
command=slow |
|
bandwidth=56kbit |
|
latency=40ms |
|
;; |
|
T1|t1) |
|
command=slow |
|
bandwidth=1500kbit |
|
latency=20ms |
|
;; |
|
T3|t3) |
|
command=slow |
|
bandwidth=45mbit |
|
latency=10ms |
|
;; |
|
DSL|dsl) |
|
command=slow |
|
bandwidth=2mbit |
|
latency=40ms |
|
;; |
|
cablemodem) |
|
command=slow |
|
bandwidth=10mbit |
|
latency=20ms |
|
;; |
|
wifi-a|wifi-g) |
|
command=slow |
|
bandwidth=54mbit |
|
latency=5ms |
|
;; |
|
wifi-b) |
|
command=slow |
|
bandwidth=11mbit |
|
latency=10ms |
|
;; |
|
wifi-n) |
|
command=slow |
|
bandwidth=110mbit |
|
latency=2ms |
|
;; |
|
vsat) |
|
command=slow |
|
bandwidth=5mbit |
|
latency=500ms |
|
;; |
|
clear|reset) |
|
command=clear |
|
;; |
|
clear-dev) |
|
command="clear clear-dev" |
|
;; |
|
status) |
|
command=status |
|
break |
|
;; |
|
|
|
|
|
# FUN STUFF HERE: |
|
# Split apart combined short options |
|
-*) |
|
split=$1 |
|
shift |
|
set -- $(echo "$split" | cut -c 2- | sed 's/./-& /g') "$@" |
|
continue |
|
;; |
|
|
|
# Done with options |
|
*) |
|
break |
|
;; |
|
esac |
|
|
|
# for testing purposes: |
|
echo "param $1" |
|
|
|
shift |
|
done |
|
|
|
echo "netns=$netns" |
|
echo "device=$device" |
|
echo "command=$command" |
|
echo "bandwidth=$bandwidth" |
|
echo "latency=$latency" |
|
|
|
echo $command |
|
|
|
for c in $command; do |
|
|
|
case $c in |
|
help) |
|
echo $0 |
|
echo 'Usage: slow <network-type> [-d device] [-b bandwidth] [-l latency] [-p drop]' |
|
echo ' slow reset' |
|
echo ' slow status' |
|
echo |
|
echo '"network-type" type can be:' |
|
echo ' GPRS' |
|
echo ' GSM' |
|
echo ' EDGE' |
|
echo ' 2.5G' |
|
echo ' GPRS' |
|
echo ' 3G' |
|
echo ' 4G' |
|
echo ' modem-2.4k' |
|
echo ' modem-9.6k' |
|
echo ' modem-14.4k' |
|
echo ' modem-28.8k' |
|
echo ' modem-56k' |
|
echo ' 56k' |
|
echo ' T1' |
|
echo ' T3' |
|
echo ' DSL' |
|
echo ' cablemodem' |
|
echo ' wifi-a' |
|
echo ' wifi-b' |
|
echo ' wifi-g' |
|
echo ' wifi-n' |
|
echo ' eth-10' |
|
echo ' eth-100' |
|
echo ' eth-1000' |
|
echo ' vsat' |
|
echo ' vsat-busy' |
|
;; |
|
version) |
|
# version info |
|
echo "slow version $VERSION" |
|
echo "Copyright (c) 2012 Modus Create" |
|
;; |
|
slow) |
|
# enable forwarding |
|
echo "echo 1 > /proc/sys/net/ipv4/ip_forward" |
|
echo 1 > /proc/sys/net/ipv4/ip_forward |
|
|
|
# generate sudoers file if sudo is detected |
|
if [ -n "$SUDO_USER" ]; then |
|
echo sudo detected, generating sudoers.d file |
|
sudoers_line="$SUDO_USER ALL = (ALL) NOPASSWD: $(which ip) netns exec $netns sudo -u $SUDO_USER *" |
|
echo "$sudoers_line" | tee /etc/sudoers.d/99-netns-$netns |
|
fi |
|
|
|
# add device if it does not exist |
|
if ip link | egrep -q "^[0-9]+: $device@[^:]+:" ; then |
|
echo using existing network interface $device |
|
|
|
else |
|
echo adding new netns $netns |
|
echorun ip netns add $netns |
|
|
|
echo adding new veth device ${netns}0 |
|
echorun ip link add ${netns}0 type veth peer name ${netns}1 |
|
echorun ip link set ${netns}0 netns $netns |
|
|
|
# non-routed block allocated to uk dwp |
|
# https://news.ycombinator.com/item?id=9587746 |
|
echorun ip netns exec $netns ip addr add dev ${netns}0 51.0.0.1/16 |
|
echorun ip addr add dev ${netns}1 51.0.1.1/16 |
|
fi |
|
|
|
echorun ip netns exec $netns ip link set up dev ${netns}0 |
|
echorun ip link set up dev ${netns}1 |
|
|
|
echorun ip netns exec $netns route add default gw 51.0.1.1 #dev ${netns}0 |
|
|
|
# Credit to Superuser for the basic commands that run this |
|
# Question: http://superuser.com/questions/147156/simulating-a-low-bandwidth-high-latency-network-connection-on-linux |
|
# Author: Justin L. http://superuser.com/users/38740/justin-l |
|
# Answer: http://superuser.com/a/147434/159810 |
|
if tc qdisc show dev $device | fgrep -q "qdisc htb 1: root"; then |
|
verb=change |
|
echo "Changing existing queuing discipline" |
|
else |
|
verb=add |
|
echo "Adding new queuing discipline" |
|
echorun tc qdisc $verb dev $device root handle 1: htb default 12 |
|
fi |
|
if ! lsmod | fgrep -q "sch_netem"; then |
|
modprobe sch_netem |
|
fi |
|
echorun tc class $verb dev $device parent 1:1 classid 1:12 htb rate $bandwidth ceil $bandwidth && |
|
echorun tc qdisc $verb dev $device parent 1:12 netem delay $latency loss $packetloss |
|
retcode=$? |
|
;; |
|
clear) |
|
echo resetting queueing discipline |
|
echorun tc qdisc del dev $device root |
|
retcode=$? |
|
;; |
|
clear-dev) |
|
if ! ip tuntap | egrep -q "^${device}:" ; then |
|
echo $device not a tun/tap device, not deleted. |
|
retcode=1; |
|
else |
|
echo deleting $device |
|
echorun ip tuntap del dev ${device}0 mode tap |
|
echorun ip tuntap del dev ${device}1 mode tap |
|
retcode=$? |
|
fi |
|
;; |
|
status) |
|
echorun tc qdisc |
|
retcode=$? |
|
;; |
|
esac |
|
|
|
if [ $retcode != '0' ]; then |
|
echorun exit $retcode |
|
fi |
|
|
|
done |
|
|
|
echo |
|
echo 1. make sure you bind your server to: 51.0.1.1 |
|
echo 2. make sure you run slowed process with: "sudo ip netns exec $netns sudo -u $USER \"<whatever>\"" |
|
echo |
|
echo Have fun'!' |
|
echo |
|
|
|
exit $retcode |