hi regedit,
actually I am currently developing a solution for exactly this situation. here is how I do it.
1. I require all guests to use dhcp. I use the --dhcp-script option together with a little C-Program I wrote. however, bash scripts would work, too. so whenever some new client receives a dhcp-lease, dnsmasq will run your script, passing 3 or 4 arguments to your program. the first arg is add|del|old, telling you whether the client just signed on(add), whether he signed off(del) or whether he just renewed his lease. the second argument will be the clients MAC address. The third will be his ip-address. if dnsmasq sends a fourth, this will be the hostname, but that information is unimportant to us now.
so this is what you want to do:
write a script that processes the dnsmasq triggers. everytime you receive an add message, create these 3 iptables rules:
(I will use $IP as the clients IP, $AP as the access point's ip):
iptables -t nat -I PREROUTING -j DNAT -s $IP -p tcp --to-destination $AP
iptables -t nat -I PREROUTING -j DNAT -s $IP -p udp --to-destination $AP
iptables -t nat -I PREROUTING -j DNAT -s $IP --dports 80 -p tcp --to-destination $AP:80
NOTE: the third rule is only necessary if the webserver resides on a different IP than the routers IP, otherwise the 2 rules above will reroute all outgoing traffic from the clients IP to the AP, including all http(Port 80) requests which will resurlt in a captive portal. NOTE 2: you will need the iptables-mod-nat package installed in order for the --to-destination option to work.
You should maintain a kind of database with the MAC-Addresses and IPs for each client.
(consider something like this:
echo "$MAC $IP" >> /tmp/leases
for the DEL-rule, you simply use the three rules from above, replacing -I by -D(for delete). Also you should remove the entry from your MAC/IP Database:
cat /tmp/leases | sed 's/$MAC $IP//g' > /tmp/leases
for old, you can look for your database.
Look up the mac in your database, like
replace = $(cat /tmp/leases | grep $MAC | sed 's/$MAC //g')
# check whether the new IP(stored in $replace) is the same as $IP
# if not, do this:
iptables -t nat -D PREROUTING -j DNAT -s $replace -p tcp --to-destination $AP
iptables -t nat -D PREROUTING -j DNAT -s $replace -p udp --to-destination $AP
iptables -t nat -D PREROUTING -j DNAT -s $replace --dports 80 -p tcp --to-destination $AP:80
then set the firewall rules like in step add again.
finally, update you database:
cat /tmp/leases | sed 's/$MAC $replace/$MAC $IP/g' > /tmp/leases
with this setup, all traffic will be redirected to your AP, and every url typed into the browser will be redirected to your AP.
NOTE: You might want to consider setting up a 404 document for redirecting your clients to the main page of your captive portal
for this, you could add some simple php script, that is simply:
<?php
if($_SERVER['HTTP_REFERER'] != "myapaddressordnsname' || $_SERVER['HTTP_REFERER'] == "")
header("Location: http://myapaddressordnsname/");
?>
note that the second option is intended to satisfy browsers that do not send or do send a false HTTP_REFERER header
Well, however, apart from this there is another thing you should watch out for - some people could come up with the idea to circumvent redirection by using static ip addresses.
you can circumvent this as well. all you will need is a cronjob.
this cronjob should be set rather granular, like every 30 seconds or something. it should simply execute a script that is parsing all ARP-Table entries(these can be read out in /proc/net/arp) with your leases database. something like this should work:
#!/bin/sh
# read in the arp-table, TODO/ get it line by line -> $lines
# parse it:
for i in $lines
do
$MAC = #todo - grep out mac address from line
if !(grep -f /tmp/leases $MAC)
do
$IP = # todo: grep out IP Address from line
# create iptables rule as from above
# then create a second database with a timeout value to determine and delete this rule once the client is no longer active
done
done
as I said earlier, I would use some other language/scripting language to work this one out, because I think it is easier to parse the arp entries(better readable)
I hope this helped you, if you have any more questions please feel free to ask
(Last edited by sador on 14 Feb 2011, 20:17)