Ebtables

From AAISP Support Site
Jump to: navigation, search

Firewalling with Ethernet Tables

On AAISP I have decided to run a NAT-free home network. Some devices I have allow all IP addresses to control them, but also need internet access for some functionality

In addition to all that, I assigned v4 addresses dynamically to conserve the address space. This means that IPtables rules to control inward access on their own would not do, so I am using ebtables to control access.

I have an external interface ppp0 (towards AAISP) and an internal interface eth0.20 which is in fact a VLAN on eth0.

To use ebtables, the internal interface must be enslaved to an ethernet bridge

I didn’t need Spanning Tree unless the bridge is part of a loop, which it could not be with only the one ethernet interface attached. If more than one interface is used, then STP may be needed.

/etc/network/interfaces

Valid if eth0.20 used IEEE’s example of AC-DE-48-23-45-67

iface int0 inet static
 # configure IP addresses here
 address 192.0.2.0
 netmask 255.255.255.0
 up /sbin/ifconfig int0 del fe80::aede:48ff:fe23:4567/64
 up /sbin/ifconfig int0 add fe80::aede:48ff:fe23:4567/64
 up /sbin/ifconfig int0 add 2001:db8:cafe:1:aede:48ff:fe23:4567/64
 # Optional: make use of the full capability of my Gigabit ethernet switch, by using the maximum possible MTU.
 pre-up /sbin/ifconfig eth0 mtu 7200 || true
 pre-up /sbin/vconfig add eth0 20 || true
 pre-up /sbin/ifconfig eth0.20 mtu 7200 || true
 bridge_ports eth0.20
 bridge_stp off
 bridge_ageing 0
 bridge_bridgeprio 34000
 bridge_fd 0
 bridge_gcint 0
 bridge_hello 0
 bridge_maxage 0
 bridge_maxwait 0

It is also possible to do this directly with brctl if you run a GNU/Linux system that has not got /etc/network/interfaces

I had also disabled the callout of iptables from within ebtables via sysctl at this point. The feature seems to be broken in Linux 2.6.32-5-amd64 causing IPv6’s address resolution to break amongst other things. IP6tables is still working independently.

In /etc/sysctl.conf

net.bridge.bridge-nf-call-arptables=0
net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-iptables=0

Ebtables and IPtables rules

I use the netfilter mark to communicate between ebtables and iptables that has 32 bits to use and each can be independently set. The 2 type of device I want to filter are Freesat boxes, which want to access BBC iPlayer, and Sonos which will access radio stations via Tunein/Radiotime, so I am using 3 of my bits for now, as I also do MAC address based accounting.

MINET=$'0x4'
MSONOS=$'0x1'
MFREESAT=$'0x2'

The flag is set on outgoing frames from the devices by ebtables to say they want to access a particular IP address. IPtables then remembers that destination address for the packet in a recents list.

For incoming packets, netfilter flags are set on all packets unless they are from an IP address that has been approved in the corresponding recents list. When the packets are converted to frames and reach ebtables, they are DROPPED unless the netfilter flag of the device is cleared. I would have liked to REJECT instead but ebtables does not offer that.

# first let’s do some accounting.
# These rules need only match, not do anything, as we are interested in the ebtables accounting data.
ebtables -N accounting -P RETURN
ebtables -A accounting --destination AC:DE:48:23:45:67/ff:ff:ff:ff:ff:ff
ebtables -A accounting --destination AC:DE:48:23:45:67/ff:ff:ff:ff:ff:ff
ebtables -A OUTPUT -o eth0.20 -p 0x0800 --mark $MINET/$MINET -j accounting
ebtables -A OUTPUT -o eth0.20 -p 0x86DD --mark $MINET/$MINET -j accounting

# freesat devices.
ebtables -A INPUT -i eth0.20 --source 00:03:78:00:00:00/ff:ff:ff:00:00:00 -p 0x0800 -j mark --mark-or $MFREESAT --mark-target CONTINUE
ebtables -A INPUT -i eth0.20 --source 00:06:f4:00:00:00/ff:ff:ff:00:00:00 -p 0x0800 -j mark --mark-or $MFREESAT --mark-target CONTINUE
iptables -A FORWARD -i int0 -o ppp0 -m mark --mark $MFREESAT/$MFREESAT -m recent --rdest --set --name freesat
iptables -A FORWARD -i ppp0 -o int0 -m recent --rsource ! --update --name freesat -j MARK --or-mark $MFREESAT 
ebtables -A OUTPUT -o eth0.20 --destination 00:03:78:00:00:00/ff:ff:ff:00:00:00 -p 0x0800 --mark /$MFREESAT -j DROP 
ebtables -A OUTPUT -o eth0.20 --destination 00:06:f4:00:00:00/ff:ff:ff:00:00:00 -p 0x0800 --mark /$MFREESAT -j DROP

# sonos devices.
ebtables -A INPUT -i eth0.20 --source 00:0e:58:00:00:00/ff:ff:ff:00:00:00 -p 0x0800 -j mark --mark-or $MSONOS --mark-target CONTINUE
iptables -A FORWARD -i int0 -o ppp0 -m mark --mark $MSONOS/$MSONOS -m recent --rdest --set --name sonos
iptables -A FORWARD -i ppp0 -o int0 -m recent --rsource ! --update --name sonos -j MARK --or-mark $MSONOS
ebtables -A OUTPUT -o eth0.20 --destination 00:0e:58:00:00:00/ff:ff:ff:00:00:00 -p 0x0800 --mark $MSONOS/$MSONOS -j DROP

# mark incoming data so that we can account it.
# The iptables rules should work also with a default DROP target but then additional lines are needed to pass the data that is needed.
iptables -A FORWARD -i ppp0     -o int0     -j MARK --or-mark $MINET
ip6tables  -A FORWARD -i ppp0     -o int0     -j MARK --or-mark $MINET

Accounting

To save the accounting data, I used a script called out from /etc/cron.hourly and will end up with a directory tree with accounting data that resembles that from AAISP’s clueless pages but broken down by MAC address. If I had Ethernet over ADSL then the ISP might do this step instead. Old data may need to be rotated away from the output area eventually though.

#!/bin/bash

MYTIME=`date +%s`

DIR=`date -d @$MYTIME +/var/local/ebacct/%Y-%m-%d/%H -u`
PARA=
if test -n "$(mkdir -pv $DIR)"
then
       PARA=-Z
       DIR=`date -d @$(( $MYTIME - 3600 )) +/var/local/ebacct/%Y-%m-%d/%H -u`
fi
while read F MAC N N N N N PACKET N N N OCTETS N
do
       if test "$F" = "-d"
       then
               PT=$DIR/${MAC:0:2}${MAC:3:2}${MAC:6:2}${MAC:9:2}${MAC:12:2}${MAC:15:2}
               mkdir -p $PT
               echo $PACKET > $PT/packets
               echo $OCTETS > $PT/octets
       fi
done <<<"$(ebtables -L accounting --Lc --Lmac2 $PARA)"