You are on page 1of 8

FreeBSD Network Tutorial/How-To Guide Page 1

A Network Tutorial/How-To Guide


for the FreeBSD OS
by Nick Rogness

Nat and IPFW


Nick Rogness nick@rogness.net

Introduction

One of the most common things to do with FreeBSD is to use it as a gateway to the internet. Most
internet connections have 1 IP assigned to them (via dialup, dhcp, or PPPoE) and have to serve that
connection to the whole network. That means that an entire network must share 1 public IP. How is
this possible? Well, a concept called NAT or Network Address Translation was invented to do this
very thing.

NAT was originally designed to address the lack of IP address space on the internet and also to
relieve IP routing tables.

About NAT

So how does this work? Let's take a look at our network (it's animated so be patient):

http://freebsd.rogness.net/redirect.cgi?basic/nat.html 06/23/2004 11:23:31 PM


FreeBSD Network Tutorial/How-To Guide Page 2

A machine on the Local Ethernet wants to go to the internet, it must traverse the FreeBSD machine
(via their default gateway). It is the job of the FreeBSD machine to route them to the right place.
However, you can see that the IP address of Workstation-1 is 192.168.0.10. The BSD machine must
change that address before it sends the packets out to the internet. In fact, he must change the source
address to an address that is routeable across the internet. Once the FreeBSD machine changes the
address, the packets get sent out on the internet. When the packet returns, the same thing happens,
but instead of changing the source address in the pakcet headers, it changes the destination address
back to 192.168.0.10 and the packet gets sent back to your local workstation-1.

This process is called NAT or sometimes referred to as IP Maquerading. The point is that the client
workstation does not have to know that this process is happening.

The program that does this change is called natd. natd is a userland daemon that runs seperate from
the kernel on your FreeBSD machine.

So How does natd change the packets? Well, that is where ipfw comes in. Through the ipfw `divert`
command packets are sent to natd first, natd changes the packet header information, then the
packets get reinjected back into the "IP packet processing system" and away they go. (See figure 1.1
above).

Installation

http://freebsd.rogness.net/redirect.cgi?basic/nat.html 06/23/2004 11:23:31 PM


FreeBSD Network Tutorial/How-To Guide Page 3
natd comes default with system so you do not need to install it. However, the default kernel does not
have support for "diverting" packets. You have to rebuild the kernel with this support. To do this :

# cd /sys/i386/conf
# cp GENERIC LOCAL
# vi LOCAL

Add the following line into the file:

options IPDIVERT

Save the file. Then type:

# config LOCAL
# cd ../../compile/LOCAL
# make depend && make && make install

You will see a bunch of garbage going across your screen. When it is done:

# vi /etc/rc.conf

Add the following line into this file:

firewall_enable="YES"
firewall_type="OPEN"
gateway_enable="YES"
natd_enable="YES"
natd_interface="xl0"

Now you will need to reboot your system:

# shutdown -r now

Your system will reboot and should come up with natd running. To verify lets see if everythings
setup:

First let's check that the firewall has the proper ruleset running:

# ipfw -a l
00050 1566423 901667271 divert 8668 ip from any to any via xl0
00100 116714 10731910 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
65000 3342945 1813053300 allow ip from any to any
65535 0 0 deny ip from any to any

OK, looks as if the firewall is working. Let's look at what rule 50 above (first line) is doing. It
basically says, "Send any packet incoming or outgoing on interface xl0 to port 8668 on the local
machine".

http://freebsd.rogness.net/redirect.cgi?basic/nat.html 06/23/2004 11:23:31 PM


FreeBSD Network Tutorial/How-To Guide Page 4
Luckily natd is running on port 8668, so natd will get the packets from this line. After natd is done
with the packets, they get reinjected at the next rule...in this case, it happens to be rule 100.

Now let's see if natd is running:

# ps -auxw |grep nat


root 182 0.0 1.4 528 180 ?? Rs 20Apr01 28:23.03 /sbin/natd

WOW! it looks as if its running! The "-n" option tells natd to use the IP assigned to xl0 as the
address to alias packets to. So when packets leave the local network their source address will be the
address assigned to interface xl0. You added this option when you specified "natd_interface=xl0" in
/etc/rc.conf above.

Let's test it out to see if we can get to the internet:

# ping ftp.freebsd.org
PING ftp.freebsd.org (209.180.6.225): 56 data bytes
64 bytes from 209.180.6.225: icmp_seq=0 ttl=240 time=81.597 ms
64 bytes from 209.180.6.225: icmp_seq=1 ttl=240 time=115.910 ms
64 bytes from 209.180.6.225: icmp_seq=2 ttl=240 time=50.444 ms
^C
--- ftp.freebsd.org ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 50.444/82.650/115.910/26.737 ms
#

OMFG! It works...

natd redirection

OK, so natd is up and running, but what if we have a web server or dns server on the inside network.
How can we extend service through the firewall to an internal machine on incoming requests from
the internet? The answer is to supply options to natd to "point" to the right machine and service".
For example, another diagram (hehe):

http://freebsd.rogness.net/redirect.cgi?basic/nat.html 06/23/2004 11:23:31 PM


FreeBSD Network Tutorial/How-To Guide Page 5

Since web server traffic runs on port 80 tcp we need to add the following option to nat:

natd -n xl0 -redirect_port tcp 192.168.0.10:80 80

The redirect_port option given to natd says "send any tcp traffic destined for port 80 to
192.168.0.10 on port 80".

Another option is to send all traffic destined for your outside IP (from the internet) to an internal
machine. This option is called redirect_address. So the following line:

natd -n xl0 -redirect_address 192.168.0.10 20.30.40.50

The redirect_address option given to natd says "send ALL traffic destined for 20.30.40.50 (my
outside IP) to 192.168.0.10". This option is sometimes called static nat, whereas normal nat
operation is sometimes called PAT (Port Address Translation or Overloaded NAT).

Another point of interest for static nat is that internal machines that have a redirect_address option
assigned to them will keep their public IP out on the internet. ie, They will appear to be coming from
the public IP assigned to them by redirect_address when that machine is out on the internet. It's
symmetrical!

These options can be added to /etc/rc.conf so they will stay in effect even after a reboot:

http://freebsd.rogness.net/redirect.cgi?basic/nat.html 06/23/2004 11:23:31 PM


FreeBSD Network Tutorial/How-To Guide Page 6

natd_flags="-redirect_address 192.168.0.10 20.30.40.50"

Customizing natd

There are several options that can be given to natd to make it do really cool stuff. See the natd man
page for more info.

If you look at these options they can be quite long. So I like to put them into a seperate config
file...so let's do it:

Create a file:

# vi /etc/natd.conf

In the file let's put our options:

port 8668
interface xl0
redirect_port tcp 192.168.0.10:80 80
redirect_port tcp 192.168.0.10:53 53
redirect_port udp 192.168.0.10:53 53

Save the file. The above config file says that we want to send web and dns traffic to machine
192.168.0.10 on the local network (Yippie!!). It also has the interface option which was described
earlier and the "port" option which tells natd to listen on port 8668 (the default). You don't have to
include it but I like to anyway as a reference.

Now let's start natd with the newly created configuartion file:

# natd -f /etc/natd.conf

Add this option to /etc/rc.conf so it starts up correctly on a reboot:

natd_flags="-f /etc/natd.conf"

Adding Firewalling with ipfw in conjunction with nat

Before reading this section, please review the firewalling section I wrote. There are several ways to
add firewalling rules to your nat network. However, there is 1 key point to remember:
FreeBSD reinjections diverted packets at the next rule in the firewall. This is an important
concept to remember. Let's look at the base firewall again:

http://freebsd.rogness.net/redirect.cgi?basic/nat.html 06/23/2004 11:23:31 PM


FreeBSD Network Tutorial/How-To Guide Page 7
# ipfw -a l
00050 1566423 901667271 divert 8668 ip from any to any via xl0
00100 116714 10731910 allow ip from any to any via lo0
00200 0 0 deny ip from any to 127.0.0.0/8
65000 3342945 1813053300 allow ip from any to any
65535 0 0 deny ip from any to any

With this ruleset, packets are sent to natd via rule #50. After natd changes the packet header source/
destination, the packet re-enters the firewall at rule #100 "allow ip from any to any via lo0" and
continues through the ruleset until a match is found.

The order in which you add the rules doesn't really matter...before or after the divert rule. If you add
them before the divert rule, you must firewall based on your public address. If you firewall after the
divert rule then you are dealing with your internal addresses.

Adding an ipfw rule before the divert rule where 20.30.40.50 is your public

# ipfw add 40 deny tcp from any to 20.30.40.50 in via xl0

OR

Adding an ipfw rule after the divert rule where 192.168.0.0/24 is your private

# ipfw add 60 deny tcp from any to 192.168.0.0/24 80 in via xl0

Either of the above commands will block tcp port 80 traffic (web traffic) inbound from the internet
to your machine. I would recommend the first command over the first. The reason? There are 2.
First, The second rule may not catch web traffic to your BSD machine itself. Second, to reduce the
load on the natd process.

Why not add rules before the divert rule? Well, it's a good question and here's the answer. Once a
packet hits a match in the firewall ruleset it does not reenter until it leaves another interface. Since
your divert rule is running on the outside interface, which is the last hop before it enters the internet,
your packets will never get diverted!! And yes, they will leave your machine with the real inside IP
address...ACK!

A couple of good rules to follow:

1. You will want to firewall on the PUBLIC IPs on packets incoming from the internet
2. You will want to firewall on the PRIVATE IP(s) on packet leaving to the internet

Let's give an example, Let's say we want to people on the net to be able to hit out webserver which
resides on our internal machine 192.168.0.10, but we want to block all other inbound web requests.
Also, 192.168.0.10 has a static nat entry for 20.30.40.51. The ruleset would look like this:

http://freebsd.rogness.net/redirect.cgi?basic/nat.html 06/23/2004 11:23:31 PM


FreeBSD Network Tutorial/How-To Guide Page 8

# ipfw add 40 allow tcp from any to 20.30.40.51 80 in via xl0


# ipfw add 50 divert natd ip from any to any via xl0
# ipfw add 60 deny tcp from 20.30.40.51 53 to any out via xl0
# ipfw add 70 deny tcp from any to 192.168.0.10 53 in via xl0

Note that rule #60 above is not necessarily needed and should never get hit (unless you are doing
zone transfers outside your network) but it was put in there to prove a point. That our can do it.

conclusion

Well there you have it. The combination of natd and ipfw can prove to be a powerful additive to the
FreeBSD OS. You may also want to check out the ipfw and natd man page for more advanced
needs. Enjoy!

http://freebsd.rogness.net/redirect.cgi?basic/nat.html 06/23/2004 11:23:31 PM

You might also like