Now that you have a basic understanding of Linux network routing, it's time to delve into some interesting things you can do with this information.

Dropping Packets

Here are some examples of how to drop packets, along with the result.

Silently drop packets.

ip route add blackhole [destination addr/mask]

Reject packets with ICMP / "Host unreachable" response.

ip route add unreachable [destination addr/mask]   

Reject packets with ICMP / "Administratively prohibited" response.

ip route add prohibit [destination addr/mask]  

Reject packets with ICMP / "Net unreachable" response.

ip route add throw [destination addr/mask]

Manipulating the Source Address

The src parameter forces outgoing traffic along a particular route to appear to be coming from a specified source IP address. An example of where this could be useful is directing outgoing traffic as desired onto a shared network interface.

The format is:

~# ip route add n.n.n.n/n.n.n.n dev[device] src n.n.n.n

An example:

ip route add 10.10.14.107 dev tun0 table vpn src 192.168.1.100

Which roughly translated to saying,

"Add a new route to table vpn that directs traffic addressed to destination address 10.10.14.107 to report this server's source address as 192.168.1.100 and send the packet over interface device tun0."

Breaking it down chronologically, here is another way to think about this command:

add add a new route
10.10.14.107 with destination address 10.10.14.107
dev tun0 send the outgoing packet via device tun0
table vpn place this new route in the table labeled vpn
src 192.168.1.100 alter the source address of packets traversing this route to 192.168.1.100

NOTE: The use of src only works on packets originating from the server and is generally discouraged.

Source NAT (or Not)

While at times it emulates NAT-like functions, there is no true NAT capability in iproute2 (ip route commands). The RPDB can only change the source IP address when the packet is new and originating from the server (localhost). By definition, iproute is a stateless process. It interacts with raw packets and serves to simply "route" them (iptables and ip rule, meanwhile understand network states). Therefore, iproute's NAT - technically - is stateless. And, while it may appear to be a form of NAT; it is not.

The src parameter mentioned previously modifies a packet's source address. However, it only works with packets originating from the current host.[1] This differentiates it from source NAT (SNAT), which is handled by iptables.

Source NAT in iptables (including the very similar MASQUERADE function) is limited to operating on just the first packet of a connection (though SNAT is a bit more robust). This strategy helps ensure packets that are part of an existing connection don't get lost; a condition that was possible when iproute was capable of changing source NAT arbitrarily.

The iproute2 process is limited by the kernel from having any affect (in terms of NAT) on any packet unless it is new and originating from localhost. After a packet leaves the localhost, but prior to reaching the OUTPUT iptables chain, the RPDB is capable of altering the outgoing source packet. This is technically not NAT, but a stateless mangling of the packet. Like NAT, it only works with new connections. Either way, it is generally discouraged. There are very few scenarios where using the RPDB to mangle a new packet's source IP make sense. It's almost always more desirable to perform source NAT-ing (SNAT) in the iptables POSTROUTING chain. You may wish to review the iptables Packet Routing tree diagram, or take a look at this version that demonstrates when iproute is able to affect the packet flow process.

Figure 6 (right) indicates the iptables outbound packet flow process with iproute2 source NAT backdoor highlighted.

iptables process flow with iproute2 src NAT injection

When src NAT Makes Sense

Just think about the fact that iproute has no knowledge of connection states. There are only two circumstances where I'd encourage you to use ip route to modify the source address of a packet.

The first is when more than one LAN is connected to a single network device. Under that circumstance, it makes sense for ip route to mangle the source address instead of iptables, because ip route knows which outbound route the packet will be taking and the packet is originating from the server. This solves a potential problem where the packet could get dropped because the next (hop) device tries to return a reply to the server, but the packet's source address is not in the correct range. The result is the packet is dropped and the originating server never gets a reply.

The other scenario where the src action makes sense in ip route is when the server has multiple network devices. In conjunction with specifying a particular network device to send an outgoing packet, it makes sense to use this opportunity to also set the source IP address for that outgoing packet.

Note ip route cannot be used to change the destination address of a packet (destination NAT or DNAT).

A Brief History of iproute NAT

Prior to the Linux 2.6.9 kernel update in late 2004, iproute could perform NAT on ANY outgoing packet. This feature was removed for various reasons. Primarily, to consolidate existing network tools and prevent them from stepping on each other. In the case of iproute NAT, this was a somewhat controversial decision as it had been a powerful capability (though its real-world practical use was questionable).

This change to network routing received little attention at the time, and I suspect the vast majority of people were unaware of it. In fact, if one reads the 2.6.9 release notes, there is barely any mention of changes to iproute2. The update is described as a patch for "generic network statistics." Admittedly, the full notation does mention connection tracking.

The change to iproute2 was more or less an indirect result of the connection tracking changes, though it was a purposeful impact. Even the full release notes didn't do justice to the impact of the change, "A new unified statistics tool for routing cache, connection tracking and neighbour cache is under development and will be included with iproute2."[2]

Functionally, the change brought iproute's NAT capabilities in-line with iptables, and was designed to encourage the use of iptables for packet NAT changes (using stateful SNAT and DNAT; discussed in greater detail in the iptables section of this document).

Quite predictably, a side-effect of the loss of most NAT functionality in iproute2 was to thrust iptables firmly into the NAT spotlight, as it meant there was then little point in using ip route for NAT duty.

Stateful vs. Stateless: What Do They Mean?

What does 'stateless' mean? You may hear that frequently in networking jargon. All it means is the packets are not tracked and associated with a particular connection and connection status. A tool that uses 'stateless' packet inspection doesn't care about the connection the packet is a part of. It is only evaluating the packet on its own merit. Likewise, the opposite is true.

A 'stateful' tool for example, is one that is aware of whether or not the current packet is part of an established connection, and understands some information about both the packet and the corresponding connection. In short, it is aware of the "state" of both the connection and the packet, whatever they may be.

When You MUST Have Stateless NAT

Many applications that are deprecated in Linux never truly die. And that is certainly true of iproute2's stateless packet manipulation. While the hooks via the ip route commands are gone, it's ability to perform stateless NAT is still there. You just need to know where to find it and how to use it. tc is the command line invocation for the Traffic Controller, which is part of iproute2. An in-depth explanation of tc is beyond the scope of this document, however I'll describe it briefly so that curious readers are able to learn more about it.

Utilizing stateless NAT is bly discouraged unless you are absolutely certain you know what you're doing.

tc

tc was introduced in the Linux 2.2 kernel and gained additional capabilities in kernel 2.6.24, shortly after the demise of most stateless NAT functions via iproute2 were retired in 2.6.9.

The tc tool is commonly used for QoS (Quality of Service), which is basically the management of network traffic in order to reduce packet loss and/or latency. It is sparsely documented. If you want to be able to use it for stateless NAT, you are going to have to do some research.

A b overall guide discussing the basic tenets of traffic control concepts is found in a Traffic Control HowTo guide published by the Linux Documentation Project. You might also want to browse the Linux Advanced Routing & Traffic Control HOWTO document. And finally, if you want a quick-start type of guide for Debian-based Linux distributions and using tc, I recommend you take a look at the good overview found on the ArchLinux wiki topic, Advanced traffic control.

Single Network Connection, Multiple LANs

Here's an example. Let's say you have two LANs accessible from your server via a single network device.

LAN1 has an address range of 192.168.10.0-192.168.10.255 and a gateway at 192.168.10.10

LAN2 has an address range of 10.10.10.0-10.10.10.255 and no gateway

Your server wants to send data to another device at address 10.10.10.14. Let's run the route command to see what the routing table looks like:

Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.10.10 0.0.0.0 UG 0 0 0 eth0
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
10.10.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0

Examining the route command output, you see there is a gateway at 192.168.10.10 and two local routes (192.168.10.0/24 and 10.10.10.0/24). So, what's the big deal?

The issue is guaranteeing outgoing packets sent from this server to each LAN will have responses returned back to your server. We know the longest matching route prefix will be chosen to send the packet to 10.10.10.14. However, we don't know if the source address will indicate it's coming from the 192.168.10.0/24 network or the 10.10.10.0/24 network. We obviously want it on the latter, because we know there is no gateway on the 10.10.10.0/24 network, which means any address outside the network's range will fail because no other server on that network will respond to the packet.

The problem is from this display, we don't know if that guarantee exists or not. Did whomever setup this routing table include the src parameter? We can't tell. This is where the route command is lacking. We need more detail. Instead, try running ip route list. And you get this display:

default via 192.168.10.10 dev eth0
192.168.10.0/24 dev eth0 proto kernel scope link src 192.168.10.11
10.10.10.0/24 dev eth0 proto kernel scope link src 10.10.10.11

Wow. Now we can see there is a src parameter for both LANs, which is excellent. That means depending on which LAN a packet is addressed to (destination), we know it will come back to this local machine because it will leave this server with an address on the same LAN it went out on.

Multiple Network Connections, Multiple LANs

A more common scenario is where you have multiple network devices on a server and each is connected to an independent network. In this case the src parameter serves a similar function: guaranteeing which ip address is specified for an outgoing packet that is part of a new connection initiated from your server. By using src to force a particular source IP address for a particular outgoing route, you ensure the return packet will find its way back to your server.

Let's briefly examine how the ip route commands might look if you were setting up a table called test to do this. Let's presume you already created the test table and can use that for testing purposes.

ip route add default via 192.168.10.10 dev eth0 table test src 192.168.10.11
ip route add 192.168.10.0/24 dev eth0 table test src 192.168.10.11
ip route add 10.10.10.0/24 dev eth0 table test src 10.10.10.11

You can see each route specifies the source IP address when a packet traverses it. This ensures the packet will be able to return to this host regardless of its destination.

Endnotes

[1] Andreasson, Oskar. Iptables Tutorial 1.2.1. Chapter 6: Traversing of tables and chains. https://www.frozentux.net/iptables-tutorial/chunkyhtml/x1237.html.

[2] Summary of changes from v2.6.8 to v2.6.9. 19 October 2004. Changelog. https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.9.