As far as I know there's no feature allowing to switch two routing tables in Linux nor to replace atomically an existing routing rule. It's still possible to achieve an atomic switch like presented below. All the gymnastic with intermediate rules is to really be atomic at any step without any caveat. For all intent and purposes, the goto
rules below are optional, and quasi atomicity would still be achieved without them.
Algorithm:
previous state:
The routing table 1000 was already added as well as a rule with preference 20000 doing a lookup on this table (no selector here because this information wasn't available from OP):
$ ip -4 rule
0: from all lookup local
20000: from all lookup 1000
32766: from all lookup main
32767: from all lookup default
preparation:
Routing table 1001 is prepared in advance with a few ip route add ... table 1001
commands. As no rule references it, it's still inactive. That's the routing table that will replace the routing table 1000.
add the new routing rule (which could have additional selectors) referencing it at a later preference value, but skip it first with a goto rule
... pointing to the usual rule that looks up the main, so there's not even a case where the old routing table would affect some packets and the newer routing case would affect other packets simultaneously before the switch is over. This should arguably not really matter and goto
rules below could be skipped if such strict atomicity is not required.
ip rule add pref 20001 goto 32766
ip rule add pref 20002 lookup 1001
atomically switch to the newer routing table with an other goto rule with an earlier preference that will skip both the rule for the older routing table and the rule that skipped the newer routing table.
ip rule add pref 19999 goto 20002
clean up
ip rule del pref 20000
ip rule del pref 20001
ip rule del pref 19999
go back to previous state: reuse the previous routing table ID and routing rule preference values
redo on routing table 1000 the same operations previously done on routing table 1001 in step 2., starting by flushing it
ip route flush table 1000
ip route add ... table 1000
...
reset preference and reference to previous state and clean up
ip rule add pref 20000 lookup 1000
ip rule del pref 20002
ip route flush table 1001
Or switching the roles of tables 1000 and 1001 in the next iteration would probably avoid having to populate routes twice.
State is now back to step 1.
If the goal is to replace the main routing table, just consider that it's table ID 254 (and that it's the only table (along the local routing table) to receive automatic kernel routes: better always use noprefixroute
to addresses and add routes by the daemon in this case): not much to change in the algorithm above except possibly replacing goto 32766
with goto 32767
or a similar change. Also any new route added in a routing table (even if not referenced) requires validation with itself or routes already present in this table or current active rules and referenced tables' routes.