Download quotas for users

Hi peeps,

I have a teenage lad who needs to learn some discipline with internet access etc. etc. My main problem is that he is downloading so much that he is using more than his fair share. What I'd like to do is set a download quota for him so that he can learn what this all means. Can anyone point me to decent tools / tutorials / or show me how to do this? I have his mother's support on this so this should not turn into a lesson in teenage behaviour :-).

There are ways of achieving this kind of thing, but the appropriate method depends on your configuration. In order to be able to give a reasonable suggestion can you please post the following information.

  1. How do you connect to the internet (broadand router/modem) ?
  2. How many other PCs are using the connection?
  3. Are you using wireless or ethernet or usb?

Thanx for the quick reply I realised that I had not supplied enough info - DOH!!!!!!!!! Anyway here's the stuff: Connection is via eth0 to a broadband router. Ther eis one PC connected and four users on the PC using Hardy Heron. Funny thing is that /dev/eth0 does not exist and I thought that this would be the obvious place to start.

That's the most difficult setup to implement quotas on because there is no real correlation between the user an the network traffic. I think however that I can figure something out to do what you want.

Over what time period do you want to set the quota? Daily/weekly/monthly? And what effect do you want to have when the quota runs out?

Well what I'd really like to do is stop his access to the eth0 interface so that he has no way of accessing the internet once he has reached his limit after a month. I'm sure that he can do it in three days so it will be a hard lesson for him to learn :-). Is it not possible to do this on a per user basis?

Not with the standard tools, but that's nothing unusual.

I'll have a look and see what needs to be done to get it working. I should have something reasonable by tomorrow afternoon.

Thanx REBORG you're a saint.

I've been looking around and it seems that ip-tables is the place to start - do you concur? Trouble is I cannot seem to get on with ip-tables.

greadey

Iptable is probably the final part of the puzzle, ie. how to block access, but other things are needed before getting that far.

Ok then mate let's see what you can offer.

Happy New Year :smiley:

I didn't get much time to look at this today, but I put something together in about 20 minutes to get started. I have done some basic testing and it does work but might not be the most robust yet.

The main script:

#!/bin/bash
#
#
#    posted by reborg on The UNIX and Linux Forums
#	(c) 2009 - The UNIX and Linux Forums
#

CONFIGFILE=/etc/bandlimit.conf

RUNDIR=/var/run/bandcap
STATSFILE=/proc/net/dev

AWK=/usr/bin/awk
DATE=/bin/date
IPTABLES=/sbin/iptables
TOUCH=/usr/bin/touch
WHO=/usr/bin/who

NETWORK_BLOCKED=0


block_network() {

	if [[ $NETWORK_BLOCKED -ne 1 ]] ; then
		$IPTABLES -A INPUT -j DROP
		NETWORK_BLOCKED=1
	fi
}

unblock_network() {
	$IPTABLES -D INPUT -j DROP > /dev/null 2>&1
	NETWORK_BLOCKED=0
}


init() {
	
	if [[ -f ${RUNDIR}/pidfile ]] ; then
		exit
	else
		echo $$ > ${RUNDIR}/pidfile
	fi

	

	INTERFACE=$($AWK '$1 == "INTERFACE" {print $2}' $CONFIGFILE)
	USER_BANDWIDTH=$($AWK -v user="$username" '$1 == user { printf "%i\n", $2 * 1024 * 1024 ; exit}' $CONFIGFILE)

	if [[ -z "$USER_BANDWIDTH" ]] ; then
		unblock_network
		CAP_USER=0
		return
	fi
	CAP_USER=1

	$TOUCH ${RUNDIR}/${username}.$($DATE +%m)
		
	[[ -d $RUNDIR ]] || mkdir -p $RUNDIR	

	if [[ ! -f "${RUNDIR}/${username}" ]] ; then
		echo $USER_BANDWIDTH > "${RUNDIR}/$username"
	fi

	REMAINING=$(< "${RUNDIR}/${username}" )
	# note IFS on the next line is '<space><tab>:'
	while IFS='     :' read net bandwidth junk; do
        	if [[ "$net" = "$INTERFACE" ]] ; then
			STARTUP_BANDWIDTH=$bandwidth
		fi
        done < $STATSFILE
	if (( REMAINING > 0 )) ; then
		unblock_network
	fi
	
}

run() {
	while : ; do
		set -- $($WHO)
		
		if [[ $# -lt 1 ]] ; then
			sleep 60
			next
		fi

		if [[ "$1" != "$username" ]] ; then
			username="$1"
			init
		fi

		if [[ $CAP_USER -eq 1 ]] ; then

			while IFS=' 	:' read net bandwidth junk; do
				if [[ "$net" = "$INTERFACE" ]] ; then

					LAST_MONTH=$($DATE -d "last month" +%m)
					if [[ -f ${RUNDIR}/${username}.${LAST_MONTH} ]] ; then
						rm ${RUNDIR}/${username}.${LAST_MONTH}
						$TOUCH ${RUNDIR}/${username}.$($DATE +%m)
						REMAINING=$USER_BANDWIDTH
						STARTUP_BANDWIDTH=$bandwidth
						unblock_network
					fi
			
					(( SESSION = bandwidth - STARTUP_BANDWIDTH ))
					(( REMAINING = REMAINING - SESSION ))
					if (( REMAINING <= 0 )) ; then
						block_network
						echo 0 > ${RUNDIR}/$username
					else
						echo $REMAINING > ${RUNDIR}/$username
					fi
				fi
			done < $STATSFILE
		fi
		sleep 60
	done
}

if [[ ! -f $CONFIGFILE ]] ; then
	echo "No bandwidth limits set" >& 2
	exit 2
fi

run

Config file

The format of this file is:

INTERFACE <iterface name>
<username> <monthly bandwidth limit in megabytes>

In this version you can have only one INTERFACE line and as many user lines as you want. Don't forget to update the INTERFACE line to eth0 if you use the attached sample config file.

INTERFACE wlan0
testuser 100

Init script

#!/bin/bash

PIDFILE=/var/run/bandcap/pidfile

case $1 in 
	start)
		if [[ -f "$PIDFILE" ]] ; then
			PID=$(< "$PIDFILE")
			/bin/ps -fp $PID | grep -q userband_limit.sh
			if [[ $? -eq 0 ]] ; then
				echo "User bandwidth limiter already running" >&2
				exit 1
			else
				rm "$PIDFILE"
				$START_COMMAND
			fi
		else
			( /usr/bin/userband_limit.sh & )
		fi
		;;

	stop)
		if [[ -f "$PIDFILE" ]] ; then
			PID=$(< "$PIDFILE")
			/bin/ps -fp $PID | grep -q userband_limit.sh
			if [[ $? -ne 0 ]] ; then
				rm "$PIDFILE"
				echo "User bandwidth limiter already stopped" >&2
				exit 1
			else
				rm "$PIDFILE"
				/bin/kill $PID
			fi
		else
			echo "User bandwidth limiter already stopped" >&2
			exit 1
		fi
		;;
esac

Instructions:

  1. Download the three files attached
  2. Open a terminal and cd to the directory where you downloaded the files
  3. Run the following commands:
sudo cp userband_limit.sh /usr/bin
sudo cp userband_init.sh /etc/init.d
sudo cp bandlimit.conf /etc
sudo chown root:root /usr/bin/userband_limit.sh /etc/init.d/userband_init.sh /etc/bandlimit.conf
sudo chmod 755 /usr/bin/userband_limit.sh /etc/init.d/userband_init.sh
sudo chmod 644 /etc/bandlimit.conf
cd /etc/rc1.d
sudo ln -s ../init.d/userband_init.sh K99userband
cd /etc/rc3.d
sudo ln -s ../init.d/userband_init.sh S99userband
cd /etc/rc4.d
sudo ln -s ../init.d/userband_init.sh S99userband
cd /etc/rc5.d
sudo ln -s ../init.d/userband_init.sh S99userband
  1. Edit the config file to add your usernames and bandwidth limits
sudo gedit /etc/bandlimit.conf
  1. Start the bandwidth monitor
/etc/init.d/userband_init.sh start

NOTES

  1. Stopping the limiter using
    text /etc/init.d/userband_init.sh stop
    does not unblock the network. If you need to unblock the network you need run
    text sudo /sbin/iptables -D INPUT -j DROP
  2. It can take up to 1 minute after a new user who has no limit or who has not exceeded their limit logs in for the network to become available.

WOW Reborg - if you did that in 20 mins you must be speed coder extrordinaire - I had only mangaged to get a glimpse of a perl solution.

greadey.