Let me ask this another way:

There are at least (2) ways to have a bash script called automatically when a user connects to OpenVPN:

  1. Placing the script in the /etc/network/if-up.d folder
  2. Renaming the script to 'learn-address' and modifying the server.conf file

I have tried both but either doesn't work

The following script called up.sh works perfect when I run it from the command line as root.

However, instead of manually calling this script each time a new user connects to OpenVPN to individually limit the bandwidth for each user (User1, User2, User3) via tc (qdisc), I would like the script to be called automatically each time a new user connects to OpenVPN without affecting the bandwidth of the current user

I tried moving the script to the following folder /etc/network/if-up.d to have it executed when a new user connects to OpenVPN, however for some reason the script does not get called (it makes no changes to qdisc), yet it is exactly the same script and works perfect when I execute it from the command line.

I also tried renaming the script to learn-address.sh and placed it in the following folder /etc/openvpn/netem/learn-address.sh to automatically get called when OpenVPN learns a new address but this doesn't work either

I have also updated the server.conf file to read as follows

script-security 3

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


script-security 3

up /etc/network/if-up.d/up.sh

But it didn't work either

Lastly, I have also tried updating the /etc/sudoers.tmp file to give permissions to the scripts and this does not seem to help either (see at the end of the post)

I am running Ubuntu 14.04

Many thanks for your help

Here is the script called up.sh that works when I call it from the command line:

# Full path to tc binary 

TC=$(which tc)

# interface - name of your interface device
# interface_speed - speed in mbit of your $interface
# ip - IP address of your server, change this if you don't want to use
#      the default catch all filters.
ip= # The IP address bound to the interface

# Define the upload and download speed limit, follow units can be 
# passed as a parameter:
# kbps: Kilobytes per second
# mbps: Megabytes per second
# kbit: kilobits per second
# mbit: megabits per second
# bps: Bytes per second

# Filter options for limiting the intended interface.
FILTER="$TC filter add dev $interface protocol ip parent 1: prio 1 u32"

# This function starts the TC rules and limits the upload and download speed
# per already configured earlier.

function start_tc { 
    tc qdisc show dev $interface | grep -q "qdisc pfifo_fast 0"  
    [ "$?" -gt "0" ] && tc qdisc del dev $interface root; sleep 1  

    # start the tc configuration
    $TC qdisc add dev $interface root handle 1: htb default 30
    $TC class add dev $interface parent 1: classid 1:1 htb rate $interface_speed burst 15k

    $TC class add dev $interface parent 1:1 classid 1:10 htb rate $download_limit burst 15k
    $TC class add dev $interface parent 1:1 classid 1:20 htb rate $upload_limit burst 15k

    $TC qdisc add dev $interface parent 1:10 handle 10: sfq perturb 10
    $TC qdisc add dev $interface parent 1:20 handle 20: sfq perturb 10

    # Apply the filter rules

    # Catch-all IP rules, which will set global limit on the server
    # for all IP addresses on the server. 
    $FILTER match ip dst flowid 1:10
    $FILTER match ip src flowid 1:20

    # If you want to limit the upload/download limit based on specific IP address
    # you can comment the above catch-all filter and uncomment these:
    # $FILTER match ip dst $ip/32 flowid 1:10
    # $FILTER match ip src $ip/32 flowid 1:20

# Removes the network speed limiting and restores the default TC configuration
function stop_tc {
    tc qdisc show dev $interface | grep -q "qdisc pfifo_fast 0"
    [ "$?" -gt "0" ] && tc qdisc del dev $interface root

function show_status {
        $TC -s qdisc ls dev $interface
# Display help 
function display_help {
        echo "Usage: tc [OPTION]"
        echo -e "\tstart - Apply the tc limit"
        echo -e "\tstop - Remove the tc limit"
        echo -e "\tstatus - Show status"

# Start
if [ -z "$1" ]; then
elif [ "$1" == "start" ]; then
elif [ "$1" == "stop" ]; then
elif [ "$1" == "status" ]; then

Here is the following file I also updated:


# This file MUST be edited with the 'visudo' command as root.
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
# See the man page for details on how to write a sudoers file.
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification    

# User privilege specification
root    ALL=(ALL:ALL) ALL
#nobody ALL=(ALL) NOPASSWD: /usr/lib/tc
nobody ALL=(ALL) NOPASSWD: /usr/lib/tc
www-data ALL=NOPASSWD: /user/lib/tc
root ALL=NOPASSWD: /user/lib/tc
root    ALL=(ALL:ALL) ALL
nobody  ALL=(ALL) NOPASSWD: /etc/openvpn/netem/learn-address.sh
root  ALL=(ALL) NOPASSWD: /etc/openvpn/netem/learn-address.sh
www-data  ALL=(ALL) NOPASSWD: /etc/openvpn/netem/learn-address.sh
nobody  ALL=(ALL) NOPASSWD: /etc/openvpn/netem/up.sh
www-data  ALL=(ALL) NOPASSWD: /etc/openvpn/netem/up.sh
root  ALL=(ALL) NOPASSWD: /etc/openvpn/netem/up.sh
nobody  ALL=(ALL) NOPASSWD: /etc/network/if-up.d/up.sh
www-data  ALL=(ALL) NOPASSWD: /etc/network/if-up.d/up.sh
root  ALL=(ALL) NOPASSWD: /etc/network/if-up.d/up.sh  

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "#include" directives:

#includedir /etc/sudoers.d
  • Thank you for posing this question! I have exactly the same problem since switching to 16.04 and just wanted to create a question regarding this after procrastinating with this for several days. – UTF-8 May 26 '16 at 20:57
  • You might want to take a look at this one. Maybe having the .sh on the end of the script is causing it not to load. askubuntu.com/questions/406126/… – Terrance May 26 '16 at 21:27
  • You're welcome UTF-8 and thank you for the suggestion @Terrance - what is frusterating is when a bash script runs perfect via the command line interpreter and the moment you want the script to automatically be called upon connection by the client the train doesn't just fall of the rails, it hits a brick wall – Server Programmer May 26 '16 at 21:30
  • @UTF-8, any progress on the suggestions by Terrance? – Server Programmer May 26 '16 at 23:14
  • @Terrance I tried the script without the .sh and it still does not appear to make any changes to qdisc – Server Programmer May 26 '16 at 23:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.