So, our task is to enable so-called masquerading (or NAT - Network Address Translation) in FreeBSD using ipfw. In our example, we'll enable NAT based on the natd daemon, without dwelling on kernel NAT due to its problems (as of FreeBSD 8.1) with long configuration lines.
NATd in FreeBSD is, in fact, built in out of the box and nothing extra needs to be installed.
1) Enable NAT in rc.conf
Edit /etc/rc.conf and insert the following lines into it:
natd_enable="YES"
natd_flags="-f /usr/local/etc/natd.conf"
natd_interface="xl0"
Here:
- natd_enabled="YES" : enables NAT
- natd_flags="-f /usr/local/etc/natd.conf" : where to get the config file for NATd from
- natd_interface="xl0" : here, instead of xl0, specify the name of the interface facing outward
2) Create the natd.conf file itself
Go to /usr/local/etc and create a file named natd.conf with the following content:
same_ports
use_sockets
Note - an empty line at the end of the file is required!
There's nothing special being told to NATd in this file - just that it should try to use the same outgoing ports as the private client, and that it should use sockets.
3) Rules in ipfw
Now we need to configure ipfw to redirect all packets leaving through the external interface toward the internet, and all packets arriving on the external interface addressed to the router itself, to the natd daemon.
Here is the necessary piece of code:
oif="xl0"
inet="192.168.0.0/16"
oip="11.22.33.44"
### NAT
# Outgoing traffic to the internet - pass it through NAT
${fwcmd} add divert natd ip from ${inet} to any out via ${oif}
# Incoming traffic to ${oip} - i.e., to the router's own IP address
# also route into NATD - for de-masquerading
${fwcmd} add divert natd ip from any to ${oip} in via ${oif}
Here you need to define the following variables:
- oif : the name of the network card in FreeBSD that "faces" outward - to the internet. In our example this is "xl0";
- inet : the definition of the internal (private) network addressing. I.e., the network that sits behind NAT in your organization. In our example: 192.168.0.0/16, i.e., everything with an IP starting with 192.168.
- oip : the IP address of the "external" network interface (oif, which in our example is xl0). I.e., this is your router's "public" address. In our example - made up - "11.22.33.44"
This piece of code needs to be inserted before defining all the permissions and restrictions for the internal network. Here's the same piece, but "extracted" from the default config - showing where it needs to go.
#!/bin/sh
# Command for talking to ipfw
fwcmd="/sbin/ipfw -q"
# eth0 - this is the internal network interface
# with IP address 192.168.0.1
iip="192.168.0.1"
imask="255.255.255.0"
inet="192.168.0.0"
iif="eth0"
# eth1 - this is the internet-facing interface
# IP, mask, subnet, etc. - purely as an example
oip="11.22.33.44"
omask="255.255.255.0"
onet="11.22.33.0"
oif="eth1"
# Clear the current rule set
${fwcmd} -f flush
### Rules before NAT
# There should be no traffic with private IPs through eth1
${fwcmd} add deny all from 10.0.0.0/8 to any in via ${oif}
${fwcmd} add deny all from 192.168.0.0/16 to any in via ${oif}
${fwcmd} add deny all from any to 10.0.0.0/8 via ${oif}
${fwcmd} add deny all from any to 172.16.0.0/12 via ${oif}
${fwcmd} add deny all from any to 192.168.0.0/16 via ${oif}
${fwcmd} add deny all from any to 0.0.0.0/8 via ${oif}
${fwcmd} add deny all from any to 169.254.0.0/16 via ${oif}
${fwcmd} add deny all from any to 192.0.2.0/24 via ${oif}
${fwcmd} add deny all from any to 224.0.0.0/4 via ${oif}
${fwcmd} add deny all from any to 240.0.0.0/4 via ${oif}
# And on the internal interface - where would external traffic come from anyway
${fwcmd} add deny all from ${onet}:${omask} to any in via ${iif}
# Allow traffic to flow within our networks
${fwcmd} add allow all from ${inet}:${imask} to ${inet}:${imask}
### NAT
# Outgoing traffic to the internet - pass it through NAT
${fwcmd} add divert natd ip from ${inet}:${imask} to any out via ${oif}
# Incoming traffic to ${oip} - i.e., to the router's own IP address
# also route into NATD - for de-masquerading
${fwcmd} add divert natd ip from any to ${oip} in via ${oif}
### Rules after NAT
# All traffic originating from this router is allowed. This same rule
# fires during masquerading - if it is disabled, nothing will go out to
# the internet after NAT
${fwcmd} add allow all from me to any
# Don't inspect packets belonging to an already established, and thus
# already checked, TCP connection
${fwcmd} add allow all from any to any established
# Deny long fragmented echo requests
${fwcmd} add deny icmp from any to any frag
# Allow the second and subsequent parts of fragmented packets to pass through
# (their headers have already been checked)
${fwcmd} add allow all from any to any frag
...
...
...
4) Reboot the router and get a working NAT that successfully masquerades the internal subnets, presenting them as its own public IP address for traffic to the internet.
$ sudo reboot
if you only need to restart natd
then
$ sudo natd restart
ATTENTION! Be very careful with IPFW (the firewall) - ideally, be right in front of the router's console while performing such actions. Because if you mess something up somewhere, there's a real chance you'll lose the ability to reconnect to the router to fix your mistakes.
P.S. I've already written about how to set up port forwarding with this kind of NAT
Applies to: FreeBSD 6.x and above
Comments