Last active
November 26, 2024 14:25
-
-
Save arr2036/6598137 to your computer and use it in GitHub Desktop.
Simple script to introduce delay on outbound packets to one or more (run multiple times) ip addresses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# root qdisc handle | |
r_handle=1 | |
# netem qdisc handle | |
n_handle=2 | |
interface= | |
delay= | |
dstip= | |
qdpresent= | |
delete=false | |
verbose=false | |
error() { | |
printf "%s\n" "$1" >&2 | |
} | |
log() { | |
if [ $verbose == true ]; then | |
printf "%s" "$1" | |
if [ "$2" != false ]; then | |
printf "\n" | |
fi | |
fi | |
} | |
# Ish... | |
isip () { | |
if ! echo "$1" | grep -E '([0-9]{1,3}[.]){3}[0-9]{1,3}(/[0-9]{1,2})?' > /dev/null; then | |
return 1 | |
fi | |
return 0 | |
} | |
iptohex () { | |
printf '%02x' `echo "$1" | sed 's/\./ /g'` | |
} | |
usage() { | |
echo "$0 -i <interface> -d <ip> -m <milliseconds> [start|stop]" | |
exit 1 | |
} | |
while getopts ":hi:d:m:v" opt; do | |
case "${opt}" in | |
h) | |
usage | |
;; | |
i) | |
interface="${OPTARG}" | |
;; | |
d) | |
dstip="${OPTARG}" | |
;; | |
m) | |
delay="${OPTARG}" | |
if ! echo "$delay" | grep -E '^[0-9]+$' > /dev/null; then | |
error "-m must be an interger value, got '$delay'" | |
usage | |
fi | |
;; | |
v) | |
verbose=true | |
;; | |
*) | |
usage | |
;; | |
esac | |
done | |
shift $((OPTIND-1)) | |
if [ "$interface" == '' ]; then | |
error "No interface specified" | |
usage | |
fi | |
if [ "$1" == 'stop' ]; then | |
delete=true | |
elif [ "$1" != 'start' ]; then | |
error "Invalid operation '$1', expected start or stop" | |
usage | |
fi | |
# | |
# Play nice with FQDNs too (IPv4 only) | |
# | |
if [ "$dstip" != '' ]; then | |
if ! isip "$dstip"; then | |
ret=`host $dstip` | |
rsv=`echo "$ret" | tail -n 1 | grep -o -E '([0-9]{1,3}[.]){3}[0-9]{1,3}'` | |
if [ $? -ne 0 ] || ! isip "$rsv"; then | |
error "Failed resolving $dstip: $ret" | |
exit 1 | |
fi | |
dstip=$rsv | |
fi | |
fi | |
# | |
# Check if we have our queue discipline already added to the target inerface | |
# let's hope nothing else if using this handle | |
# | |
log "Checking if root qdisc already added to $interface... " false | |
if tc qdisc show dev "$interface" | grep "qdisc prio $r_handle:" > /dev/null; then | |
log "yes" | |
qdpresent=true | |
else | |
log "no" | |
qdpresent=false | |
fi | |
# | |
# Were we told to stop delaying packets? | |
# | |
if [ $delete == true ]; then | |
if [ $qdpresent == true ]; then | |
log "Removing qdisc with handle $r_handle... " false | |
if tc qdisc del dev "$interface" root handle $r_handle:; then | |
log "ok" | |
else | |
log "failed ($?)" | |
exit 1 | |
fi | |
fi | |
exit 0 | |
fi | |
# | |
# Nope, first add the new root queue discipline if required | |
# | |
if [ $qdpresent != true ]; then | |
log "Adding qdisc with handle $r_handle... " false | |
if tc qdisc add dev "$interface" root handle $r_handle: prio; then | |
log "ok" | |
else | |
log "failed ($?)" | |
exit 1 | |
fi | |
fi | |
# | |
# Add any IP filters these classify the traffic and limit what's delayed | |
# | |
if [ "$dstip" != '' ]; then | |
log "Checking if IP is already in filter... " false | |
if ! tc filter show dev "$interface" parent $r_handle:0 | grep -E 'match.*'`iptohex "$dstip"` > /dev/null; then | |
log "no" | |
log "Adding IP $dstip to filter... " false | |
# Add a filter to device $interface | |
# - attach it to qdisc $r_handle:0 | |
# - apply it to IP packets | |
# - with a prio/pref (priority) of 1 (this is arbitrary as all filters have the same priority) | |
# - use the u32 classifier | |
# - match on ip dst $dstip | |
# - forward matching packets to flowid $n_handle:1 | |
if tc filter add dev "$interface" parent $r_handle:0 protocol ip prio 1 u32 match ip dst $dstip flowid $n_handle:1; then | |
log "ok" | |
else | |
log "failed ($?)" | |
exit 1 | |
fi | |
else | |
log "yes" | |
fi | |
fi | |
# | |
# This is the destination for the filters we added above | |
# | |
if [ "$delay" != '' ]; then | |
log "Checking if netem qdisc has been added (and has correct delay)... " false | |
netem=`tc qdisc show dev "$interface" | grep "netem.*$n_handle:"` | |
if [ $? -ne 0 ]; then | |
log "no" | |
log "Adding qdisc netem with handle $n_handle (delay ${delay}ms)... " false | |
if tc qdisc add dev "$interface" parent $r_handle:1 handle $n_handle: netem delay ${delay}ms; then | |
log "ok" | |
else | |
log "failed ($?)" | |
exit 1 | |
fi | |
elif ! echo "$netem" | grep "delay ${delay}.*ms" > /dev/null; then | |
log "yes" | |
log "Changing qdisc netem delay to ${delay}ms... " false | |
if tc qdisc change dev "$interface" parent $r_handle:1 handle $n_handle: netem delay ${delay}ms; then | |
log "ok" | |
else | |
log "failed ($?)" | |
exit 1 | |
fi | |
else | |
log "yes" | |
fi | |
fi |
Great script! I was a bit intimidated by the 'tc' command and needed something urgently, so this is exactly what the doctor ordered!! :)
If i run the command like this (notice the ip range): delay.sh -i bond0 -d 10.3.21.0/24 -m 300 start
... i see this message: delay.sh: line 38: printf: 0/24: invalid number
- but it still works!
Can i use an ip range like this on the '-d' parameter?
There are some issues. When starting there are a bunch of errors printed (although the delay seems to be operational).
Stopping the rule does not work.
Is this expected to run under a specific shell ?
:~$ sudo ./delay.sh -i em3 -d 10.3.1.2 -m 100 start
./delay.sh: 79: [: em3: unexpected operator
./delay.sh: 84: [: start: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 122: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
./delay.sh: 19: [: false: unexpected operator
valerio@sgx1:~$ ping 10.3.1.2
PING 10.3.1.2 (10.3.1.2) 56(84) bytes of data.
64 bytes from 10.3.1.2: icmp_seq=1 ttl=64 time=100 ms
64 bytes from 10.3.1.2: icmp_seq=2 ttl=64 time=101 ms
64 bytes from 10.3.1.2: icmp_seq=3 ttl=64 time=100 ms
64 bytes from 10.3.1.2: icmp_seq=4 ttl=64 time=101 ms
64 bytes from 10.3.1.2: icmp_seq=5 ttl=64 time=100 ms
^C
--- 10.3.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 100.566/100.903/101.017/0.264 ms
@vschiavoni looks like the shebang in the script is using sh instead of /bin/bash (which should fix your issue)
@akonkol fixed, thanks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On Ubuntu 14.10 (Linux 3.13.0-44-generic), the script seems to add delay not only the specified IP but to the whole interface, as if the filter is not properly taken into account. I didn't do my homework and didn't take a close look to tc documentation yet.
I'll send a patch if I manage to get this working. In the meantime, suggestions are welcome :)
And by the way, we meet again Mr Cudbard-Bell...