Search code examples
bashbandwidthopenvpn

Bash Script Not Working Bandwidth Shaping


I hope this is an easy answer

Problems:

  1. I placed the following bash script called learn-address.sh in the following folder:

vi /etc/openvpn/netem/learn-address.sh

  1. Added the following (2) lines to the .conf file:
script-security 3

learn-address /etc/openvpn/netem/learn-address.sh
  1. And applied the following permission to the learn-address script:
chmod 755 /etc/openvpn/netem/learn-address.sh
  1. However, the script does update the files ($ip.classid and $ip.dev) in the tmp file and passes the variables correctly

  2. But the bash script does not execute the tc class and filter commands (there is no change to qdisc)

  3. What permissions would I use on the script to execute the tc class and filter commands when the learn-address script is called when a user connects to OpenVPN or is there something else that I missed?

Many thanks

Name of script: learn-address.sh

#!/bin/bash

statedir=/tmp/

function bwlimit-enable() {
ip=$1
user=$2
dev=eth0

# Disable if already enabled.
bwlimit-disable $ip

# Find unique classid.
if [ -f $statedir/$ip.classid ]; then
    # Reuse this IP's classid
    classid=`cat $statedir/$ip.classid`
else
    if [ -f $statedir/last_classid ]; then
        classid=`cat $statedir/last_classid`
        classid=$((classid+1))
    else
        classid=1
    fi
    echo $classid > $statedir/last_classid
fi

# Find this user's bandwidth limit
# downrate: from VPN server to the client
# uprate: from client to the VPN server
if [ "$user" == "myuser" ]; then
    downrate=10mbit
    uprate=10mbit
elif [ "$user" == "anotheruser"]; then
    downrate=2mbit
    uprate=2mbit
else
    downrate=5mbit
    uprate=5mbit
fi

# Limit traffic from VPN server to client
tc class add dev $dev parent 1: classid 1:$classid htb rate $downrate
tc filter add dev $dev protocol all parent 1:0 prio 1 u32 match ip dst $ip/32 flowid 1:$classid

# Limit traffic from client to VPN server
tc filter add dev $dev parent ffff: protocol all prio 1 u32 match ip src $ip/32 police rate $uprate burst 80k drop flowid :$classid

# Store classid and dev for further use.
echo $classid > $statedir/$ip.classid
echo $dev > $statedir/$ip.dev
}

function bwlimit-disable() {
ip=$1

if [ ! -f $statedir/$ip.classid ]; then
    return
fi
if [ ! -f $statedir/$ip.dev ]; then
    return
fi

classid=`cat $statedir/$ip.classid`
dev=`cat $statedir/$ip.dev`

tc filter del dev $dev protocol all parent 1:0 prio 1 u32 match ip dst $ip/32
tc class del dev $dev classid 1:$classid

tc filter del dev $dev parent ffff: protocol all prio 1 u32 match ip src $ip/32

# Remove .dev but keep .classid so it can be reused.
rm $statedir/$ip.dev
}

# Make sure queueing discipline is enabled.
tc qdisc add dev $dev root handle 1: htb 2>/dev/null || /bin/true
tc qdisc add dev $dev handle ffff: ingress 2>/dev/null || /bin/true

case "$1" in
    add|update)
        bwlimit-enable $2 $3
        ;;
    delete)
        bwlimit-disable $2
        ;;
    *)
        echo "$0: unknown operation [$1]" >&2
        exit 1
        ;;
esac

exit 0

Solution

  • The calls to tc here are occurring before dev is defined, which happens later after you've parsed the function arguments and either called bwlimit-enable or bwlimit-disable. It looks like you want to move those two calls:

    # Make sure queueing discipline is enabled.
    tc qdisc add dev $dev root handle 1: htb 2>/dev/null || /bin/true
    tc qdisc add dev $dev handle ffff: ingress 2>/dev/null || /bin/true
    

    ... to below the case statement.