Now you see me, now you don't

setting up a scheduled internet connection -
using Linux as router


The Problem

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.

An automated software sollution

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 :

what happens if the router is rebooted ?
it should then probably take a decision about what routing configuration to use. If it does not So we'll see if a script can be run whenever the system is rebooted, let it evaluate the current date and time, and enable or disable the internbet connection acoordingly
remote control
As icing on the cake, some sort of remote control, remote administration, ... would be nice, so that the routing PC can be administered, configured, maintained from a remote PC on the internal network. The router can then be placed anywhere convenient and does not need to be physically accessible easily.
This could allow the kids to try and 'hack' the system to get around the limited internet access, but that's more of an interesting challenge than just plugging in a hub, so we don't mind too much for now.
Of course, remote controll from the outside (via the internet) should be impossible, otherwise the router is an open door for any culprit on the internet who'd want to come and mess with our network.

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.

Tux Router


Getting started

The network

we assume the network will look something like this : assumed network design

Routing

see Routing with Linux - the basics to set up Linux as a router. The rest of this paper will build on that setup.

create a script that modifies the routing table

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
	

create a script

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.

creating the script

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
	

scheduling : cron

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.


choosing the correct configuration when system (re-)boots

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.

ckecking for current date or day and time

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).

logging

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.


Koen Noens
April 2004