Make simple things simple, complex things possible. Linux router is an example of this motto. A linux box with 2 NICs (network interface card) can function as a router with 2 simple commands from default configuration, and a 3rd command enables port forwarding to a specific port on a specific host. We'll cover a lot of theory of linux routing and firewalling in this article because subsequent article in this mini-serie are built based on the knowledge present here and will focus on more practical use.
First of all, let's understand the requirement of a router or its definition. A router routes network packets between two network segments. It needs to do source NAT or destination NAT on a network packet. NAT stands for network address translation and is a layer 3 (IP) functionality. What this means is that an outgoing network packet through the router will have its source IP address modified to the external IP of the router (sometimes referred to as WAN IP). A related incoming network packet or port forwarded network packet will have its destination IP address modified, known as destination NAT.
On 2.4+ Linux, NAT is done by iptables nat table during prerouting or postrouting. SNAT of an outgoing packet is performed during postrouting in nat just before it leaves the box. Linux kernel routing table knows that a packet destined to external IP address should go through the PREROUTING->FORWARD->POSTROUTING chain. Similarly, DNAT is performed during prerouting in nat just after it enters the box, typically going through PREROUTING->FORWARD->POSTROUTING chain. DNAT is done automatically for related or established packets (e.g. consequence of an outgoing SNAT SYN packet that establishes a network connection). DNAT is also done explicitly for packets that meet predefined port forwarding rules (an incoming SYN packet). It's strongly recommended the table traverse graph studied and understood.
By default, kernel disables forward chain. That is when a packet coming in on a NI (network interface) has a different destination IP address than the address bound to that NI will be dropped. To allow linux kernel to make routing decision on such kind of packets, the forward chain must be enabled. Next to ensure the kernel can properly route such kind of packets, DNAT must be performed such that when the packet is inspected by the kernel, upon consulting the kernel routing table, the packet can be forwarded to the correct destination NI.
An example will make this clear, given the following defintion
eth0_ip="192.168.0.1"
eth0_nw="192.168.0.1/24"
eth0_gw="192.168.0.150"
eth1_ip="192.168.1.1"
eth1_nw="192.168.1.1/24"
eth1_gw="192.168.1.150"
pfw_serv_ip="192.168.1.3"
kernel routing table can be displayed by "ip route" or "route -n", "ip route" shows more information and sometimes shows information not available through "route -n".
In this hypothetical case, let's suppose:
$eth0_nw route to eth0 via $eth0_nw_gw
$eth1_nw route to eth1 via $eth1_nw_gw
default route to eth0 via $eth0_nw_gw
This is equivalent in English, packets destined to $eth0_nw will be routed to interface eth0, ditto for $eth1_nw and eth1. And if a packet whose destination is not in either $eth0_nw or $eth1_nw, it should be routed to interface eth0.
Now that we have established the routing table and have the forward chain enabled (echo 1 > /proc/net/ipv4/ip_forward), let's see what happens when a packet originated from $pfw_server_ip with its destination ip set to kernel.org comes in from eth1. First the packet is seen on the wire and by the card, the kernel takes the packet and put it on the network stack. Before routing decision is made for this particular packet, there are 2 state machine states it will go through: the mangle table and nat table in PREROUTING chain. Two common actions will be taken by the kernel
1. The incoming packet has SYN flag set and is the first packet for an outgoing connection (similarly for UDP packet, the connection tracking is based on the 5-tuple: src ip, src port, dst ip, dst port, and protocol). The kernel consults the mangle table and nat table for PREROUTING chain and make appropriate changes to the packet. It's not recommended to do any filtering (ACCEPT, DROP, REJECT etc) with mangle/nat tables in PREROUTING chain. Suppose a DNAT rule exists to change the destination address of this packet in nat table, this packet will be redirected to a different server IP address. Thus it's extremely easy to construct a layer 3 proxy with Linux, some times referred to as port forwarding.
The mangle table has rules to modify the packet in other packet header fields different than the IP address. By marking the packet, mangle table can be used to select routing decisions for this packet based 'ip route/rule' kernel tables. For example, the mangle table rule can be used to create a tunnel, a bridge of 2 NIs. mangle table marks a packet and then the kernel routing table routes the packet based on its marking. We'll cover this in a later article in this serie.
2. The incoming packet does not have SYN flag set and the kernel connection tracking module determines it's part of an established connection. In this case, the rules that applied to the SYN packet of this 5-tuple connection are automatically applied to this packet as well.
At this point the packet leaves the PREROUTING chain, based on the kernel routing table, a routing decision will be made where this packet will be delivered to, typically one of the following decisions is made:
1. the destination ip address is one of local host addresses, in this case, the packet is delivered to the internal/local ip address and NI the address is bound to. Next the packet is scheduled to go through the INPUT/OUTPUT chain and the filter table. The filter table is the default table for the INPUT/OUTPUT chain, provided through iptables syntactical sugar. This is an often overlooked fact and causes most confusion to iptables beginners. It'd have been better that '-t filter' be made explicit on command line at the cost of increased verbosity.
It's possible a local process on the local host will consume the incoming packet after the INPUT chain and the story of the packet ends here.
2. the destination ip address is an external address, a routing table entry is available to calculate its destination network interface on the host, the packet is scheduled to go through the FORWARD chain next.
3. the destination ip address is an external address but none of the routing table entry yields a destination network interface on the host, this packet will be dropped. This packet is said to be undeliverable.
After the packet has gone through either the INPUT/OUTPUT or the FORWARD chain, another routing decision is made whether the packet is 'undeliverable' or can be sent out through POSTROUTING chain. Here is place to do either SNAT or MASQUERADE on the outgoing packets before it goes on the wire. The difference between SNAT or MASQUERADE is that MASQUERADE allows dynamic destination address calculation and is typically used when the outgoing NIC has its address obtained from a DHCP server. SNAT rule binds a static IP address to the nat table rule. If you are not sure you should use SNAT or MASQUERADE, use MASQUERADE just to be safe.
After the packet is mangled or SNATed/MASQUERADEd in POSTROUTING chain, it leaves the local host and is sent on the wire.
References:
1. http://iptables-tutorial.frozentux.net/images/tables_traverse.jpg
2. http://iptables-tutorial.frozentux.net/iptables-tutorial.html