|
Packet-Filtering Bridge
This page documents the packet-filtering bridge I setup using the
FreeBSD 3.1 release kernel. I run a small 100Mbps network with
machines that have real IP addresses that are visible to the outside
world (no natd translation).
Instead of running ipfw on each machine
that I wanted to protect, I wanted to filter traffic at the 10Mbps
(ick) connection to the outside world. I added an ethernet card to one
of the machines that I wanted to use as a bridge. Then, the fun began.
Kernel Patches
I compiled in dummynet (options DUMMYNET) and bridging (options
BRIDGE), along with the usual ipfw options. I discovered that a few
kernel source files that used `#ifdef BRIDGE' in them did not include
opt_bdg.h (which is where BRIDGE is defined when the kernel option is
specified :) ). To get a list of potential offenders, try:
find /usr/src/sys -name \*.c | xargs grep BRIDGE
Make sure these files contain
#include "opt_bdg.h"
in them. This should go before the first occurrence of #ifdef BRIDGE
in each file.
The next problem is that bridging is not supported on most
drivers. Since I use 3Com Fast Etherlink PCI cards, I had to patch
pci/if_xl.c . Thanks to Nick's page, this
process was made relatively painless. His patch to
netinet/ip_fw.c is already part of the 3.1 release source
tree. His patch to net/bridge.c was useful and I used it
to modify the 3.1 release version of net/bridge.c (the
firewall check function takes an additional parameter). In addition, I
removed the print statement two lines down that prints a message to
console when the bridge discards a packet because my ipfw
rules log this anyway. Here's the patch for
net/bridge.c .
- patch for
pci/if_xl.c (3Com
Fast EtherLink PCI)
- patch for
pci/if_tx.c
(EtherPower II Fast Ethernet), modified from
Nick's page
It should be relatively simple to apply a similar patch to other
drivers. I could simply list a bunch of patches, but I don't have the
cards to test the code. :) Here are some guidelines:
- Add the following near the top of the driver source:
#include "opt_bdg.h"
#ifdef BRIDGE
#include <net/bridge.h>
#endif
- Look for the call to
ether_input() in the driver
source. Just before the call, there are probably lines of code that
strip off the ethernet header (by adjusting the m_len field of the
mbuf m and moving the m_data field forward by the same
amount; this can also be done by calling m_adj() ). Something that
looks like the following needs to be added just before the code that
strips the header.
#ifdef BRIDGE
if (do_bridge) {
struct ifnet *bdg_ifp;
bdg_ifp = bridge_in (m);
if (bdg_ifp == BDG_DROP) {
m_freem(m);
continue;
}
if (bdg_ifp != BDG_LOCAL)
bdg_forward (&m, bdg_ifp);
if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_BCAST &&
bdg_ifp != BDG_MCAST) {
continue;
}
}
#endif
- This code is usually just preceded by code that invokes
bpf_mtap() for the Berkeley packet filter.
- For the
if_xl.c driver, the m_len and
m_pkthdr.len fields are not
correctly set until ether_input() is called.
In this case, we need to
set those fields before calling bridge_in() (see patch
above).
- The driver code probably drops non-local packets after calling the
Berkeley packet filter code (if you have bpf compiled into the
kernel). However, this is no longer the right thing to do when
bridging is enabled. Instead, you should drop non-local packets only
when the variable
do_bridge is zero. The code to drop
non-local packets is usually just after the call to bpf_mtap() .
Otherwise non-local
packets will be passed up the protocol stack when bridging is not
enabled and the interface is in promiscuous mode (see patch above).
Configuring
In my setup, the bridge machine has two interfaces: xl0
connected to the internal network (1.2.3.0, netmask 255.255.255.0),
and xl1 connected to the outside world. Interface
xl0 is assigned an IP address; interface xl1
should not have one.
After building the custom kernel and rebooting, you need to set some
sysctl variables.
sysctl -w kern.link.ether.bridge=1
sysctl -w kern.link.ether.bridge_ipfw=1
This should probably go into your /etc/rc.local file so
that you don't have to type this in each time you reboot the machine.
A simple ipfw rule that would disallow external udp connections to
port 111 can now be implemented as follows:
ipfw add 500 deny log udp from any to 1.2.3.0/24 111 via xl1 in
This specifies that packets coming in via the xl1 interface are
blocked if they attempt to connect to port 111 using UDP. Your ipfw
rules should also go into /etc/rc.local if you want them
to take effect whenever your machine is rebooted. Before you setup
your own rules, check /etc/rc.firewall ; it contains a
few standard rules you might want to use.
Good luck! :)
|
|