Building a custom wireless registration system with Linux
by Alex Fedosov

This document describes how to create a Linux-based Access Point that supports wireless device registration. That is, users must authenticate themselves in order to make use of the wireless network. This authentication only has to be done once and the system will subsequently remember the user's MAC address.

Some familiarity with Linux, system administration, and iptables is assumed on the reader's part. Furthermore, it would help to know HTML and Python if you would like to modify the provided scripts.


We use network registration in our labs. Our students bring their laptops with wireless devices so they would like to access the wireless network. I was looking for a system with a following features:

There aleady exists a network registration system, called NetReg. However, it has some shortcomings, notably the last points above are what I disliked about Netreg. It uses the shared network feature of the DHCP server to give out "fake" 192.168.x.x IP addresses to unknown clients and real IP address to registered clients.
The problems here are that: As a solution, I decided to build my own system that I dubbed NetGreg. NetGreg attempts to solve the above problems: rather than using fake DHCP and DNS servers, it employs iptables for access control and works the following way:

First, you need some hardware:
You should be able to use any old 802.11b card as well, if you are willing to accept less bandwidth. (Of course you'd need a different driver -- hostap is the best bet, so get a Prism card supported by that driver, if you are going that route.)
If you are using a computer with PCMCIA support, there is no reason why a PCMCIA card won't work instead of a PCI card. For that matter, any hardware supported by prism54 should work fine. (As far as I know, hostap and prism54 are currently the only Linux drivers that support "Master" (i.e. being an Access Point) mode, so choose your hardware accordingly.

Then, you need to have the following packages installed:
You may be able to get away with Linux 2.4, however, 2.6 has better support for Ethernet bridging (which is what I am using) and includes the prism54 driver in the kernel. (You would have to install it separately with for 2.4 kernel.) You also need a recent version of iptables that includes support for bridging. (e.g. physdev match extension).
It is a good idea to start with a distribution that already includes all the above software -- I used Fedora Core 2.

Short version for the impatient

Configuring the wireless card

In order to use the prism54 driver under Linux, you need to obtain the firmware for your card. Download it from the Prism54 site (I got the one named isl8390) and place it in the /usr/lib/hotplug/firmware directory. Now you should be able to bring up the wireless interface and get connectivity.

Prism54 makes it very easy to set up your Linux box to become an Access Point. All you really have to do is (replace eth1 with your wireless interface name):
# iwconfig eth1 mode Master
Then assign yourself an ESSID:
# iwconfig eth1 essid myessid

Bridging wireless and wired interfaces

To set up bridging, we use the brctl utility. Make sure you installed the bridge-utils package or obtained it in some other ways. First, we create a bridge interface:
# brctl addbr br0
Then we add both interfaces to it:
# brctl addif br0 eth0
# brctl addif br0 eth1
That's it! Now assign an IP address to it, bring it up, and make sure you have connectivity. You should create an ifcfg-br0 config file in /etc/sysconfig/network-scripts which will allow you to use the ifup and ifdown scripts. (On RedHat and compatible distributions. YMMV.)

NOTE: I discovered that before you can add the wireless interface to the bridge and even put the wireless interface in Master mode, you have to load the firmware. The easiest way to accomplish this is to bring up the wireless interface without assigining an IP address to it, which will trigger a firmware load:
    # ifconfig eth1 up

So to automate this all, here is a script (that should be called from rc.local) that triggers the firmware load, puts the card in Master mode, creates and brings up a bridge (basically the above three steps). This seems to be the cleanest solution, as I was not able to accomplish the same with the RedHat/Fedora network scripts.
At this point, you should have a functioning Linux-based access point that should allow access to everybody.

Setting up iptables

The next thing we need to do is set up iptables so that it allows wireless access to registered MAC addresses, and denies access to non-registered MACs (but allowing them to register.)
First, we create some basic iptables rules: At this point you should have a locked-down access point that lets all wireless clients get DHCP addresses, but does not allow any other wireless traffic through, except for connecting to the local HTTP server.

Now, we need to create rules for somehow identifying registered MAC addresses and allowing their traffic to pass through. Assuming we have a file /tmp/known_macs that contains a list of registered MAC addresses, we can read that list into a variable with
KNOWN_MACS=`cat /tmp/known_macs | awk '{print $1}'`
Now, the trick we are going to use to recognize packets from known MAC addresses is this: we are going to match packets based on MAC address in PREROUTING chain of the mangle table (basically the first entry point of the packet into the system) and we will "mark" it using iptables MARK target.
# mark all the packets from the registered MACs
for MAC in $KNOWN_MACS ; do
        $iptables -t mangle -I PREROUTING -m mac --mac-source $MAC \
                 -j MARK --set-mark 0x42
Any packets that have been marked should be allowed to pass through the FORWARD chain of the main filter table and through the PREROUTING chain without applying any other rules. We can match our previously-set mark using iptables mark extension:
# do not apply any rules to packets that have been marked (i.e. known MACs)
$iptables -t nat -A PREROUTING -m mark --mark 0x42 -j ACCEPT

# forward any packets that have been marked (i.e. know MACs)
$iptables -t filter -I FORWARD 1 -m mark --mark 0x42 -j ACCEPT
Finally, any traffic from unknown MACs that attempts to go to port 80 should be redirected to local web server for registration. This we can accomplish with the REDIRECT target:
# redirect HTTP requests from unknown MACs to local machine
$iptables -t nat -A PREROUTING -m physdev --physdev-in eth1 \
        -p tcp --dport 80 -j REDIRECT
The complete script (with some extra things added in, like logging) is available here. At this point you should have a functional access point that allows traffic from all MACs listed in your known_macs file (well, probably nobody at this point) and redirects everyone else's http traffic to the local web server. Of course, there is nothing on the web server yet, and that's what we will take care of next.

Setting up the web pages and scripts

It is up to you to design the web page as you like it. What really matters is how you authenticate users to make sure they are allowed to use the wireless network. In our case, if a user is not known, any URL he or she types into their browser will take them to the following page: (see live demo here)

At this point the user must enter his or her password which gets verified by the CGI script which gets launched when the user submits their information.
Our script attempts to ssh to a machine on which the user would have an account. If the connection is successful, the script finds the user's MAC address and adds it to the known MAC table. (e.g. the known_macs file.) Note: Make sure your known_macs file is owned by apache (or whatever user your webserver runs as), since the script will be writing to it.

Here are the two scripts: is the module that performs account verification and register.cgi is the main script. Adjust the scripts as needed, stick them in your cgi-bin/ directory, and you are ready to go!

Note: the module relies on two other packages to handle the actual SSH connection: You can also download a tarball of all the files.

Known problems