VyOSXIV · Multiple Routing TablesRouting tables
Table ID namespace — reserved IDs, custom IDs, and VRF conflict
What you'll learn
- Identify the kernel-reserved table IDs and explain why they cannot be reused
- Choose and document operator-defined table IDs without colliding with VRF table IDs
- Read and write /etc/iproute2/rt_tables to declare table aliases
- Recognise the failure mode when a custom table ID collides with a VRF table
Prerequisites
Verified against VyOS 1.5.x LTS (circinus) · VyOS 1.4.x (sagitta) — legacy · FRRouting 10.x (VyOS 1.5) · Linux kernel 6.6 LTS (VyOS 1.5 base) · strongSwan 5.9.x (IPsec) · WireGuard 1.0.x (kernel module + userspace tooling) · 2026-08-15
Table ID namespace — reserved IDs, custom IDs, and VRF conflict
Every routing table in the Linux kernel is identified by a
32-bit integer — the table ID. The kernel reserves a handful of
IDs for its own purposes (local, default, main, and
aliases). The operator can use any other ID, subject to one
constraint that catches the unwary: VRF table IDs live in the
same namespace as routing-table IDs, and a routing table and a
VRF cannot share an ID. This lesson covers the reserved IDs, the
operator-defined range, the /etc/iproute2/rt_tables alias
file, and the failure mode when a custom table ID collides with
a VRF.
The reserved IDs
flowchart LR
A[Table ID 0-2^32] --> B[Reserved by kernel]
A --> C[VRF namespace]
A --> D[Operator-defined]
B --> B1[0: local]
B --> B2[253: default]
B --> B3[254: main]
B --> B4[255: local alias]
C --> C1[1000-2^31 typical VRF range]
D --> D1[1-252, 256-999 typical]
The kernel reserves four IDs in the IPv4 and IPv6 table namespace:
| ID | Name | Cannot be redefined |
|---|---|---|
| 0 | unspec | Yes (kernel internal; never appears in user output) |
| 253 | default | Yes (empty by default; some user-space tools install here) |
| 254 | main | Yes (the operator’s standard routes) |
| 255 | local | Yes on some kernels (alias of ID 0) |
The IDs 0, 253, 254, and 255 are protected by the kernel. An
operator who tries to install a route at ID 0 or 254 (the
main table) gets a netlink error — EINVAL. The VyOS
configuration tree rejects set system ip rt-table rt-table-id 254 at validation time.
The convention for operator-defined tables is to use IDs in the range 1 to 252 and 256 to 999, leaving room above 1000 for VRFs. This is a convention, not a kernel constraint. The kernel permits any ID outside the reserved set. The convention exists because VRFs and routing tables share the namespace.
/etc/iproute2/rt_tables
The kernel does not read /etc/iproute2/rt_tables. The file is
read by iproute2 to translate symbolic names to numeric IDs.
The kernel sees only the numeric ID; the alias is for human
operators.
The shipped file on VyOS 1.5 LTS:
vyos@vyos:~$ cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
# 1 inr.ruhep
The four reserved entries cannot be removed. Operator-defined
tables are added below, one per line, with the format <id> <name> [<comment>]. A comment is metadata; iproute2 ignores
it but it is useful for documentation.
On VyOS 1.5 LTS, the file is managed through the configuration tree:
[edit]
vyos@vyos# set system ip rt-table rt-table-id 100 name mgmt description "management traffic"
[edit]
vyos@vyos# commit
[edit]
vyos@vyos# save
After commit, the file contains:
vyos@vyos:~$ cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
# 1 inr.ruhep
# Added by VyOS
100 mgmt management traffic
The comment is preserved verbatim. The 100 is the ID; mgmt
is the alias. The iproute2 commands now accept table mgmt
in place of table 100:
vyos@vyos:~$ ip route show table mgmt
10.30.0.0/24 via 192.0.2.3 dev eth0 proto static metric 1
If the alias is not declared, iproute2 returns RTNETLINK answers: Invalid argument — iproute2 cannot resolve mgmt to a
numeric ID. The kernel-side rule still works because the kernel
saw the numeric ID, but the operator-facing tooling is broken.
Choosing operator-defined IDs
The choice of ID has no effect on performance — the kernel treats all operator-defined IDs identically. The choice matters for two reasons:
- Operator documentation. The ID should be memorable and
associated with the table’s purpose.
100for management,200for backup WAN,300for VPN are common patterns. - VRF collision avoidance. VRFs occupy IDs in the same
namespace. A routing-table ID that collides with a VRF ID is
rejected by the kernel with
EEXIST.
The VyOS default convention reserves IDs 1-252 and 256-999 for
operator-defined routing tables, and IDs 1000-2^31 for VRFs.
This is enforced by the VyOS configuration validators. An
operator who tries to set rt-table-id 1000 for a routing table
when vrf is also using 1000 is rejected at commit time with a
descriptive error.
VRF namespace collision
VRF (Virtual Routing and Forwarding) is a separate lesson sequence (Part XV), but the table-ID collision is a problem that operators encounter in mixed deployments and must understand here.
flowchart LR
A[Custom table ID 100] --> B{Routing table?}
B -->|yes| C[Install as routing table]
B -->|no| D{VRF ID 100?}
D -->|yes| E[Cannot — conflict]
D -->|no| F[Both can coexist]
A VRF in Linux is a routing-table ID with a name and a set of bound interfaces. The kernel uses the VRF’s ID as its routing table ID. The VRF has its own FIB, its own RIB, its own connected routes (only for interfaces bound to it), and its own lookup rules.
The kernel allows a single ID to be either a routing table or a
VRF, not both. An operator who defines a VRF at ID 100 cannot
also define a routing table at ID 100. The kernel returns
EEXIST on the second install.
The VyOS convention — routing tables at 1-999, VRFs at 1000+ — keeps the two namespaces separate. An operator who deviates from the convention (e.g. a custom table at ID 1500 when a VRF is at ID 1500) hits the conflict at runtime.
How the result is validated
The validation command set for table ID and alias configuration:
vyos@vyos:~$ show system ip rt-table
100 mgmt management traffic
vyos@vyos:~$ cat /etc/iproute2/rt_tables
#
# reserved values
#
255 local
254 main
253 default
0 unspec
#
# local
#
# 1 in.r.ruhep
# Added by VyOS
100 mgmt management traffic
vyos@vyos:~$ ip route show table all | head -20
table local:
local 192.0.2.1 dev lo proto kernel scope host src 192.0.2.1
broadcast 192.0.2.255 dev lo proto kernel scope link src 192.0.2.1
table main:
10.20.0.0/16 via 192.0.2.2 dev eth0 proto static metric 1
192.0.2.0/24 dev eth0 proto kernel scope link src 192.0.2.1
table mgmt:
10.30.0.0/24 via 192.0.2.3 dev eth0 proto static metric 1
table default:
vyos@vyos:~$ ip link show vrf
The ip link show vrf command lists the VRFs. If the IDs in the
operator’s routing-table list overlap with the VRF list, there
is a collision. The operator must either rename the routing
table or rename the VRF.
The cross-check between the VyOS view (show system ip rt-table)
and the kernel view (/etc/iproute2/rt_tables) is the canonical
validation. They must agree.
How it fails
The production failure modes the engineer must recognise:
- Alias declared in VyOS but missing from /etc/iproute2/rt_tables.
The configuration tree has the rt-table entry, but the file is
stale (a previous failed commit left it in an inconsistent
state).
ip route show table <name>returns nothing. The fix iscommit; saveagain, which triggers a re-render. - Custom table at reserved ID.
set system ip rt-table rt-table-id 254 name customis rejected by the VyOS validator atcommittime with “table ID 254 is reserved for themaintable”. - Custom table ID collides with VRF.
set system ip rt-table rt-table-id 1500 name customwhen a VRF is already using 1500. The commit fails with “table ID 1500 is in use by VRF customer-a”. The fix is to choose a different ID. - Routes installed in table but name not declared. An
operator script added routes to ID 100 without updating
/etc/iproute2/rt_tables. The routes are in the kernel; the operator cannot query them withip route show table 100by name because there is no name. - Multiple aliases for the same ID. Two
set system ip rt-tableentries that claim the same ID with different names. The commit fails with “table ID X is declared twice”.
Rollback
The recovery from a table ID misconfiguration:
- Wrong ID:
delete system ip rt-table rt-table-id <wrong>; set system ip rt-table rt-table-id <correct> name <name> ...; commit; save. - VRF collision:
delete system ip rt-table rt-table-id <X>; set system ip rt-table rt-table-id <new> name <name> ...; commit; save. Update any rules that referenced the old ID. - Alias missing: re-render with
commit; saveafter the rt-table declaration is in place. - Whole-tree rollback:
rollback N; commit; save.
For emergency rollback of live-state changes that bypassed
VyOS, the operator must ip route del table <id> and update the
VyOS configuration to match.
Production discipline
Cross-course references
The Linux course’s XXI-Linux-NetAdvanced covers the same
table ID namespace from a host perspective. The OPNsense
course’s II-OPNsense-Routing covers the FreeBSD setfib
namespace, which is similar in concept but uses different IDs.
The VRF course’s XV-VyOS-VRFs covers the VRF-side of the
namespace collision.
Quiz
Knowledge check · 4 questions
Q1. Which table IDs are reserved by the Linux kernel and cannot be redefined by the operator?
Q2. On a VyOS 1.5 LTS router, an operator can define a routing table at the same numeric ID as a VRF table.
Q3. An operator declares a custom routing table at ID 100 named `mgmt` in VyOS. After commit, `cat /etc/iproute2/rt_tables` shows the entry, but `ip route show table mgmt` returns `RTNETLINK answers: Invalid argument`. What is the most likely cause?
iproute2 cannot resolve the alias `mgmt` to a numeric ID. The most common cause is a typo or a stale alias file — the entry is in the file, but iproute2 cannot parse it. The fix is to inspect the file, correct the syntax, and re-render.
Q4. An operator deploys a VRF at ID 1000 named `customer-a`. Later, they declare a routing table at ID 1000 named `mgmt`. The commit fails with an error about a VRF collision. What is the fix?
Routing tables and VRFs share the ID namespace; one ID cannot be both. The VyOS convention is to use IDs 1-999 for routing tables and 1000+ for VRFs. The operator has crossed the convention by putting the routing table at the VRF ID range. The fix is to move the routing table to a different ID.
Passing score: 75%. Answers are checked in this browser.