[Previous: Packet Filtering] [Contents] [Next: Traffic Redirection (Port Forwarding)]

PF: Network Address Translation (NAT)

Table of Contents


Network Address Translation (NAT) is a way to map an entire network (or networks) to a single IP address. NAT is necessary when the number of IP addresses assigned to you by your Internet Service Provider is less than the total number of computers that you wish to provide Internet access for. NAT is described in RFC 1631, "The IP Network Address Translator (NAT)."

NAT allows you to take advantage of the reserved address blocks described in RFC 1918, "Address Allocation for Private Internets." Typically, your internal network will be setup to use one or more of these network blocks. They are: ( - ( - ( -

An OpenBSD system doing NAT will have at least two network adapters, one to the Internet, the other to your internal network. NAT will be translating requests from the internal network so they appear to all be coming from your OpenBSD NAT system.

How NAT Works

When a client on the internal network contacts a machine on the Internet, it sends out IP packets destined for that machine. These packets contain all the addressing information necessary to get them to their destination. NAT is concerned with these pieces of information:

When the packets pass through the NAT gateway they will be modified so that they appear to be coming from the NAT gateway itself. The NAT gateway will record the changes it makes in its state table so that it can a) reverse the changes on return packets and b) ensure that return packets are passed through the firewall and are not blocked. For example, the following changes might be made:

Neither the internal machine nor the Internet host is aware of these translation steps. To the internal machine, the NAT system is simply an Internet gateway. To the Internet host, the packets appear to come directly from the NAT system; it is completely unaware that the internal workstation even exists.

When the Internet host replies to the internal machine's packets, they will be addressed to the NAT gateway's external IP ( at the translation port (53136). The NAT gateway will then search the state table to determine if the reply packets match an already established connection. A unique match will be found based on the IP/port combination which tells PF the packets belong to a connection initiated by the internal machine PF will then make the opposite changes it made to the outgoing packets and forward the reply packets on to the internal machine.

Translation of ICMP packets happens in a similar fashion but without the source port modification.

NAT and Packet Filtering

NOTE: Translated packets must still pass through the filter engine and will be blocked or passed based on the filter rules that have been defined. The only exception to this rule is when the pass keyword is used within the nat rule. This will cause the NATed packets to pass right through the filtering engine.

Also be aware that since translation occurs before filtering, the filter engine will see the translated packet with the translated IP address and port as outlined in How NAT Works.

IP Forwarding

Since NAT is almost always used on routers and network gateways, it will probably be necessary to enable IP forwarding so that packets can travel between network interfaces on the OpenBSD machine. IP forwarding is enabled using the sysctl(3) mechanism:

# sysctl net.inet.ip.forwarding=1
# sysctl net.inet6.ip6.forwarding=1 (if using IPv6)

To make this change permanent, the following lines should be added to /etc/sysctl.conf:


These lines are present but commented out (prefixed with a #) in the default install. Remove the # and save the file. IP forwarding will be enabled when the machine is rebooted.

Configuring NAT

The general format for NAT rules in pf.conf looks something like this:
nat [pass] [log] on interface [af] from src_addr [port src_port] to \
   dst_addr [port dst_port] -> ext_addr [pool_type] [static-port]
The keyword that begins a NAT rule.
Causes translated packets to completely bypass the filter rules.
Log matching packets via pflogd(8). Normally only the first packet that matches will be logged. To log all matching packets, use log (all).
The name or group of the network interface to translate packets on.
The address family, either inet for IPv4 or inet6 for IPv6. PF is usually able to determine this parameter based on the source/destination address(es).
The source (internal) address of packets that will be translated. The source address can be specified as:
The source port in the Layer 4 packet header. Ports can be specified as: The port option is not usually used in nat rules because the goal is usually to NAT all traffic regardless of the port(s) being used.
The destination address of packets to be translated. The destination address is specified in the same way as the source address.
The destination port in the Layer 4 packet header. This port is specified in the same way as the source port.
The external (translation) address on the NAT gateway that packets will be translated to. The external address can be specified as:
Specifies the type of address pool to use for translation.
Tells PF not to translate the source port in TCP and UDP packets.

This would lead to a most basic form of this line similar to this:

nat on tl0 from to any ->

This rule says to perform NAT on the tl0 interface for any packets coming from and to replace the source IP address with

While the above rule is correct, it is not recommended form. Maintenance could be difficult as any change of the external or internal network numbers would require the line be changed. Compare instead with this easier to maintain line (tl0 is external, dc0 internal):

nat on tl0 from dc0:network to any -> tl0

The advantage should be fairly clear: you can change the IP addresses of either interface without changing this rule.

When specifying an interface name for the translation address as above, the IP address is determined at pf.conf load time, not on the fly. If you are using DHCP to configure your external interface, this can be a problem. If your assigned IP address changes, NAT will continue translating outgoing packets using the old IP address. This will cause outgoing connections to stop functioning. To get around this, you can tell PF to automatically update the translation address by putting parentheses around the interface name:

nat on tl0 from dc0:network to any -> (tl0)

This method works for translation to both IPv4 and IPv6 addresses.

Bidirectional Mapping (1:1 mapping)

A bidirectional mapping can be established by using the binat rule. A binat rule establishes a one to one mapping between an internal IP address and an external address. This can be useful, for example, to provide a web server on the internal network with its own external IP address. Connections from the Internet to the external address will be translated to the internal address and connections from the web server (such as DNS requests) will be translated to the external address. TCP and UDP ports are never modified with binat rules as they are with nat rules.


web_serv_int = ""
web_serv_ext = ""

binat on tl0 from $web_serv_int to any -> $web_serv_ext

Translation Rule Exceptions

Exceptions can be made to translation rules by using the no keyword. For example, if the NAT example above was modified to look like this:
no nat on tl0 from to any
nat on tl0 from to any ->

Then the entire network would have its packets translated to the external address except for

Note that the first matching rule wins; if it's a no rule, then the packet is not translated. The no keyword can also be used with binat and rdr rules.

Checking NAT Status

To view the active NAT translations pfctl(8) is used with the -s state option. This option will list all the current NAT sessions: # pfctl -s state fxp0 TCP -> -> TIME_WAIT:TIME_WAIT fxp0 UDP -> -> MULTIPLE:SINGLE

Explanations (first line only):

Indicates the interface that the state is bound to. The word self will appear if the state is floating.
The protocol being used by the connection.
The IP address ( of the machine on the internal network. The source port (2132) is shown after the address. This is also the address that is replaced in the IP header.
The IP address ( and port (53136) on the gateway that packets are being translated to.
The IP address ( and the port (22) that the internal machine is connecting to.
This indicates what state PF believes the TCP connection to be in.

[Previous: Packet Filtering] [Contents] [Next: Traffic Redirection (Port Forwarding)]

[back] www@openbsd.org
$OpenBSD: nat.html,v 1.27 2007/11/01 02:57:56 joel Exp $