<!doctype linuxdoc system>

<article>

<title>Linux Bridge+Firewall Mini-HOWTO version 1.2.0</title>
<author>Peter Breuer (<htmlurl url="mailto:ptb@it.uc3m.es"
name="ptb@it.uc3m.es">)</author>
<date>v, 19 December 1997

<toc>

<sect><heading>Introduction<LABEL ID="Introduction">

<p>
You should look at the original
<url url="ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/mini/Bridge"
name="Bridging mini-HOWTO"> by Chris Cole for a different perspective on this.
He is <htmlurl url="mailto:chris@polymer.uakron.edu"
name="chris@polymer.uakron.edu">.  The version of his HOWTO
that I have based this document on (alternatively, ripped off) is 1.03
dated Aug 23 1996.


<sect><heading>What and Why (and How?)<LABEL ID="What and Why (and How?)">

<p>

<sect1><heading>What<LABEL ID="What">

<p>
A bridge is an intelligent connecting wire betwen two network cards.
A firewall is an intelligent insulator.

<sect1><heading>Why<LABEL ID="Why">

<p>You might want a bridge if you have several computers:

<enum>
<item><LABEL ID="bridge1">to save the price of a new hub when you just happen
to have an extra ethernet card available.

<item><LABEL ID="bridge2"> to save the bother of learning how to do
IP-forwarding and other tricks when you _have_ two cards in your
computer.

<item><LABEL ID="bridge3"> to avoid maintenance work in the future when
things change around!

</enum>

<p>
``Several computers'' might be as few as three if those are
routing or bridging or just moving around the room from time to time! You
also might want a bridge just for the fun of finding out what it does.
<REF ID="bridge2" NAME="2"> was what I wanted a bridge for. 

<p>
If you are really interested in <REF ID="bridge1" NAME="1">, you have to be one of the
very few.  Check the <url
url="ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/NET-2-HOWTO"
name="NET-2-HOWTO"> and the <url
url="ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/Serial-HOWTO"
name="Serial-HOWTO"> for better tricks.

<p>You want a firewall if

<enum>
<item> you are trying to protect your network from
external accesses, or<LABEL ID="firewall1">

<item> you are trying to deny access to the world
outside from your network.<LABEL ID="firewall2">

</enum>

<p>
Curiously, I needed <REF ID="firewall2" NAME="2"> here too.  Policy at my university
presently is that we should not act as internet service providers to
undergraduates.

<sect1><heading>How?<LABEL ID="How?">

<p>
I started out bridging the network cards in a firewalling machine
and ended up firewalling without having cut the bridge. It seems to work
and is more flexible than either configuration alone. I can take down the
firewall and keep bridging or take down the bridge when I want to be more
circumspect.

<p>I would guess that the bridge code lives just above the physical device
layer and the firewalling code lives one layer higher up, so that the bridging
and firewalling configurations effectively act as though they are running
connected together ``in sequence'' and not ``in parallel''
(ouch!). Diagram: 

<tscreen><verb>
-&gt; Bridge-in -&gt; Firewall-in -&gt; Kernel -&gt; Firewall-out -&gt; Bridge-out -&gt;
</verb></tscreen>

<p>There is no other way to explain how one machine can be a
``conductor'' and an ``insulator'' at the same time.  There are a few
caveats but I'll come to those later.  Basically you must route packets
that you want to firewall.  Anyway, it all seems to work together nicely
for me.  Here is what you do ...

<sect><heading>BRIDGING<LABEL ID="BRIDGING">

<sect1><heading>Software<LABEL ID="Software">

<p>
Get the <url url="ftp://shadow.cabi.net/pub/Linux/BRCFG.tgz"
name="bridge configuration utility"> from Alan Cox's home pages. This
is the same reference as in Chris' document. I just didn't realize that
it was an ftp and not an http URL ... 


<sect1><heading>Prior Reading.<LABEL ID="Prior Reading">

<p>Read the <url
url="ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/mini/Multiple-Ethernet"
name="Multiple Ethernet HOWTO"> for some advice on getting more than
one network card recognized and configured.

<p>Yet more details of the kind of boot magic that you may need are in
the <url
url="ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/BootPrompt-HOWTO"
name="Boot Prompt HOWTO">.

<p>You may be able to get away without the <url
url="ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/NET-2-HOWTO"
name="NET-2 HOWTO">. It is a good long
read and you will have to pick from it the details you need. 

<sect1><heading>Boot configuration<LABEL ID="Boot configuration">

<p>The reading material above will tell you that you need to prepare the
kernel to recognize a second ethernet device at boot up by adding this
to your <bf>/etc/lilo.conf</bf>, and then re-run <bf>lilo</bf>: 

<tscreen><verb>append = &quot;ether=0,0,eth1&quot; </verb></tscreen>

<p>Note the &quot;eth1&quot;. &quot;eth0&quot; is the first card. &quot;eth1&quot;
is the second card. You can always add the boot parameters in your response
to the line that lilo offers you. This is for three cards: 

<tscreen><verb>linux ether=0,0,eth1 ether=0,0,eth2 </verb></tscreen>

<p>I use <bf>loadlin</bf> to boot my kernel from DOS: 

<tscreen><verb>loadlin.exe c:\vmlinuz root=/dev/hda3 ro ether=0,0,eth1 ether=0,0,eth2 </verb></tscreen>

<p>Note that this trick makes the kernel probe at bootup. That will not
happen if you load the ethernet drivers as <bf>modules</bf> (for safety since
the probe order can't be determined) so if you use modules you will have
to add the appropriate IRQ and port parameters for the driver in your <bf>/etc/conf.modules</bf>.
I have at least 

<tscreen> <verb>
alias eth0 3c509
alias eth1 de620
options 3c509 irq=5 io=0x210
options de620 irq=7 bnc=1
</verb> </tscreen>

<p>You can tell if you use modules by using ``ps -aux'' to see
if <bf>kerneld </bf>is running and checking that there are .o files in a
subdirectory of your <bf>/lib/modules</bf> directory. You want the directory
named with what uname -r tells you. If you have kerneld and/or you have
a foo.o then edit <bf>/etc/conf.modules</bf> and read the man page for depmod
carefully. 

<p>Note also that until recently (kernel 2.0.25) the <bf>3c509</bf> driver
could not be used for more than one card if used as a module. I have seen
a patch floating around that fixes the oversight. It may be in the kernel
when you read this. 

<sect1><heading>Kernel configuration <LABEL ID="Kernel configuration">

<p>Recompile the kernel with bridging enabled. 

<tscreen><verb>CONFIG_BRIDGE=y </verb></tscreen>

<p>I also compiled with firewalling and IP-forwarding and -masquerading
and the rest enabled. Only if you want firewalling too ... 

<tscreen><verb>CONFIG_FIREWALL=y           
CONFIG_NET_ALIAS=y          
CONFIG_INET=y               
CONFIG_IP_FORWARD=y         
CONFIG_IP_MULTICAST=y       
CONFIG_IP_FIREWALL=y        
CONFIG_IP_FIREWALL_VERBOSE=y
CONFIG_IP_MASQUERADE=y      </verb></tscreen>


<p>You don't need all of this. What you do need apart from this is the
standard net configuration: 

<tscreen><verb>CONFIG_NET=y </verb></tscreen>

<p>and I do not think you need worry about any of the other networking
options. I have any options that I did not actually compile into the kernel
available through kernel modules that I can add in later. 

<p>Install the new kernel in place, rerun lilo and reboot with the new
kernel. Nothing should have changed at this point! 


<sect1><heading>Network addresses <LABEL ID="Network addresses">

<p>Chris says that a bridge should not have an IP address but that is not
the setup to be described here. 

<p>You are going to want to use the machine for connecting to the net so
you need an address and you need to make sure that you have the loopback
device configured in the normal way so that your software can talk to the
places they expect to be able to talk to. If loopback is down the name
resolver or other net sevices might fail. See the NET-2-HOWTO, but your
standard configuration should already have done this bit: 

<tscreen><verb>ifconfig lo 127.0.0.1 route add -net 127.0.0.0 </verb></tscreen>

<p>You will have to give addresses to your network cards. I altered
the /etc/rc.d/rc.inet1 file in my slackware (3.x) to setup two cards
and you should also essentially just look for your net configuration file
and double or treble the number of instructions in it.  Suppose that you
already have an address at

<tscreen><verb>192.168.2.100 </verb></tscreen>

<p>(that is in the private net reserved address space, but never mind - it
won't hurt anybody if you use this address by mistake) then you probably
already have a line like 

<tscreen><verb>ifconfig eth0 192.168.2.100 netmask 255.255.255.0 metric 1 </verb></tscreen>

<p>in your configuration. The first thing you are going to probably want
to do is cut the address space reached by this card in half so that you
can eventually bridge or firewall the two halves.  So add a line which
reduces the mask to address a smaller number of machines:

<tscreen><verb>ifconfig eth0 netmask 255.255.255.128 </verb></tscreen>

<p>Try it too. That restricts the card to at most the address space between
.0 and .127. 

<p>Now you can set your second card up in the other half of the local address
space. Make sure that nobody already has the address. For symmetry I set
it at 228=128+100 here. Any address will do so long as it is not in the
other card's mask, and even then, well, maybe. Avoid special addresses
like .0, .1, .128 etc. unless you really know what you are doing. 

<tscreen><verb>ifconfig eth1 192.168.2.228 netmask 255.255.255.128 metric 1 </verb></tscreen>

<p>That restricts the second card to addresses between .128 and .255. 

<sect1><heading>Network routing <LABEL ID="Network routing">

<p>This is where I have to announce the caveats in the bridging +
firewalling scheme: you cannot firewall packets which are not routed.
No routes, no firewall. At least this appears to be true in the 2.0.30
and more recent kernels. The firewalling filters are closely involved
with the ip-forwarding code.

<p>That does not mean that you cannot bridge. You can bridge between two
cards and firewall them from a third. You can have only two cards
and firewall both of them against an outside IP such as a nearby router,
provided that the router is routed by you to exactly one of the
cards.

<p>In other words, since I will be doing firewalling, so I want to precisely
control the physical destination of some packets. 

<p>I have the small net of machines attached to a hub hanging off eth0,
so I configure a net there: 

<tscreen><verb>route add -net 192.168.2.128 netmask 255.255.255.128 dev eth0 </verb></tscreen>

<p>The 128 would be 0 if I had a full class C network there.  I don't,
by definition, since I just halved the address space.  The &quot;dev
eth0&quot; is not necessary here because the cards address falls within
the mask, but it may be necessary for you.  One might need more than one
card holding up this subnet (127 machines on one segment, oh yeah) but
those cards would be being bridged under the same netmask so that they
appear as one to the routing code.

<p>On the other card I have a line going straight through to a big router
that I trust. 

<verb>                                             client 129
         __                                        |    __ 
client 1   \    .0                    .128         |   /   net 1
client 2 --- Hub - eth0 - Kernel - eth1 - Hub - Router --- net 2
client 3 __/       .100            .228         .2 |   \__ net 3
                                                   |
                                             client 254 </verb>

<p>I attach the address of the router to that card as a fixed
(&quot;static&quot;) route because it would otherwise fall within the
first cards netmask and the kernel would be thinking wrongly about how
to send packets to the big router. I will want to firewall these packets
and that is another reason fow wanting to route them specifically.

<tscreen><verb>route add 192.168.2.2 dev eth1 </verb></tscreen>

<p>I don't need it, since I don't have any more machines in that half of
the address space, but I declare a net also on the second card.
Separating my interfaces into two sets via routing will allow me to do
very tight firewalling eventually , but you can get away with far less
routing than this.

<tscreen><verb>route add -net 192.168.2.128 netmask 255.255.255.128 dev eth1 </verb></tscreen>


<p>I also need to send all non-local packets out to the world so I tell
the kernel to send them to the big router 

<tscreen><verb>route add default gw 192.168.2.2 </verb></tscreen>

<sect1><heading>Card configuration <LABEL ID="Card configuration">

<p>So much was standard networking setup, but we are bridging so we also
have to listen on both (?) cards for packets that are not aimed at us.
The following should go into the network configuration file. 

<tscreen><verb>ifconfig promisc eth0 ifconfig promisc eth1 </verb></tscreen>

<p>The man page says allmulti=promisc, but it didn't work for me. 

<sect1><heading>Additional routing <LABEL ID="Additional routing">

<p>One thing that I noticed was that I had to put at least the second card
into a mode where it would respond to the big router's questions about
which machines I was hiding in my local net. 

<tscreen><verb>ifconfig arp eth1 </verb></tscreen>

<p>For good measure I did this to the other card too. 

<tscreen><verb>ifconfig arp eth0. </verb></tscreen>

<sect1><heading>Bridge Configuration <LABEL ID="Bridge Configuration">

<p>Put bridging enabling on and into your configuration file: 

<tscreen><verb>brcfg -enable </verb></tscreen>

<p>You should have been trying this out in real time all along, of course!
The bridge configure will bring up some numbers. You can experiment with
turning on and off the ports one at a time 

<tscreen><verb>brcfg -port 0 -disable/-enable
 brcfg -port 1 -disable/-enable </verb></tscreen>

<p>You get status reports anytime by just running 

<tscreen><verb>brcfg </verb></tscreen>

<p>without any parameters. You will see that the bridge listens,learns,
and then does forwarding. (I don't understand why the code repeats the
same hardware addresses for both my cards, but never mind .. Chris' howto
say that is OK) 

<sect1><heading>Try it out <LABEL ID="Try it out">

<p>If you are still up and running as things are, try out your configuration
script for real by taking down both cards and then executing it: 

<tscreen><verb>ifconfig eth0 down ifconfig eth1 down /etc/rc.d/rc.inet1 </verb></tscreen>

<p>With any luck the various subsystems (<bf>nfs</bf>, <bf>ypbind</bf>, etc.)
won't notice. <it>Do not try this unless you are sitting at the keyboard!
</it>

<p>If you want to be more careful than this, you should take down as many
daemons as possible beforehand, and unmount nfs directories. The worst
that can happen is that you have to reboot in single-user mode (the &quot;<bf>single</bf>&quot;
parameter to <bf>lilo </bf>or <bf>loadlin</bf>), and take out your changes
before rebooting with things the way they were before you started. 

<sect1><heading>Checks

<p>Verify that there is different traffic on each interface: 

<tscreen>
<verb>tcpdump -i eth0</verb> (in one window)
<verb>tcpdump -i eth1</verb> (in another window)
</tscreen>

<p>You should get used to using <bf>tcpdump</bf> to look for things that
should not be happening or that are happening and should not. 

<p>For instance look for packets that have gone through the bridge to the
second card from the internal net. Here I am looking for packets from the
machine with address .22: 

<tscreen><verb>
tcpdump -i eth1 -e host 192.168.2.22
</verb></tscreen>

<p>Then send a ping from the .22 host to the router. You should see the
packet reported by tcpdump. 

<p>At this stage you should have a bridge ready that also has two
network addreses.  Test that you can ping them from outside and inside
your local net, and that you can telnet and ftp around between inside
and outside too.

<sect><heading>FIREWALLING<LABEL ID="FIREWALLING">

<sect1><heading>Software and reading <LABEL ID="Software and reading">

<p>You should read the <url
url="ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/Firewall-HOWTO"
name="Firewall-HOWTO">. 

<p>That will tell you where to get <bf>ipfwadm</bf> if you don't already
have it. There are other tools you can get but I made no progress until
I tried <bf>ipfwadm</bf>. It is nice and low level! You can see exactly what
it is doing. 

<sect1><heading>Preliminary checks <LABEL ID="Preliminary checks">

<p>You have compiled IP-forwarding and masquerading into the kernel so
you will want to check that the firewall is in its default (accepting)
state with 

<tscreen><verb>ipfwadm -I -l ipfwadm -O -l ipfwadm -F -l </verb></tscreen>

<p>That is respectively, &quot;display the rules affecting the ..&quot;
incoming or outgoing or forwarding (masquerading) &quot;.. sides of the
firewall&quot;. The &quot;-l&quot; means &quot;list&quot;. 

<p>You might have compiled in accounting too: 

<tscreen><verb>ipfwadm -A -l </verb></tscreen>

<p>You should see that there are no rules defined and that the default
is to accept every packet. You can get back to this working state anytime
with 

<tscreen><verb>ipfwadm -I -f
ipfwadm -O -f
ipfwadm -F -f </verb></tscreen>

<p>The &quot;-f&quot; means &quot;flush&quot;. You may need to use that.


<sect1><heading>Default rule <LABEL ID="Default rule">

<p>I want to cut the world off from my internal net and do nothing else,
so I will want to give as a last (default) rule that the firewall should
ignore any packets coming in from the internal net and directed to outside.
I put all the rules (in this order) into <bf>/etc/rc.d/rc.firewall </bf>and
execute it from <bf>/etc/rc.d/rc.local</bf> at bootup. 

<tscreen><verb>ipfwadm -I -a reject -S 192.168.2.0/255.255.255.128 -D 0.0.0.0/0.0.0.0 </verb></tscreen>

<p>The &quot;-S&quot; is the source address/mask. The &quot;-D&quot; is
the destination address/mask. 

<p>This format to is rather long-winded. <bf>Ipfwadm</bf>
is intelligent about network names and some common abbreviations. Check
the man pages. 

<p>It is possibly more convenient to put some or all of these rules on
the outgoing half of the firewall by using &quot;-O&quot; instead of &quot;-I&quot;,
but I'll state the rules here all formulated for the incoming half. 

<sect1><heading>Holes per address <LABEL ID="Holes per address">

<p>Before that default rule, I have to place some rules that serve as exceptions
to this general denial of external services to internal clients. 

<p>I want to treat the firewall machines address on the internal net specially.
I will stop people logging in to the firewall machine unless they have
special permission, but once they are there they should be allowed to talk
to the world. 

<tscreen><verb>ipfwadm -I -i accept -S 192.168.2.100/255.255.255.255 \
 -D 0.0.0.0/0.0.0.0 </verb></tscreen>

<p>I also want the internal clients to be able to talk to the firewalling
machine. Maybe they can persuade it to let them get out! 

<tscreen><verb>ipfwadm -I -i accept -S 192.168.2.0/255.255.255.128 \
 -D 192.168.2.100/255.255.255.255 </verb></tscreen>

<p>Check at this point that you can get in to the clients from outside
the firewall via <bf>telnet</bf>, but that you cannot get out. That should
mean that you can just about make first contact, but the clients cannot
send you any prompts. You should be able to get all the way in if you use
the firewall machine as a staging post. Try <bf>rlogin</bf> and <bf>ping </bf>too,
with <bf>tcpdump</bf> running on one card or the other. You should be able
to make sense of what you see. 

<sect1><heading>Holes per protocol <LABEL ID="Holes per protocol">

<p>I went on to relax the rules protocol by protocol. I want to allow pings
from the outside to the inside to get an echo back, for instance, so I
inserted the rule: 

<tscreen><verb>ipfwadm -I -i accept -P icmp -S 192.168.2.0/255.255.255.128 \
 -D 0.0.0.0/0.0.0.0 </verb></tscreen>

<p>The &quot;<TT>-P icmp</TT>&quot; works the protocol-specific magic.


<p>Until I get hold of an <bf>ftp</bf> proxy I am also allowing ftp calls
out with port-specific relaxations. This targets ports 20 21 and 115 on
outside machines. 

<tscreen><verb>ipfwadm -I -i accept -P tcp -S 192.168.2.0/255.255.255.128 \
 -D 0.0.0.0/0.0.0.0 20 21 115 </verb></tscreen>

<p>I could not make <bf>sendmail</bf> between the local clients work without
a nameserver. Rather than set up a nameserver right then on the firewall,
I just lifted the firewall for tcp domain service queries precisely aimed
at the nearest existing nameserver and put its address in the clients <bf>/etc/resolv.conf</bf>
(&quot;<TT>nameserver 123.456.789.31</TT>&quot; on a separate line). 

<tscreen><verb>ipfwadm -I -i accept -P tcp -S 192.168.2.0/255.255.255.128 \
 -D 123.456.789.31/255.255.255.255 54 </verb></tscreen>

<p>You can find which port number and protocol a service requires with
<bf>tcpdump</bf>. Trigger the service with a an <bf>ftp</bf> or a <bf>telnet</bf>
or whatever to or from the internal machine and then watch for it on the
input and output ports of the firewall with <bf>tcpdump</bf>: 

<tscreen><verb>tcpdump -i eth1 -e host client04 </verb></tscreen>

<p>for example. The <bf>/etc/services </bf>file is another important source
of clues. To let <bf>telnet</bf> and <bf>ftp</bf> IN to the firewall from outside,
you have to allow the local clients to call OUT on a specific port. I understand
why this is necessary for <bf>ftp</bf> - it's the server that establishes
the data stream in the end - but I am not sure why <bf>telnet</bf> also needs
this. 

<tscreen><verb>ipfwadm -I -i accept -P tcp -S 192.168.2.0/255.255.255.128 ftp telnet \
 -D 0.0.0.0/0.0.0.0 </verb></tscreen>

<p>There is a particular problem with some daemons that look up the hostname
of the firewalling machine in order to decide what is their networking
address. <bf>Rpc.yppasswdd</bf> is the one I had trouble with. It insists
on broadcasting information that says it is outside the firewall (on the
second card). That means the clients inside can't contact it. 

<p>Rather than start IP aliasing or change the daemon code, I mapped the
name to the inside card address on the clients in their <bf>/etc/hosts</bf>.


<sect1><heading>Checks

<p>You want to test that you can still <bf>telnet</bf>, <bf>rlogin </bf>and
<bf>ping</bf> from the outside. From the inside you should be able to <bf>ping</bf>
out. You should also be able to <bf>telnet</bf> to the firewall machine from
the inside and the latter should be able to do anything. 

<p>That is it. At this point you probably want to learn about <bf>rpc</bf>/<bf>Y</bf>ellow
<bf>P</bf>ages and the interaction with the password file. The firewalled
network wants to run without its unprivileged users being able to log on
to the firewall - and thus get out. Some other HOWTO! 

</article>
