TL;DR: Two default route entries coexist fine, but the kernel forwards via the lowest metric and treats the rest as failover only. Deterministic multihoming means policy routing: one routing table per uplink, ip rule selecting by source address, and connection marks keeping replies on the NIC that received them.
How to approach it
Ask which behaviour is wanted before designing: failover, load spread across links, or a dual-homed server that must answer on both addresses through the same NIC it was contacted on. Those are three different mechanisms, and naming that distinction early is most of the score.
A strong answer
Yes, you can add two defaults. ip route add default via 10.0.0.1 metric 100 plus a second with metric 200 gives active-backup: the kernel uses only the lowest-metric entry until it disappears. Note the kernel does no health probing of its own; failover happens when the route is withdrawn by something watching the link, such as a routing daemon or keepalived tracking scripts.
Two defaults at equal metric invite ECMP: legacy nexthop syntax (nexthop via ... weight 1 nexthop via ... weight 1) hashes flows across both links. This is where hosts break in production. Traffic exits eth0, replies arrive on eth1, and rp_filter in strict mode (value 1, common on enterprise distros) drops the reply because its reverse path lookup would egress elsewhere. The symptom is unmistakable once you have seen it: tcpdump on eth1 shows answers arriving for connections whose sockets never see them, and replies may never reach the socket. INVALID is a packet classification, not a persistent state necessarily shown for a conntrack entry.
inbound SYN -> eth1 (198.51.100.7)
reply lookup: ip rule "from 198.51.100.7" -> table isp2
table isp2: default via 198.51.100.1 dev eth1 (symmetric, rp_filter happy)
without the rule: table main default via eth0
reply leaves eth0, provider drops it or rp_filter kills it first
Policy routing is the deterministic design. Register uplink tables in /etc/iproute2/rt_tables (say isp1, isp2), give each its own default route, then steer with rules:
ip route add 203.0.113.0/24 dev eth0 src 203.0.113.7 table isp1
ip route add default via 203.0.113.1 dev eth0 table isp1
ip route add 198.51.100.0/24 dev eth1 src 198.51.100.7 table isp2
ip route add default via 198.51.100.1 dev eth1 table isp2
ip rule add from 203.0.113.7/32 lookup isp1 priority 100
ip rule add from 198.51.100.7/32 lookup isp2 priority 110
Rules evaluate by increasing priority; a matching table lookup can continue to later rules when it finds no usable route, while a terminal result ends the search. For traffic initiated inbound, add CONNMARK: save the mark on ingress, restore it on egress, so an established flow keeps leaving through its arrival NIC even when source-based rules would miss it.
| Requirement | Design | Mechanism |
|---|---|---|
| Failover only | metric ordering | lower metric wins; external health watcher |
| Spread throughput | ECMP nexthops | per-flow hash, needs symmetric paths |
| Dual-homed servers | policy routing | per-uplink tables + ip rule + CONNMARK |
One everyday cause worth mentioning: DHCP on two NICs installs two defaults that fight on every renewal. Suppress the secondary with NetworkManager's never-default on that connection rather than deleting routes by hand.
My position: metric ordering when only availability matters, ECMP for stateless throughput, full policy routing when hosts hold addresses in multiple subnets. Reverse to plain metrics the moment someone proposes policy routing without an operational reason, because rules are invisible to the next engineer at 3am.
What interviewers probe next
"What does rp_filter actually check?" Reverse-path validation per RFC 3704: strict mode drops packets whose source has no route back through the arriving interface; loose mode accepts if any route exists.
"How do you prove asymmetric routing?" tcpdump on both NICs for one flow, plus source-specific ip route get lookups and conntrack tuples. A standard conntrack listing does not provide an ingress/egress interface pair.
"Where do these tables live?" Numbers 1 to 252 defined in /etc/iproute2/rt_tables; 254 main and 255 local are built in, and rules can also match fwmark, not just source.
Common mistakes
Setting equal metrics expecting balancing and shipping asymmetric blackholes instead.
Blaming the firewall for dropped replies while rp_filter silently discards them; counters in nftables never move because the kernel core does the dropping.
Treating rule order as cosmetic. Priority numbers control lookup order, and a broad rule with a usable default route can shadow later rules.
References