My brother in law presented me with this interesting problem. He has a home office, a veterinarin's group practice, with a number of computers connected to the internet by means of an xDSL broadband router. He's also a electronics and computer hardware hobbyist, so he has refurbished a couple of computers for his kids. Those computers recently got networked and connected to the internet as well, using the same broadband router as the home office.
The home office network exchanges data with other vets' practices over the internet. However, when the kids use up too much bandwith (playing online games ? downloading music ? movies ? ...), so he wants to have some control over just how much time his kids spend online. After all, they also have other things to do, such as school home work maybe ?
A sollution would be to be able to schedule the internet connection so that during office hours, the kids' network is automatically disconnected from the internet, or that the kids' computers can only go on the internet at scheduled times - say between 7 and 10:15 PM on weekdays and between 3:30 and 11:45 PM on Saterday and Sunday.
As a preliminary solution, my brother in law put a timing device on the wall outlet where the kid's network hub takes its power from. That works, of course. But there's room for improvement. The scheduling options of the timer are limited. And it does not take a genius to figure out that removing the timer and plugging the hub into the power outlet directly circumvents the timer effectively.
When he asked me about it, I figured that some sort of scheduled routing / firewall software would do the trick. The first thing that came to mind was that
On top of that, Linux is free, and we would only need a minimal system, so it would easily run on an old PC - like the first Pentiums at 90 or 133 Mhz that are literally being thrown away because too old, too slow.
Additionally, we can imagine the following extra's :
All of that is perfectly possible with Linux, so it's an interesting exercise in network administration and a good way to practise some basic Linux skills. So, here we go.
we assume the network will look something like this :
see Routing with Linux - the basics to set up Linux as a router. The rest of this paper will build on that setup.
The approach we take here is to change the routing table to enable / disable a connection between the kids network and the internet. A different approach would be to use firewall rules - explained further.
the routing table is modified with the route command, like this (re. man route) :
adding a route to each network (using 0.0.0.0. for any network, the internet) - the final 'route' (without parameters) will show the routing table after it has been modified.
route add -net -n 10.0.1.0 netmask 255.255.255.0 dev eth1 route add -net -n 0.0.0.0 dev eth0 route
Likewise, deleting routes is done like this :
route add -net -n 10.0.1.0 netmask 255.255.255.0 dev eth1 route add -net -n 0.0.0.0 dev eth0 route
We can also add the ADSL router's address as default gateway : everything that does not have a solution higher up in the routing table, will then be sent to the internet.
route add default gw -n 10.0.0.1
Because we'll be switching between add and del statements to turn the internet connection on and of, we'll put these statements in a script that we can run when needed. We could have 2 separate scripts (1 to enable, 1 to disable), or we can run 1 script with an option to enable / disable.
The latter is more clean (1 task - 1 script) and more interesting as a learning experience, so we'll do it that way. This is also closer to the Linux system initialisation where the same script is executed with 'start', 'stop' or 'restart' arguments.
With a text editor, create a script with the following contents. Call it "routeradjust". You can save it in /etc/rc.d for now. We'll come back to that later.
#! /bin/bash
#
# Koen Noens
# April 2004
#
# Script to add and delete routes to linux routing table
# e.g. to turn an internet connection on / of
# see http://users.telenet.be/mydotcom/howto/lanconnect/scheduled.htm
case "$1" in
enable)
echo "adjust routing table : set enabled"
/sbin/route add -net -n 10.0.1.0 netmask 255.255.255.0 dev eth1
/sbin/route add -net -n 10.0.0.0 netmask 255.255.255.0 dev eth0
/sbin/route add default gw -n 10.0.0.1
/sbin/route
;;
disable)
echo "adjust routing table : set disabled"
/sbin/route del -net -n 10.0.1.0 netmask 255.255.255.0 dev eth1
/sbin/route del -net -n 10.0.0.0 netmask 255.255.255.0 dev eth0
/sbin/route del default gw -n 10.0.0.1
/sbin/route
;;
*)
echo "Usage: $0 {enable|disable}"
exit 1
esac
exit 0
Those are just the 'route' commands we saw before, but now in a small script, right ? Make the script executable with chmod :
chmod -v u+x /etc/rc.d/routeradjust
To enable an internet connection for the 10.0.1.0 network, the script can now be called with
/etc/rc.d/routeradjust enable
to disable the internet connection, run :
/etc/rc.d/routeradjust disable
cron is used for scheduling tasks. The schedules are kept in crontab (cron table), per user. To edit the crontab for root, log on as root and type crontab -e. Then, edit or enter new tasks, and their schedule.
the cron man page is a bit obscure to a newbie such as I, but fortunately there's something like linuxhelp.net : This is their cron help guide. Thanks Joey.
The format for crontab entries is
Each entry is separated by whitespace. You can list multiple values within an item if you separate them with commas (eg Days: Sat,Sun). You can also indicate a range, with - (like 1-7 or Mon-Fri). Items without a value get *.
For the time schedule we have in mind
the following entries would be appropriate :
00 19 * * 1-5 /etc/rc.d/routeradjust enable 30 15 * * 0,6 /etc/rc.d/routeradjust enable 15 22 * * 1-5 /etc/rc.d/routeradjust disable 45 23 * * 0,6 /etc/rc.d/routeradjust disable
The default editor for crontab is vi, so use insert to add text, return to command mode with : , save with :w and save+close with :wq. Easy.
Note that in the crontab, there is no user given. Although the help says you need to give a user, on my system, with user root, that caused an error by crontab (command not found).
We now have a system that will turn routing (and thus the connection to the internet) on and off at specified times. What remains to be done is making sure that when the system is rebooted, the correct configuration is choosen. The routing configuration is saved by the system, so if the Linux router is shut down during the 'enabled' time slot, it will start again with routing enabled, and this will not be disabled until the 'disable' times are reached. So without a way of evaluating the time at system boot, and loading the corresponding configuration, the system can easliy be circumvented.
System configuration scripts are located in /etc/rc.d directory. The are called by runlevel-specific links in the runlevel directories : etc/rc.d/rc1.d, etc/rc.d/rc2.d, etc. We will now create the script in etc/rc.d, and call it from etc/rc2.d. rc2.d is the directory with (links to) the 'Resource Control' scripts that are executed when runlevel 2 is entered (or left). We thus work on the assumption that runlevel2 is the default runlevel. Use an other directory if your system defaults to another runlevel (probably 3 or 5), or change the default runlevel in etc/inittab.
rc scripts are executed with 'start' as argument when the system starts and enters the runlevel, and 'stop' when the system goes to an other runlevel. So we need a 'start' case and a 'stop' case added to the routeradjust script. the 'stop' parameter can be the same case as 'disable', but for the 'start' we need to add a case, and handle it.
case "$1" in enable) #code for option 'enable' ;; disable || stop) #code for option 'disable' and 'stop' ;; start) #code for system start ;; restart) #stop, then start again ;; *) #default : all other cases esac
To have the script executed when entering runlevel 2, create 2 links from rc2.d to this script :
ln -s -v /etc/rc.d/routeradjust /etc/rc.d/rc2.d/S12routeradjust ln -s -v /etc/rc.d/routeradjust /etc/rc.d/rc2.d/K12routeradjust
The numbers (S08..., S12..., S40...) define the order in which the scripts of the rcX.d will be executed. Choose an appropriate number, high enough so that the configuration is not changed again by an other route or network script.
In the routeradjust script, under the case start, we need some code that will get the current day, hour and minutes, and then enable or disable the router. That could look something like this :
TODAY = $(date +%a); HOUR = $(date +%k); MIN = $(date +%M); if test HOUR -gt 19 && test HOUR -lt 23; # if 'now' is between 19:00 and 23:00 then routeradjust enable else routeradjust disable fi
Note that we use routeradjust with enable / disable. A script can call itself, so we can keep al of this inside the routeradjust script. This way, future modifications (such as : different times, new routes, ...) need only be done in 1 place.
For flexibility, we can put the values (the hours, minutes, names of days, ...) in variables, so that we can easily replace them with other values if we want to modify the configuration. The complete script then looks like this :
#!/bin/bash
#
# Koen Noens
# koennoens@tiscali.be
# April 2004
#
# Script adds or deletes routes to/from kernel routing tables
# e.g. to enable / disable connection from a local network to the internet
# see http://users.telenet.be/mydotcom/howto/lanconnect/scheduled.htm
#
#
#################################################################################
#create simple log entry#
date >> /var/log/routerlog
echo "starting $0 $1" >> /var/log/routerlog
echo "===================" >> /var/log/routerlog
#handle arguments#
case "$1" in
enable)
echo "adjust routing table : set enabled"
/sbin/route add -net -n 192.168.0.0 netmask 255.255.255.0 dev eth0
/sbin/route add -net -n 192.168.1.0 netmask 255.255.255.0 dev eth1
/sbin/route add default gw -n 192.168.1.1
/sbin/route | more
;;
disable)
echo "adjust routing table : set disabled"
/sbin/route del -net -n 192.168.0.0 netmask 255.255.255.0 dev eth0
/sbin/route del -net -n 192.168.1.0 netmask 255.255.255.0 dev eth1
/sbin/route del default gw -n 192.168.1.1
/sbin/route | more
;;
stop)
$0 disable
;;
start)
#########################################################
# executed by rc2 when system starts #
# need to define time of day, and decide whether to run #
# routeradjust enable or #
# routeradjust disable #
#########################################################
echo "running $0 $1"
echo "creating routing table according to date and time"
#get current time
TODAY=$(date +%a)
HOUR=$(date +%k)
MIN=$(date +%M)
#time schedule
case $TODAY in
Sun)
BeginH=15
BeginM=30
StopH=23
StopM=45
;;
Mon)
BeginH=19
BeginM=0
StopH=22
StopM=15
;;
Tue)
BeginH=19
BeginM=0
StopH=22
StopM=15
;;
Wed)
BeginH=19
BeginM=0
StopH=22
StopM=15
;;
Thu)
BeginH=19
BeginM=0
StopH=22
StopM=15
;;
Fri)
BeginH=19
BeginM=0
StopH=22
StopM=15
;;
Sat)
BeginH=15
BeginM=30
StopH=23
StopM=45
;;
esac
echo "connection on $(date +%A) allowed beween $BeginH:$BeginM - $StopH:$StopM"
if [[ $HOUR -gt $BeginH && $HOUR -lt $StopH ]]
then
$0 enable
elif [[ $HOUR -eq $BeginH && $MIN -ge $BeginM ]]
then
$0 enable
elif [[ $HOUR -eq $StopH && $MIN -lt $StopM ]]
then
$0 enable
else
$0 disable
fi
#update cron table : use given schedule
#find and delete 'routeradjust' entries in crontab ?
#how ? : grep ? crontab -e ? pipe ? redirect ?
#enter new entries - how ?
;;
### 'start' section ends ###
restart)
$0 stop && $0 start
;;
*)
echo "Usage : $0 {enable|disable|start|stop|restart}"
exit 1
esac
exit 0
The only thing left to check : routing may be set up twice, once by this script, once by by etc/rc.d/route script (reading its configuration from etc/rc.d/route.conf). Depending on which routes are added or removed by the routeradjust script, this may result in multiple (identical) routes to the same networks, so that there will still be ("unallowed") routes left after the disabled option has run (eg. by cron). This can be solved by carefully considering which routes to add (enable) or delete (disable), or by disabling the route script (change the name so it is not found by the system), or deleting the etc/rc.d/route.conf file (eg: via a statement in 'disable' or 'stop' case).
To create a simple log, add the following statement to the beginning of the script. This will write entries such as 'Sun April 1 15:03:01 routeradjust enable' to var/log/routerlog.
date >> /var/log/routerlog echo "$0 $1" >> /var/log/routerlog
The only inconsistency left (for as far as I can see), is that the times in the routeradjust restart script are not necessarily the same as in the crontab. It would be interesting to have the script update crontab as well, when it is started, so that the values are always consistent. That remains on the To Do list for now.