How To Create a Throttled Public WiFi Hotspot
with Secure VPN Override using Fedora Core 2
Originally created Mon 8 Nov 2004 by Andrew Oakley.
Last updated Fri 12 Feb 2010 10:04 by Andrew Oakley.
Summary
Aims
Requirements
Assumptions
Preparation
DHCP
DNS
IPTables
Microsoft MPPE (VPN Encryption)
PPTPD (VPN)
TC (Throttling)
WiFi Access Point
Captive Portal Page
At home, I have created a Linux-based public WiFi hotspot that anyone, including my neighbours and the general public, can use to browse the web. Their access is throttled down, using traffic shaping, and limited to web pages and SSH only (ports 22, 80 and 443 only). Meanwhile, I can use a Virtual Private Network to bypass these restrictions and use my connection at full speed with all ports on my Microsoft Windows WiFi laptop.
Although originally created under Fedora Core 2, the system now runs on the current Ubuntu LTS release. Almost all the setup is the same.
Here we're setting up the public subnet (192.168.7.0/24) so that we can obtain an IP address, gateway and DNS servers through DHCP.
ddns-update-style interim;
ignore client-updates;
# eth1 LAN
subnet 192.168.7.0 netmask 255.255.255.0 {
option routers 192.168.7.253;
option subnet-mask 255.255.255.0;
option domain-name-servers 192.168.7.253;
range dynamic-bootp 192.168.7.17 192.168.7.191;
default-lease-time 21600;
max-lease-time 86400;
}
service dhcpd start
[GOTCHA] You also have to edit chkconfig to ensure that DHCPD stays enabled after reboots.
chkconfig --level 35 dhcpd on
Enable named to act as a DNS nameserver on the Private LAN.
service named start
chkconfig --level 35 named on
Configure iptables to act as both a firewall against the Public LAN, and to do a NAT IP masquerade for Public LAN users.
# eth1 is HOT (unencrypted public wifi hotspot)
# eth0 is LAN (private LAN & internet)
# ppp0 is VPN (PPTP virtual private network)
##################################################
# NAT
# This bit does the IP masquerading
*nat
# Kick off by accepting everything at this stage
# Anything unpleasant will be filtered out in the
# filter section anyway.
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# Allow forwarding from HOT to LAN
# eg. from 192.168.7.x clients to external websites
-A POSTROUTING -o eth0 -j MASQUERADE
-A POSTROUTING -s 192.168.0.0/24 -j MASQUERADE
COMMIT
##################################################
# FILTER
# This is where you add port rules
*filter
# Firstly, block all access
# We will open up access, but only to the things we want, later
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Accept everything from known safe networks; LAN, VPN and lo
-A INPUT -i ppp0 -s 0/0 -d 0/0 -j ACCEPT
-A INPUT -i eth0 -s 0/0 -d 0/0 -j ACCEPT
-A INPUT -i lo -s 0/0 -d 0/0 -j ACCEPT
# Drop access from HOT to internal LAN subnet
-A INPUT -i eth1 -d 192.168.0.0/24 -j DROP
-A FORWARD -i eth1 -d 192.168.0.0/24 -j DROP
# Accept restricted set of ports from HOT
# Echo
-A INPUT -i eth1 -p icmp --icmp-type echo-request -j ACCEPT
# SSH
-A INPUT -i eth1 -p tcp --dport 22 -j ACCEPT
# PPTP/VPN
-A INPUT -i eth1 -p tcp --dport 1723 -j ACCEPT
# DNS
-A INPUT -i eth1 -p tcp --dport 53 -j ACCEPT
-A INPUT -i eth1 -p udp --dport 53 -j ACCEPT
# HTTP
-A INPUT -i eth1 -p tcp --dport 80 -j ACCEPT
-A INPUT -i eth1 -p tcp --dport 443 -j ACCEPT
# Accept NAT related packets
-A FORWARD -i eth0 -j ACCEPT
-A FORWARD -i eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
# Accept NAT from VPN (PPP0), unrestricted
-A FORWARD -i ppp0 -o eth0 -j ACCEPT
# Return packets
-A INPUT -i ppp0 -m state --state ESTABLISHED,RELATED -j ACCEPT
# Accept NAT from HOT, restricted to certain ports
# Echo
-A FORWARD -i eth1 -o eth0 -p icmp --icmp-type echo-request -j ACCEPT
# SSH
-A FORWARD -i eth1 -o eth0 -p tcp --dport 22 -j ACCEPT
# DNS
-A FORWARD -i eth1 -o eth0 -p tcp --dport 53 -j ACCEPT
-A FORWARD -i eth1 -o eth0 -p udp --dport 53 -j ACCEPT
# HTTP
-A FORWARD -i eth1 -o eth0 -p tcp --dport 80 -j ACCEPT
-A FORWARD -i eth1 -o eth0 -p tcp --dport 443 -j ACCEPT
# Return packets
-A INPUT -i eth1 -m state --state ESTABLISHED,RELATED -j ACCEPT
COMMIT
echo 1 > /proc/sys/net/ipv4/ip_forward
FORWARD_IPV4 = YES
service iptables start
chkconfig --level 35 iptables on
[GOTCHA] I recommend we add Microsoft Point-To-Point Encryption (MPPE), which doesn't ship as standard in Red Hat Linux Fedora Core 2. If we don't do this, the VPN connection will be rejected by default on Windows machines, and traffic to and from your private subnet will be unencrypted (this could be very bad, especially if you had shared drives, folders or printers). If you're not using MS Windows machines on your VPN, you may skip this step.
http://prdownloads.sourceforge.net/poptop/dkms-1.12-2.noarch.rpm
http://prdownloads.sourceforge.net/poptop/kernel_ppp_mppe-0.0.4-2dkms.noarch.rpm
rpm -Uvh dkms-1.12-2.noarch.rpm kernel_ppp_mppe-0.0.4-2dkms.noarch.rpm
That's the Microsoft encryption out of the way, so if you skipped it because you're not using MS Windows machines, you need to start paying attention again now.
Now we create the PPTP VPN. Our goal here is to allow authenticated VPN users on WiFi to access a full range of Internet services at full speed, unencumbered by the firewall and speed limits of the WiFi Public LAN, and in addition, to allow VPN users access to the Private LAN.
localip 192.168.0.251
remoteip 192.168.0.193-248
name pptpd
lock
mtu 1490
mru 1490
proxyarp
auth
require-chap
# Comment out these two require-mppe lines if you are not
# using MS VPN clients.
require-mppe
require-mppe-stateless
-chap
-chapms
+chapms-v2
ipcp-accept-local
ipcp-accept-remote
lcp-echo-failure 30
lcp-echo-interval 5
mppe-128
#mppe-40 # Both 40 and 128 bits bite eachother
mppe-stateless
# ms-wins 192.168.0.253
ms-dns 192.168.0.254
netmask 255.255.255.0
# Secrets for authentication using CHAP
# client server secret IP addresses
toddington pptpd teddington
service start pptpd
chkconfig --level 35 pptpd on
The hotspot and VPN are now fully functional, the only thing that remains is to throttle the bandwidth available to the hotspot, without throttling the VPN.
#!/bin/sh
# Traffic shaping for WiFi hotspot
#
# Throttles web/DNS/SSH access but allows unlimited VPN
# Also requires iptables to firewall out unwanted ports.
#
# Adapted from http://www.wlug.org.nz/TrafficShaping
DEV=eth1
IP=192.168.7.253
# Change LINERATE to reflect your total available bandwidth
LINERATE=1mbit
# Change LOWRATE to your desired restricted public bandwidth
LOWRATE=16kbps
# Where the tc executable is.
TC=/sbin/tc
if ! test -x $TC; then
echo Cant find $TC, aborting
exit 1
fi
$TC qdisc del dev $DEV root
$TC qdisc add dev $DEV root handle 1: cbq avpkt 1000 bandwidth
$LINERATE
$TC class add dev $DEV parent 1: classid 1:1 cbq rate $LOWRATE \
allot 1500 prio 5 bounded isolated
# Throttled traffic - class 1:1
# Non-local SSH (22), HTTP (80/443), DNS (53) & DHCP (67)
$TC filter add dev $DEV parent 1: protocol ip prio 16 u32 \
match ip sport 22 0xFFFF flowid 1:1
$TC filter add dev $DEV parent 1: protocol ip prio 16 u32 \
match ip sport 53 0xFFFF flowid 1:1
$TC filter add dev $DEV parent 1: protocol ip prio 16 u32 \
match ip sport 67 0xFFFF flowid 1:1
$TC filter add dev $DEV parent 1: protocol ip prio 16 u32 \
match ip sport 80 0xFFFF flowid 1:1
$TC filter add dev $DEV parent 1: protocol ip prio 16 u32 \
match ip sport 443 0xFFFF flowid 1:1
# Prioritise queues using sfq perturb
$TC qdisc add dev $DEV parent 1:1 sfq perturb 60
# Display Traffic Shaping details
echo "---- qdisc parameters Ingress ----------"
$TC qdisc ls dev $DEV
echo "---- Class parameters Ingress ----------"
$TC class ls dev $DEV
echo "---- filter parameters Ingress ----------"
$TC filter ls dev $DEV
chmod 750 /root/tc/tc-init
/root/tc/tc-init
/root/tc/tc-init
Finally, it is time to place the WiFi access point on the 192.168.7.0/24 subnet.
I also added a "captive portal" page which redirects first-time web browsing visitors to an informational page. Unlike most other captive portals, this one deliberately does not require a log-in. My solution is yet to be fine-tuned, but in the mean time you can read about my first attempt in this uk.comp.os.linux posting.
Unfortunately this captive portal was abandoned, since without requiring authentication, it would be displayed once only to the first HTTP request originating from the client. Usually this first request was the operating system polling for updates, or an anti-virus polling for updates, so the user never saw it.
Public Domain - Andrew Oakley - 2004-11-08