Friday, April 21, 2006

3.3 Bridge Config

Depending upon your kernel version you will need either the old bridge configuration utility (BRCFG) for kernels before 2.2.14, or the new bridge configuration utility (bridgex) for later kernels; these utilities allow you to control the bridging in your kernel when CONFIG_BRIDGE is turned on. BRCFG is distributed as source with pre-compiled executables. I do not know what kernel the executable was compiled with, but I got different results after I recompiled it with my kernel (2.2.13) include files. Unfortunately, to do this I had to patch them slightly. Here are the patches:

diff -C 3 -r /tmp/BRCFG/brcfg.c ./brcfg.c
*** /tmp/BRCFG/brcfg.c Wed Feb 21 19:11:59 1996
--- ./brcfg.c Wed Dec 8 12:52:23 1999
***************
*** 1,6 ****

! #include
! #include
#include

#include "br.h"
--- 1,6 ----

! #include
! #include
#include

#include "br.h"


Apply the patch, recompile brcfg and install it somewhere appropriate (I chose /usr/sbin).

For kernels later than 2.2.13 you definitely want to use the newer bridge configuration utility bridgex. I am not sure if it works with earlier kernels or not. Not that the URL for this utility is found in the kernel configuration help file /usr/src/linux/Documentation/Configure.help, so if the URL mentioned here is not correct, look in the help file (it is the help for the CONFIG_BRIDGE kernel configuration item. The bridgex tarball contains an already compiled executable, but you should probably remake it using the included Makefile. Note that the bridgex utility takes slightly different arguments than does the BRCFG package (that will be covered later when I talk about configuring the bridge).
3.4 Kernel Configuration

You will need to patch and configure your kernel for bridging and the bridging filter (as well as firewalling, networking, etc. if you do not already have it). The following kernel configuration items will be needed (at least):

CONFIG_EXPERIMENTAL=y
CONFIG_BRIDGE=y
CONFIG_FIREWALL=y
CONFIG_IP_FIREWALL=y


You should grab the Bridge Filter Patch and apply it to your kernel. Recompile and install your kernel and then reboot.
3.5 Putting It All Together

So you should have your two NIC's working, a newly configured kernel, and brcfg installed. Now you need to construct a startup script to put it all together. I did this using the RedHat type startup scripts (/etc/rc.d). I put specific network addresses and masks in /etc/sysconfig/network:

GATEWAY=192.168.2.129 # the address of the DSL router
GATEWAYDEV=eth1 # the NIC that the router is connected to
ETH0_ADDR=192.168.2.130 # the IP address for the NIC on our LAN
ETH0_MASK=255.255.255.192 # the netmask of our LAN
ETH0_BROAD=192.168.2.191 # the broadcast address of our LAN
ETH1_ADDR=192.168.2.130 # the IP address for the NIC on the DSL side
# can be different from ETH0_ADDR if you want
ETH1_MASK=$ETH0_MASK # the DSL side netmask, should be the same as eth0
ETH1_BROAD=$ETH1_BROAD # ditto for the broadcast address


Next I created a script in /etc/rc.d/init.d/bridge to setup the bridge. I include two scripts here. The first script is used with the old BRCFG utility, the second for the newer bridgex. First the one for the older BRCFG:

#!/bin/sh
#
# bridge This shell script takes care of installing bridging for dsl with BRCFG
#
# description: Uses brcfg to start bridging and ifconfigs eths
# processname: bridge
# config:

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# See how we were called.
case "$1" in
start)
echo -n "Configuring bridge: "
ifconfig eth0 $ETH0_ADDR netmask $ETH0_MASK broadcast $ETH0_BROAD
ifconfig eth1 $ETH1_ADDR netmask $ETH1_MASK broadcast $ETH1_BROAD
route add $GATEWAY dev $GATEWAYDEV
route add default gw $GATEWAY dev $GATEWAYDEV
ifconfig eth0 promisc
ifconfig eth1 promisc
brcfg -enable
echo
;;
stop)
# Stop daemons.
brcfg -disable
ifconfig eth0 down
ifconfig eth1 down
;;
restart)
$0 stop
$0 start
;;
status)
ifconfig eth0
ifconfig eth1
brcfg
;;
*)
echo "Usage: bridge {start|stop|restart|status}"
exit 1
esac

exit 0


The next script is the one to use with the newer bridge configuration utility bridgex. Note that bridgex is much more configurable than the older BRCFG and so you may want to look man page included with the bridgex tarball and custom configure this script:

#!/bin/sh
#
# bridge This shell script takes care of installing bridging for dsl with BRCFG
#!/bin/sh
#
# bridge This shell script takes care of installing bridging for dsl with bridgex
#
# description: Uses brcfg to start bridging and ifconfigs eths
# processname: bridge
# config:

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# See how we were called.
case "$1" in
start)
echo -n "Configuring bridge: "
ifconfig eth0 $ETH0_ADDR netmask $ETH0_MASK broadcast $ETH0_BROAD
ifconfig eth1 $ETH1_ADDR netmask $ETH1_MASK broadcast $ETH1_BROAD
route add default gw $GATEWAY dev $GATEWAYDEV
ifconfig eth0 promisc
ifconfig eth1 promisc
brcfg start
brcfg device eth0 enable
brcfg device eth1 enable
echo
;;
stop)
# Stop daemons.
brcfg stop
ifconfig eth0 down
ifconfig eth1 down
;;
restart)
$0 stop
$0 start
;;
status)
ifconfig eth0
ifconfig eth1
brcfg
;;
*)
echo "Usage: bridge {start|stop|restart|status}"
exit 1
esac

exit 0


The script is run during bootup. It assigns addresses to each NIC, adds a default route that goes to the DSL router, adds a specific route direct to the DSL router, puts each NIC in "promiscuous" mode, and then enables bridging. I linked this script into the following directories in /etc/rc.d:

/etc/rc.d/rc0.d/K90bridge
/etc/rc.d/rc1.d/K90bridge
/etc/rc.d/rc2.d/S11bridge
/etc/rc.d/rc3.d/S11bridge
/etc/rc.d/rc4.d/S11bridge
/etc/rc.d/rc5.d/S11bridge
/etc/rc.d/rc6.d/K90bridge


This makes it run right after the network start script. You should disable other configuration of eth0 (or eth1) such as done in the /etc/rc.d/init.d/network script (in RedHat by removing files ifcfg-eth? from /etc/sysconfig/network-scripts/).

To try things out, I suggest rebooting in single user mode (specify "single" as an arg to the kernel, e.g. in lilo "lilo: linux single") and running the startup scripts in /etc/rc.d/rc3.d one at a time until you get to the bridge startup. Startup the bridge and then see if you can reach some machines (you probably want to use "ping -n" for this to keep the nameserver out of the equation):

ping the DSL router
ping a local machine
ping a machine on the global net

If you can ping all those places, there is a good chance that things are working. Note that the bridge takes a few moments to startup. You can monitor the status of the bridge by issuing the command brcfg with no arguments.
----------------------------------------------------------------------------
#!/bin/sh
#
# rc.firewall - Initial SIMPLE IP Firewall test script for 2.4.x
#
# Author: David Whitmarsh
# (c) 2001, 2002 Sparkle Computer Co ltd.
# based on rc.firewall by Oskar Andreasson
# parts (c) of BoingWorld.com, use at your own risk,
# do whatever you please with
# it as long as you don't distribute this without due credits to
# BoingWorld.com and Sparkle Computer Co Ltd
#

###########
# Configuration options, these will speed you up getting this script to
# work with your own setup.

#
# your LAN's IP range and localhost IP. /24 means to only use the first 24
# bits of the 32 bit IP adress. the same as netmask 255.255.255.0
#
# BR_IP is used to access the firewall accross the network
# For maxium security don't set one up - but then you must do
# everything directly on the firewall.

BR_IP="xxx.xxx.xxx.57"
BR_IFACE=br0

LAN_BCAST_ADDRESS="xxx.xxx.xxx.63"
INTERNAL_ADDRESS_RANGE="xxx.xxx.xxx.56/29"

INET_IFACE="eth1"
LAN_IFACE="eth0"

LO_IFACE="lo"
LO_IP="127.0.0.1"

IPTABLES="/sbin/iptables"

#########
# Load all required IPTables modules
#

#
# Needed to initially load modules
#
/sbin/depmod -a

#
# Adds some iptables targets like LOG, REJECT
#
/sbin/modprobe ipt_LOG
/sbin/modprobe ipt_REJECT

#
# Support for connection tracking of FTP and IRC.
#
/sbin/modprobe ip_conntrack_ftp
/sbin/modprobe ip_conntrack_irc

#
# Take down the interfaces before setting up the bridge
#

ifdown $INET_IFACE
ifdown $LAN_IFACE
ifconfig $INET_IFACE 0.0.0.0
ifconfig $LAN_IFACE 0.0.0.0

# Clean up for a restart

$IPTABLES -F
$IPTABLES -X
#
# Set default policies for the INPUT, FORWARD and OUTPUT chains
#

$IPTABLES -P INPUT DROP
$IPTABLES -P OUTPUT ACCEPT
$IPTABLES -P FORWARD DROP

# Our interfaces don't have IP addresses so we have to start with the mangle
# PREROUTING table

$IPTABLES -t mangle -P PREROUTING DROP

# Now we are pretty secure, let's start the bridge
# This will create a new interface

brctl addbr $BR_IFACE

# and add the interfaces to it
brctl addif $BR_IFACE $INET_IFACE
brctl addif $BR_IFACE $LAN_IFACE

# make us visible to the network again (optional)
if [ "$BR_IP" != "" ] ; then
ifconfig $BR_IFACE $BR_IP
else
# otherwise we must at least bring the interface up for the bridge to work.
ifconfig $BR_IFACE up
fi

# Block obvious spoofs

$IPTABLES -t mangle -A PREROUTING -s 192.168.0.0/16 -j DROP
$IPTABLES -t mangle -A PREROUTING -s 10.0.0.0/8 -j DROP
$IPTABLES -t mangle -A PREROUTING -s 172.16.0.0/12 -j DROP

# Accept internal packets on the internal i/f
$IPTABLES -t mangle -A PREROUTING -i $LAN_IFACE -s $INTERNAL_ADDRESS_RANGE -j ACCEPT

# Accept external packets on the external i/f

$IPTABLES -t mangle -A PREROUTING -i $INET_IFACE ! -s $INTERNAL_ADDRESS_RANGE -j ACCEPT

#
# Accept the packets we actually want to forward
#

$IPTABLES -A FORWARD -p ALL -s $INTERNAL_ADDRESS_RANGE -j ACCEPT
$IPTABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A FORWARD -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level 7 --log-prefix "IPT FORWARD packet died: "

#
# Create separate chains for ICMP, TCP and UDP to traverse
#

$IPTABLES -N icmp_packets
#
# ICMP rules
#

$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 0 -j ACCEPT # echo reply
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 3 -j ACCEPT # dest unreachable
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 5 -j ACCEPT # redirect
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 11 -j ACCEPT # time exceeded
$IPTABLES -A FORWARD -p ICMP -j icmp_packets

#
# UDP ports
#
$IPTABLES -N udpincoming_packets

$IPTABLES -A udpincoming_packets -p UDP -s 0/0 --source-port 53 -j ACCEPT # DNS
$IPTABLES -A udpincoming_packets -p UDP -s 0/0 --source-port 123 -j ACCEPT # ntp
#$IPTABLES -A udpincoming_packets -p UDP -s 0/0 --source-port 2074 -j ACCEPT # speakfreely
#$IPTABLES -A udpincoming_packets -p UDP -s 0/0 --source-port 4000 -j ACCEPT #icq

$IPTABLES -A FORWARD -p UDP -j udpincoming_packets

#

$IPTABLES -N tcp_packets

#
# The allowed chain for TCP connections
#

$IPTABLES -N allowed
$IPTABLES -A allowed -p TCP --syn -j ACCEPT
$IPTABLES -A allowed -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A allowed -p TCP -j DROP

# TCP rules
#

#
# Bad TCP packets we don't want
#

$IPTABLES -A tcp_packets -p tcp ! --syn -m state --state NEW -j LOG --log-prefix "New not syn:"
$IPTABLES -A tcp_packets -p tcp ! --syn -m state --state NEW -j DROP

$IPTABLES -A tcp_packets -p TCP -s 0/0 -d springfield.sparkle-cc.co.uk --dport 80 -j allowed # smtp
$IPTABLES -A tcp_packets -p TCP -s 0/0 -d lisa.sparkle-cc.co.uk --dport 6346 -j allowed # gnutella
$IPTABLES -A tcp_packets -p TCP -s 0/0 -d springfield.sparkle-cc.co.uk --dport 25 -j allowed # smtp

$IPTABLES -A FORWARD -p TCP -j tcp_packets

#
# Input to the firewall itself. Leave these out if you don't want the firewall
# to be visible on the network at all.
# Note that the PREROUTING restrictions above mean that only packets form inside
# the firewall can fulfill the source condition. So the firewall machine should not be
# visible to the internet.
#

$IPTABLES -A INPUT -p ALL -i $BR_IFACE -s $INTERNAL_ADDRESS_RANGE -d $LAN_BCAST_ADDRESS -j ACCEPT
$IPTABLES -A INPUT -p ALL -i $BR_IFACE -s $INTERNAL_ADDRESS_RANGE -d $BR_IP -j ACCEPT

# But you *will* need this

$IPTABLES -A INPUT -p ALL -i $LO_IFACE -d $LO_IP -j ACCEPT

$IPTABLES -A INPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level 7 --log-prefix "IPT INPUT packet died: "

#
# OUTPUT chain
#

$IPTABLES -A OUTPUT -p tcp ! --syn -m state --state NEW -j LOG --log-prefix "New not syn:"
$IPTABLES -A OUTPUT -p tcp ! --syn -m state --state NEW -j DROP

$IPTABLES -A OUTPUT -p ALL -s $LO_IP -j ACCEPT
$IPTABLES -A OUTPUT -p ALL -s $BR_IP -j ACCEPT
$IPTABLES -A OUTPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level 7 --log-prefix "IPT OUTPUT packet died: "