Stateless NAT, occasionally maligned as dumb NAT , is the simplest form of NAT. It involves rewriting addresses passing through a routing device: inbound packets will undergo destination address rewriting and outbound packets will undergo source address rewriting. The iproute2 suite of tools provides the two commands required to configure the kernel to perform stateless NAT. This section will cover only stateless NAT, which can only be accomplished under linux with the iproute2 tools, although it can be simulated with netfilter.
Creating an iproute2 NAT mapping has the side effect of causing the kernel to answer ARP requests for the NAT IP. For more detail on ARP filtering, suppression and conditional ARP, see Chapter 2, Ethernet. This can be considered, alternatively, a benefit or a misfeature of the kernel support for NAT. The nat entry in the local routing table causes the kernel to reply for ARP requests to the NAT IP. Conversely, netfilter DNAT makes no ARP entry or provision for neighbor advertisement.
Whether or not it is using a packet filter, a linux machine can perform NAT using the iproute2 suite of tools. This chapter will document the use of iproute2 tools for NAT with a simple example and an explanation of the required commands, then an example of using NAT with the RPDB and using NAT with a packet filter.
NAT with iproute2 can be used in conjunction with the routing policy database (cf. RPDB) to support conditional NAT, e.g. only perform NAT if the source IP falls within a certain range. See Section 5.3.3, “Conditional Stateless NAT”.
Assume that example company in example network wants to provide SMTP service on a public IP (18.104.22.168/24) but plans to move to a different IP addressing space in the near future. Network address translation can assist example company prepare for the move. The administrator will select an IP on the internal network (192.168.100.0/24) and configure the router to accept and translate packets for the publicly reachable IP into the private IP.
Example 5.1. Stateless NAT Packet Capture 
[root@masq-gw]# tcpdump -qnn 19:30:17.824853 eth1 < 22.214.171.124.35131 > 126.96.36.199.25: tcp 0 (DF) 19:30:17.824976 eth0 > 188.8.131.52.35131 > 192.168.100.17.25: tcp 0 (DF) 19:30:17.825400 eth0 < 192.168.100.17.25 > 184.108.40.206.35131: tcp 0 (DF) 19:30:17.825568 eth1 > 220.127.116.11.25 > 18.104.22.168.35131: tcp 0 (DF)
The first packet comes in on eth1, masq-gw's outside interface. The packet is addressed to the NAT IP, 22.214.171.124 on tcp/25. This is the IP/port pair on which which our service runs. This is a snapshot of the packet before it has been handled by the NAT code.
The next line is the "same" packet leaving eth0, masq-gw's inside interface, bound for the internal network. The NAT code has substituted the real IP of the server, 192.168.100.17. This rewriting is handled by the nat entry in the local routing table (ip route). See also Example 5.2.
The SMTP server then sends a return packet which arrives on eth0. This is the packet before the NAT code on masq-gw has rewritten the outbound packet. This rewriting is handled by the RPDB entry (ip rule). See also Example 5.2.
Finally, the return packet is transmitted on eth1 after having been rewritten. The source IP address on the packet is now the public IP on which the service is published.
There are only a few commands which are required to enable stateless NAT on a linux routing device. The commands below will configure the host masq-gw (see Section A.1, “Example Network Map and General Notes” and Section A.2, “Example Network Addressing Charts”) as shown above in Example 5.1.
Example 5.2. Basic commands to create a stateless NAT
[root@masq-gw]# ip route add nat 126.96.36.199 via 192.168.100.17 [root@masq-gw]# ip rule add nat 188.8.131.52 from 192.168.100.17 [root@masq-gw]# ip route flush cache [root@masq-gw]# ip route show table all | grep ^nat nat 184.108.40.206 via 192.168.100.17 table local scope host [root@masq-gw]# ip rule show 0: from all lookup local 32765: from 192.168.100.17 lookup main map-to 220.127.116.11 32766: from all lookup main 32767: from all lookup 253
This command tells the kernel to perform network address translation on any packet bound for 18.104.22.168. The parameter via tells the NAT code to rewrite the packet bound for 22.214.171.124 with the new destination address 192.168.100.17. Note, that this only handles inbound packets; that is, packets whose destination address contains 126.96.36.199.
This command enters the corresponding rule for the outbound traffic into the RPDB (kernel 2.2 and up). This rule will cause the kernel rewrite any packet from 192.168.100.17 with the specified source address (188.8.131.52). Any packet originating from 192.168.100.17 which passes through this router will trigger this rule. In short, this command rewrites the source address of outbound packets so that they appear to originate from the NAT IP.
The kernel maintains a routing cache to handle routing decisions more quickly (Section 4.7, “Routing Cache”). After making changes to the routing tables on a system, it is good practice to empty the routing cache with ip route flush cache. Once the cache is empty, the kernel is guaranteed to consult the routing tables again instead of the routing cache.
These two commands allow the user to inspect the routing policy database and the local routing table to determine if the NAT routes and rules were added correctly.
NAT introduces a complexity to the network in which it is used because a service is reachable on a public and a private IP. Usually, this is a reasonable tradeoff or else stateless NAT would fail in the selection process. In the case that the linux routing device is connected to a public network and more than one private network, there is more work to do.
Though the service is available to the public network on a public (NAT) IP, internal users may need to connect to the private or internal IP.
This is accomplished by use of the routing policy database (RPDB), which allows conditional routing based on packet characteristics. For a more complete explanation of the RPDB, see Section 4.9, “Routing Policy Database (RPDB)”. The routing policy database can be manipulated with the ip rule command. In order to successfully configure NAT, familiarity with the ip rule command is required.
Example 5.3. Conditional Stateless NAT (not performing NAT for a specified destination network)
[root@masq-gw]# ip rule add to 192.168.99.0/24 from 192.168.100.17 [root@masq-gw]# ip route flush cache [root@masq-gw]# ip rule show 0: from all lookup local 32764: from 192.168.100.17 to 192.168.99.0/24 lookup main 32765: from 192.168.100.17 lookup main map-to 184.108.40.206 32766: from all lookup main 32767: from all lookup 253
Note that we now have an entry of higher priority in the RPDB for any packets returning from 192.168.100.17 bound for 192.168.99.0/24. The rule tells the kernel to find the route for 192.168.99.0/24 (from 192.168.100.17) in the main routing table. This exception to the NAT mapping of our public IP to our internal server will allow the hosts in our second internal network to reach the host named isolde on its private IP address.
If tristan were to initiate a connection to isolde now, the packet would return from IP 192.168.100.17 instead of being rewritten from 220.127.116.11.
Now we have had success creating a NAT mapping with the iproute2 tools and we have successfully made an exception for another internal network which is connected to our linux router. Now, supposing we learn that we will be losing our IP space next week, we are prepared to change our NAT rules without readdressing our server network.
Naturally, you may not wish to create these rules manually every time you want to use NAT on every device. A standard SysV initialization script and configuration file can ease the burden of managing a number of NAT IPs on your system.