Bypassing the link-local routing table

Linux can use multiple routing tables, which is convenient for providing different routes for specific networks based on many different metrics, such as the source address.  For example, if we want to route traffic from out the default gateway, you could create a new table and route it as such:

# ip route add default via dev eth7 table 100
# ip rule add from lookup 100

Now imagine another scenario, where you wish to route traffic from to an external network (the Internet), but is (for some reason) link-local on your host.  That is, an address like is directly assigned to an adapter on your host.  Linux tracks link-local connections through its ‘local’ routing table, and the ip rule’s show the preference order as:

# ip rule show
0:    from all lookup local 
32766:    from all lookup main 
32767:    from all lookup default

You might think deleting and adding the ‘local’ rule above with a higher preference and placing your new rule above it would fix the problem, but I’ve tried it—and it doesn’t.  Searching around shows that others have had the same problem.

So what to do?  Use fwmark.

First, change local’s preference from 0 to 100:

ip rule del from all pref 0 lookup local
ip rule add from all pref 100 lookup local

Next, mark all traffic from with some mark, we are using “1”.  Note that I am using OUTPUT because is my local address.  You might want PREROUTING if this is a forwarding host.

iptables -t mangle -s -A OUTPUT -j MARK --set-mark 1

And finally add the rule that routes it through table 100:

# ip rule add fwmark 1 pref 10 lookup 100
# ip rule show
10:    from all fwmark 0x1 lookup 100
100:    from all lookup local
32766:    from all lookup main
32767:    from all lookup default

# ip route flush cache

Now all locally generated traffic to from will head out on eth7 through table 100, instead of being looked up in the ‘local’ table.