I decided to restrict Internet access from my LAN to known IP/MAC pairs only. Primary to block Internet access from my PS3, virtual machines and computers that do not need it. Simplest way to achieve this on Linux: filter packet coming from LAN interface in FORWARD chain.
On OpenWRT custom firewall rules can be defined in /etc/firewall.user. Before creating rules we need some method to create and (easily) maintain IP/MAC pairs. I decided t use /etc/ethers and /etc/hosts that already contains MACs, hostnames nad IPs (used by dnsmasq).
On my router I have /etc/ethers in format:
00:11:11:11:11:12 stargate 00:22:22:22:22:23 techie 00:33:33:33:33:34 tortoise ...
And /etc/hosts:
127.0.0.1 localhost OpenWrt ... 10.0.0.2 stargate 10.0.0.5 techie 10.0.0.14 tortoise ...
The only problem I found is parsing all those information in a simple way (eg. in a one line of sh/awk/perl/whatever script). Perl is not available in default installation of OpenWRT. SH cannot easily handle text files. Finally awk with grep seems to be really simple and efficient:
cmd = "cat /etc/hosts | grep " $2 ; cmd | getline ; print $1 " -j ACCEPT" }' /etc/ethers ; \
echo "iptables -A forwarding_rule -i br0 -j DROP")
This command (script?) parses /etc/ethers and /etc/hosts and creates firewall rules on standard output:
iptables -A forwarding_rule -i br0 -m mac --mac-source 00:11:11:11:11:12 -s 10.0.0.2 -j ACCEPT iptables -A forwarding_rule -i br0 -m mac --mac-source 00:22:22:22:22:23 -s 10.0.0.5 -j ACCEPT iptables -A forwarding_rule -i br0 -m mac --mac-source 00:33:33:33:33:34 -s 10.0.0.14 -j ACCEPT iptables -A forwarding_rule -i br0 -j DROP
Now it is easy to attach it to OpenWRT firewall in /etc/firewall.user. Just after flush rules insert:
(awk '{ printf "iptables -A forwarding_rule -i br0 -m mac --mac-source " $1 " -s " ; \
cmd = "cat /etc/hosts | grep " $2 ; cmd | getline ; print $1 " -j ACCEPT" }' /etc/ethers ; \
echo "iptables -A forwarding_rule -i br0 -j DROP") | sh
Note “| sh” at the end of command. This is needed to execute created rules.
Finally reload firewall rules:
/etc/init.d/S35firewall start
Try to access any Internet hosts from allowed and blocked (any not allowed) clients. Than check if it works:
iptables -vxL forwarding_rule
Great tip, thanks. I recycled it for the opposite purpose: blocking a few heavy freeloader from accessing my guest network (not password protected, but only for decent people).
Openwrt Chaos Calmer r43143, in the firewall -> custom rules LuCI config page:
# This file is interpreted as shell script.
# Put your custom iptables rules here, they will
# be executed with each firewall (re-)start.
# block all mac addresses in /etc/roguemaclist.txt from the guest interface
# disable by commenting the awk | sh command below
# the file /etc/roguemaclist.txt has the format
# symbolic-name1 mac-address1
# symbolic-name2 mac-address2
# ….
(awk ‘{printf “iptables -A forwarding_rule -i br-guest -m mac –mac-source ” $2 ” -j DROP\n”}’ /etc/roguemaclist.txt) | sh
Unfortunately, this iptables version won’t understand the –comment option, otherwise that could have been
(awk ‘{printf “iptables -A forwarding_rule -i br-guest -m mac –mac-source ” $2 ” –comment \”Block ” $1 “\” -j DROP\n”}’ /etc/roguemaclist.txt) | sh
Thanks again! :-)
Instead of generating a script and pipe that to sh, you can also do:
while read mac host; do
iptables … –mac-source $mac;
done < /etc/ethers
which is a bit easier to parse for humans