Plugin: vrf
Version: 1.9.1
Affected: IPv6 only (IPv4 is not affected)
Description
When an IPAM plugin creates routes before the vrf plugin runs, the VRF plugin fails to correctly move an IPv6 default route (::/0) into the VRF routing table.
The suspected root cause is that the VRF plugin's route migration logic only handles routes with a single nexthop and does not implement the ECMP (Equal-Cost Multi-Path) case.
In a typical pod setup, eth0 already has an IPv6 default route in the main table. When a macvlan interface (net1) is attached via a NetworkAttachmentDefinition, the IPAM plugin adds a second IPv6 default route via net1. Since a ::/0 route already exists, the kernel requires this to be expressed as an ECMP route with two nexthops. The VRF plugin does not handle this case and fails to move the route into the VRF table.
The issue does not occur with split default routes (::/1 and 8000::/1), which do not conflict with the existing eth0 default route and therefore do not trigger the ECMP path.
IPv4 is not affected.
Steps to Reproduce
- Have a pod with a primary interface (eth0) that has IPv6 configured — an IPv6 default route (::/0) via eth0 exists in the main routing table.
- Configure a multus NetworkAttachmentDefinition using an IPAM plugin (e.g. static or dhcp) that adds an IPv6 default route (::/0) via a macvlan interface (net1), chained with the vrf plugin.
- Attach the network to a pod and inspect routing tables inside the pod.
Expected Behavior
The IPv6 default route via net1 should be present in the VRF routing table (e.g. table 1001):
$ ip -6 route show table 1001
::/0 via 2001:db8:1::1 dev net1 metric 1024 pref medium
2001:db8:1::/64 dev net1 proto kernel metric 256 pref medium
Actual Behavior
The default route via net1 is merged with the existing eth0 default route into an ECMP route in the main routing table and is not added to the VRF table:
$ ip route show table all
default metric 1024 pref medium
nexthop via fe80::1 dev eth0 weight 1
nexthop via 2001:db8:1::1 dev net1 weight 1
$ ip -6 route show table 1001
local 2001:db8:1::11 dev net1 proto kernel metric 0 pref medium
2001:db8:1::/64 dev net1 proto kernel metric 256 pref medium
local fe80::1 dev net1 proto kernel metric 0 pref medium
fe80::/64 dev net1 proto kernel metric 256 pref medium
multicast ff00::/8 dev net1 proto kernel metric 256 pref medium
Workaround
Use split default routes instead of a single ::/0 route:
$ ip -6 route show table 1001
::/1 via 2001:db8:1::1 dev net1 metric 1024 pref medium
8000::/1 via 2001:db8:1::1 dev net1 metric 1024 pref medium
These prefixes do not conflict with the existing eth0 ::/0 route, so no ECMP handling is required and the routes are correctly placed in the VRF table.
Root Cause (suspected)
The VRF plugin's route migration logic appears to only handle routes with a single nexthop. The failure sequence is:
- The pod has a primary interface (eth0) with IPv6 configured — an IPv6 default route (::/0) via eth0 already exists in the main routing table.
- The IPAM plugin adds a second IPv6 default route (::/0) via the macvlan interface (net1).
- Since a default route already exists, the kernel requires this to be added as an ECMP route (multipath, two nexthops on the same ::/0 prefix).
- The VRF plugin then attempts to move the net1 nexthop into the VRF table, but its implementation does not handle the ECMP case — it only supports adding a route with a single nexthop.
- The default route via net1 is never added to the VRF table. Both nexthops (eth0 and net1) remain as an ECMP route in the main table.
To fix this, the VRF plugin would need to detect when a route being moved is part of an ECMP set, extract only the nexthop(s) belonging to the target interface, and add them correctly into the VRF table using the RTA_MULTIPATH netlink attribute or equivalent.
Environment
- CNI Plugins version: 1.9.1
Plugin: vrf
Version: 1.9.1
Affected: IPv6 only (IPv4 is not affected)
Description
When an IPAM plugin creates routes before the vrf plugin runs, the VRF plugin fails to correctly move an IPv6 default route (::/0) into the VRF routing table.
The suspected root cause is that the VRF plugin's route migration logic only handles routes with a single nexthop and does not implement the ECMP (Equal-Cost Multi-Path) case.
In a typical pod setup, eth0 already has an IPv6 default route in the main table. When a macvlan interface (net1) is attached via a NetworkAttachmentDefinition, the IPAM plugin adds a second IPv6 default route via net1. Since a ::/0 route already exists, the kernel requires this to be expressed as an ECMP route with two nexthops. The VRF plugin does not handle this case and fails to move the route into the VRF table.
The issue does not occur with split default routes (::/1 and 8000::/1), which do not conflict with the existing eth0 default route and therefore do not trigger the ECMP path.
IPv4 is not affected.
Steps to Reproduce
Expected Behavior
The IPv6 default route via net1 should be present in the VRF routing table (e.g. table 1001):
$ ip -6 route show table 1001
::/0 via 2001:db8:1::1 dev net1 metric 1024 pref medium
2001:db8:1::/64 dev net1 proto kernel metric 256 pref medium
Actual Behavior
The default route via net1 is merged with the existing eth0 default route into an ECMP route in the main routing table and is not added to the VRF table:
$ ip route show table all
default metric 1024 pref medium
nexthop via fe80::1 dev eth0 weight 1
nexthop via 2001:db8:1::1 dev net1 weight 1
$ ip -6 route show table 1001
local 2001:db8:1::11 dev net1 proto kernel metric 0 pref medium
2001:db8:1::/64 dev net1 proto kernel metric 256 pref medium
local fe80::1 dev net1 proto kernel metric 0 pref medium
fe80::/64 dev net1 proto kernel metric 256 pref medium
multicast ff00::/8 dev net1 proto kernel metric 256 pref medium
Workaround
Use split default routes instead of a single ::/0 route:
$ ip -6 route show table 1001
::/1 via 2001:db8:1::1 dev net1 metric 1024 pref medium
8000::/1 via 2001:db8:1::1 dev net1 metric 1024 pref medium
These prefixes do not conflict with the existing eth0 ::/0 route, so no ECMP handling is required and the routes are correctly placed in the VRF table.
Root Cause (suspected)
The VRF plugin's route migration logic appears to only handle routes with a single nexthop. The failure sequence is:
To fix this, the VRF plugin would need to detect when a route being moved is part of an ECMP set, extract only the nexthop(s) belonging to the target interface, and add them correctly into the VRF table using the RTA_MULTIPATH netlink attribute or equivalent.
Environment