Dynamic Bandwidth Shaping

About writing shell scripts and making the most of your shell
Forum rules
Topics in this forum are automatically closed 6 months after creation.
Locked
User avatar
Pilosopong Tasyo
Level 6
Level 6
Posts: 1432
Joined: Mon Jun 22, 2009 3:26 am
Location: Philippines

Dynamic Bandwidth Shaping

Post by Pilosopong Tasyo »

Since I run an internet shop, I needed a way to control the bandwidth consumed by my users. At the moment this is being accomplished by using the wondershaper script (it's in the repos BTW). I've been using it for a few years now, limiting the downstream rate to a more manageable, albeit fixed level so my users don't have to fight for it.

Recently, I started toying with the concept of dynamically changing the bandwidth instead of using a fixed value. The idea was, for those users who don't consume too much bandwidth within a specified time period, they will be rewarded with a higher bandwidth allocation as compared to those who constantly consume bandwidth within the same time period.

For example, if a user casually spends more time reading website content, less bandwidth is used. As a reward, more bandwidth is allocated for that user so the next time (s)he browses for content, it's a much faster experience. This is in contrast to bandwidth hoggers who constantly download/upload content (e.g. watching video-streaming sites or multi-megabyte file transfers). To lessen the lag other non-hogging users might potentially experience, bandwidth hoggers get allocated lesser bandwidth as penalty.

I wrote the following script that implements this concept. It constantly monitors the bandwidth and changes the allocation depending on the amount of activity during a time period. It's flexible enough to allow changes to settings on-the-fly. The relevant settings are stored in a separate configuration file. You can edit the values to your liking. Before you run the script, make sure the location of the configuration file is referenced correctly in the script by editing the following line:

Code: Select all

CONFIG_FILE="/path/to/dynamic-shaper.settings"
Before you can use this script, you'll need to install wondershaper:

Code: Select all

sudo apt-get install wondershaper
To see it in action, open a terminal session where you saved the files:

Code: Select all

chmod +x dynamic-shaper.sh
sudo ./dynamic-shaper.sh
It will run in an infinite loop. Start your browser and connect to some video-streaming site. Depending on your settings, the bandwidth allocation will start to go down and will remain in the lowest setting until the streaming stops. The bandwidth will gradually increase as long as the average rate is within the specified threshold. To stop the script, hit Ctrl+C in the terminal window where the script is running, and type:

Code: Select all

sudo wondershaper clear <interface-name-here>
to disable bandwidth shaping.

To run the script on startup, save/move the files to the /root folder, and edit root's crontab settings:

Code: Select all

sudo crontab -u root -e

Code: Select all

# m h  dom mon dow   command
@reboot /root/dynamic-shaper.sh > /dev/null 2>&1
Save the cron table and reboot the computer.

I hope this is useful or helpful to someone who's in a similar boat.

Thanks to xenopeek for the heads-up about the nstat command.

Script and sample configuration file on the next post.
Last edited by LockBot on Wed Dec 28, 2022 7:16 am, edited 1 time in total.
Reason: Topic automatically closed 6 months after creation. New replies are no longer allowed.
o Give a man a fish and he will eat for a day. Teach him how to fish and he will eat for a lifetime!
o If an issue has been fixed, please edit your first post and add the word [SOLVED].
User avatar
Pilosopong Tasyo
Level 6
Level 6
Posts: 1432
Joined: Mon Jun 22, 2009 3:26 am
Location: Philippines

Re: Dynamic Bandwidth Shaping

Post by Pilosopong Tasyo »

dynamic-shaper.sh

Code: Select all

#!/bin/sh

##########
#
# Project     : Cybercafé Timer Project (CTP)
# Started     : October 17, 2011
# Last Edited : October 27, 2011
# Author      : Anthony David <Pilosopong Tasyo @ the Linux Mint Forums>
# Module      : dynamic-shaper.sh
# Description : Dynamically changes the allocated speed depending on the rate
#               of bandwidth used in a given time period.
#
##########

# Load static configuration settings
CONFIG_FILE="/path/to/dynamic-shaper.settings"
DN_CURRENT_SPEED=`cat $CONFIG_FILE | grep "DnStartSpeed" | cut -d ' ' -f2`
UP_CURRENT_SPEED=`cat $CONFIG_FILE | grep "UpStartSpeed" | cut -d ' ' -f2`

# Initial bandwidth reading
DN_READING_1=`nstat -az | grep "IpExtInOctets"  | tr -s ' ' | cut -d ' ' -f2`
UP_READING_1=`nstat -az | grep "IpExtOutOctets" | tr -s ' ' | cut -d ' ' -f2`

# Initialize variables
DN_ATTEMPTS_SO_FAR=1
UP_ATTEMPTS_SO_FAR=1
DN_AVERAGE=0
UP_AVERAGE=0

while [ true ]  # To infinity and beyond!
do
  # Let's be flexible.  Load configuration settings that can be changed on demand.
  NETWORK_CARD=`cat $CONFIG_FILE           | grep "NetworkCard"         | cut -d ' ' -f2`
  SAMPLE_RESOLUTION=`cat $CONFIG_FILE      | grep "SampleResolution"    | cut -d ' ' -f2`

  DN_MAX_SPEED=`cat $CONFIG_FILE           | grep "DnMaxSpeed"          | cut -d ' ' -f2`
  DN_MIN_SPEED=`cat $CONFIG_FILE           | grep "DnMinSpeed"          | cut -d ' ' -f2`
  DN_THRESHOLD=`cat $CONFIG_FILE           | grep "DnThreshold"         | cut -d ' ' -f2`
  DN_STEP_SPEED=`cat $CONFIG_FILE          | grep "DnStepSpeed"         | cut -d ' ' -f2`
  DN_MIN_REWARD_ATTEMPTS=`cat $CONFIG_FILE | grep "DnMinRewardAttempts" | cut -d ' ' -f2`

  UP_MAX_SPEED=`cat $CONFIG_FILE           | grep "UpMaxSpeed"          | cut -d ' ' -f2`
  UP_MIN_SPEED=`cat $CONFIG_FILE           | grep "UpMinSpeed"          | cut -d ' ' -f2`
  UP_THRESHOLD=`cat $CONFIG_FILE           | grep "UpThreshold"         | cut -d ' ' -f2`
  UP_STEP_SPEED=`cat $CONFIG_FILE          | grep "UpStepSpeed"         | cut -d ' ' -f2`
  UP_MIN_REWARD_ATTEMPTS=`cat $CONFIG_FILE | grep "UpMinRewardAttempts" | cut -d ' ' -f2`

  # Display some statistics
  clear
  echo "          Network Card - $NETWORK_CARD"
  echo "     Sample Resolution - $SAMPLE_RESOLUTION seconds"
  echo
  echo " Maximum Speed (Dn/Up) - $DN_MAX_SPEED/$UP_MAX_SPEED kbps"
  echo " Minimum Speed (Dn/Up) - $DN_MIN_SPEED/$UP_MIN_SPEED kbps"
  echo "    Step Speed (Dn/Up) - $DN_STEP_SPEED/$UP_STEP_SPEED kbps"
  echo
  echo "      Dn average speed - $DN_AVERAGE kbps"
  echo "         Do not exceed - $DN_THRESHOLD kbps"
  echo "       Attempts so far - $DN_ATTEMPTS_SO_FAR out of $DN_MIN_REWARD_ATTEMPTS"
  echo
  echo "      Up average speed - $UP_AVERAGE kbps"
  echo "         Do not exceed - $UP_THRESHOLD kbps"
  echo "       Attempts so far - $UP_ATTEMPTS_SO_FAR out of $UP_MIN_REWARD_ATTEMPTS"
  echo
  echo " Current Speed (Dn/Up) - $DN_CURRENT_SPEED/$UP_CURRENT_SPEED kbps"
  echo
  echo -n "Compiling"

  # All good things come to those who wait
  NOW=`date +%s`
  LATER=`expr $NOW + $SAMPLE_RESOLUTION`
  while [ $NOW -lt $LATER ]
  do
    echo -n "`expr $NOW - $LATER`"
    sleep 1
    NOW=`date +%s`
  done

  # Computations for downstream
  DN_READING_2=`nstat -az | grep "IpExtInOctets" | tr -s ' ' | cut -d ' ' -f2`
  DIFFERENCE=`expr $DN_READING_2 - $DN_READING_1`
  BYTES_TO_BITS=`expr $DIFFERENCE \* 8`
  BPS=`expr $BYTES_TO_BITS / $SAMPLE_RESOLUTION`
  DN_AVERAGE=`expr $BPS / 1024`
  DN_READING_1=$DN_READING_2

  # Make adjustments to the current downstream bandwith
  if [ $DN_AVERAGE -gt $DN_THRESHOLD ]
  then
    DN_CURRENT_SPEED=`expr $DN_CURRENT_SPEED - $DN_STEP_SPEED`  # Demote the bandwidth hog!
    DN_ATTEMPTS_SO_FAR=1  # And forfeit attempts
  else
    if [ $DN_ATTEMPTS_SO_FAR -ge $DN_MIN_REWARD_ATTEMPTS  ]
    then
      DN_CURRENT_SPEED=`expr $DN_CURRENT_SPEED + $DN_STEP_SPEED`  # Reward the non-hogger :D
      DN_ATTEMPTS_SO_FAR=1  # And reset attempts
    else
      DN_ATTEMPTS_SO_FAR=`expr $DN_ATTEMPTS_SO_FAR + 1`  # We're not there yet!
    fi
  fi

  # Make sure we don't exceed the minimum downstream limit...
  if [ $DN_CURRENT_SPEED -lt $DN_MIN_SPEED ]
  then
    DN_CURRENT_SPEED=$DN_MIN_SPEED
  fi
  # ...nor the maximum!
  if [ $DN_CURRENT_SPEED -gt $DN_MAX_SPEED ]
  then
    DN_CURRENT_SPEED=$DN_MAX_SPEED
  fi

  # Computations for upstream
  UP_READING_2=`nstat -az | grep "IpExtOutOctets" | tr -s ' ' | cut -d ' ' -f2`
  DIFFERENCE=`expr $UP_READING_2 - $UP_READING_1`
  BYTES_TO_BITS=`expr $DIFFERENCE \* 8`
  BPS=`expr $BYTES_TO_BITS / $SAMPLE_RESOLUTION`
  UP_AVERAGE=`expr $BPS / 1024`
  UP_READING_1=$UP_READING_2

  # Make adjustments to the current upstream bandwith
  if [ $UP_AVERAGE -gt $UP_THRESHOLD ]
  then
    UP_CURRENT_SPEED=`expr $UP_CURRENT_SPEED - $UP_STEP_SPEED`  # Demote the bandwidth hog!
    UP_ATTEMPTS_SO_FAR=1  # And forfeit attempts
  else
    if [ $UP_ATTEMPTS_SO_FAR -ge $UP_MIN_REWARD_ATTEMPTS  ]
    then
      UP_CURRENT_SPEED=`expr $UP_CURRENT_SPEED + $UP_STEP_SPEED`  # Reward the non-hogger :D
      UP_ATTEMPTS_SO_FAR=1  # And reset attempts
    else
      UP_ATTEMPTS_SO_FAR=`expr $UP_ATTEMPTS_SO_FAR + 1`  # We're not there yet!
    fi
  fi

  # Make sure we don't exceed the minimum upstream limit...
  if [ $UP_CURRENT_SPEED -lt $UP_MIN_SPEED ]
  then
    UP_CURRENT_SPEED=$UP_MIN_SPEED
  fi
  # ...nor the maximum!
  if [ $UP_CURRENT_SPEED -gt $UP_MAX_SPEED ]
  then
    UP_CURRENT_SPEED=$UP_MAX_SPEED
  fi

  # It all boils down to this piece of gem
  sudo wondershaper $NETWORK_CARD $DN_CURRENT_SPEED $UP_CURRENT_SPEED
done  # Loop back up

# EOF
dynamic-shaper.settings

Code: Select all

# Configuration settings used by the dynamic bandwidth shaper script

# Global settings
NetworkCard wlan0         # Affected interface, usually wlan0 (wireless) or eth0 (wired)
SampleResolution 10       # Time to wait for bandwidth data to accumulate (seconds)

# Downstream settings
DnMaxSpeed 1024           # Highest bandwidth allocation for non-hogs (kilobits/sec)
DnMinSpeed 256            # Lowest bandwidth when connection is being hogged (kilobits/sec)
DnStartSpeed 512          # Starting bandwidth speed (kilobits/sec)
DnThreshold 128           # Go above this amount and your speed will decrease (kilobits/sec)
DnStepSpeed 64            # Amount of bandwidth increased/decreased after an interval (kilobits/sec)
DnMinRewardAttempts 3     # Number of consecutive non-hogging intervals to attain before speed is increased

# Upstream settings
UpMaxSpeed 512            # Highest bandwidth allocation for non-hogs (kilobits/sec)
UpMinSpeed 128            # Lowest bandwidth when connection is being hogged (kilobits/sec)
UpStartSpeed 256          # Starting bandwidth speed (kilobits/sec)
UpThreshold 64            # Go above this amount and your speed will decrease (kilobits/sec)
UpStepSpeed 32            # Amount of bandwidth increased/decreased after an interval (kilobits/sec)
UpMinRewardAttempts 3     # Number of consecutive non-hogging intervals to attain before speed is increased
Sample screenshot
dynamic-shaper.png
o Give a man a fish and he will eat for a day. Teach him how to fish and he will eat for a lifetime!
o If an issue has been fixed, please edit your first post and add the word [SOLVED].
Locked

Return to “Scripts & Bash”