FreeBSD’s built-in NAT
daemon, natd(8)
, works in conjunction with IPFW
to provide network address translation.
This can be used to provide an Internet Connection Sharing solution so that several internal computers can connect to the Internet using a single IP
address.
To do this, the FreeBSD machine connected to the Internet must act as a gateway.
This system must have two NIC
s, where one is connected to the Internet and the other is connected to the internal LAN
.
Each machine connected to the LAN
should be assigned an IP
address in the private network space, as defined by RFC
1918, and have the default gateway set to the natd(8)
system’s internal IP
address.
Some additional configuration is needed in order to activate the NAT
function of IPFW
.
If the system has a custom kernel, the kernel configuration file needs to include option IPDIVERT
along with the other IPFIREWALL
options described in Enabling IPFW.
To enable NAT
support at boot time, the following must be in /etc/rc.conf
:
gateway_enable="YES" # enables the gateway
natd_enable="YES" # enables NAT
natd_interface="rl0" # specify interface name of NIC attached to Internet
natd_flags="-dynamic -m" # -m = preserve port numbers; additional options are listed in natd8
|
It is also possible to specify a configuration file which contains the options to pass to natd(8)
:
natd_flags="-f /etc/natd.conf"
The specified file must contain a list of configuration options, one per line.
For example:
redirect_port tcp 192.168.0.2:6667 6667
redirect_port tcp 192.168.0.3:80 80
For more information about this configuration file, consult natd(8)
.
|
Next, add the NAT
rules to the firewall ruleset.
When the rulest contains stateful rules, the positioning of the NAT
rules is critical and the skipto
action is used.
The skipto
action requires a rule number so that it knows which rule to jump to.
The following example builds upon the firewall ruleset shown in the previous section.
It adds some additional entries and modifies some existing rules in order to configure the firewall for NAT
.
It starts by adding some additional variables which represent the rule number to skip to, the keep-state
option, and a list of TCP
ports which will be used to reduce the number of rules:
#!/bin/sh
ipfw -q -f flush
cmd="ipfw -q add"
skip="skipto 500"
pif=dc0
ks="keep-state"
good_tcpo="22,25,37,53,80,443,110"
The inbound NAT
rule is inserted after the two rules which allow all traffic on the trusted internal interface and on the loopback interface and before the check-state
rule.
It is important that the rule number selected for this NAT
rule, in this example 100
, is higher than the first two rules and lower than the check-state
rule:
$cmd 005 allow all from any to any via xl0 # exclude LAN traffic
$cmd 010 allow all from any to any via lo0 # exclude loopback traffic
$cmd 100 divert natd ip from any to any in via $pif # NAT any inbound packets
# Allow the packet through if it has an existing entry in the dynamic rules table
$cmd 101 check-state
The outbound rules are modified to replace the allow
action with the $skip
variable, indicating that rule processing will continue at rule 500
.
The seven tcp
rules have been replaced by rule 125
as the $good_tcpo
variable contains the seven allowed outbound ports.
# Authorized outbound packets
$cmd 120 $skip udp from any to x.x.x.x 53 out via $pif $ks
$cmd 121 $skip udp from any to x.x.x.x 67 out via $pif $ks
$cmd 125 $skip tcp from any to any $good_tcpo out via $pif setup $ks
$cmd 130 $skip icmp from any to any out via $pif $ks
The inbound rules remain the same, except for the very last rule which removes the via $pif
in order to catch both inbound and outbound rules.
The NAT
rule must follow this last outbound rule, must have a higher number than that last rule, and the rule number must be referenced by the skipto
action.
In this ruleset, rule number 500
diverts all packets which match the outbound rules to natd(8)
for NAT
processing.
The next rule allows any packet which has undergone NAT
processing to pass.
$cmd 499 deny log all from any to any
$cmd 500 divert natd ip from any to any out via $pif # skipto location for outbound stateful rules
$cmd 510 allow ip from any to any
In this example, rules 100
, 101
, 125
, 500
, and 510
control the address translation of the outbound and inbound packets so that the entries in the dynamic state table always register the private LAN
IP
address.
Consider an internal web browser which initializes a new outbound HTTP
session over port 80.
When the first outbound packet enters the firewall, it does not match rule 100
because it is headed out rather than in.
It passes rule 101
because this is the first packet and it has not been posted to the dynamic state table yet.
The packet finally matches rule 125
as it is outbound on an allowed port and has a source IP
address from the internal LAN
.
On matching this rule, two actions take place.
First, the keep-state
action adds an entry to the dynamic state table and the specified action, skipto rule 500
, is executed.
Next, the packet undergoes NAT
and is sent out to the Internet.
This packet makes its way to the destination web server, where a response packet is generated and sent back.
This new packet enters the top of the ruleset.
It matches rule 100
and has its destination IP
address mapped back to the original internal address.
It then is processed by the check-state
rule, is found in the table as an existing session, and is released to the LAN
.
On the inbound side, the ruleset has to deny bad packets and allow only authorized services.
A packet which matches an inbound rule is posted to the dynamic state table and the packet is released to the LAN
.
The packet generated as a response is recognized by the check-state
rule as belonging to an existing session.
It is then sent to rule 500
to undergo NAT
before being released to the outbound interface.