AMD Host Device CNI — Policy Route Injection via NAD#
Background#
The amd-host-device CNI plugin moves a host NIC into a pod’s network namespace using
the host-device sub-plugin, and synthesises a static IPAM config from the addresses
already configured on that NIC. When chained with the sbr (Source-Based Routing) CNI
plugin, SBR reads the IPAM result and installs policy routing rules so that traffic
sourced from the secondary NIC IP is steered back through the correct interface.
Previously, the only routing information propagated to SBR was an implicit default
gateway derived for /31 point-to-point links. For aggregated spine-fabric topologies —
where a pod needs to reach a supernet prefix (e.g. /19) via its ToR peer — there was
no mechanism to inject an explicit route into the IPAM result without additional CNI
plugins or manual host configuration.
Change Summary#
Two new optional fields are introduced in the NetworkAttachmentDefinition (NAD) config
block for amd-host-device:
Field |
Type |
Description |
|---|---|---|
|
integer |
Offset added to the network address of the interface subnet to derive the nexthop IP |
|
integer (0-32) |
Prefix length applied to the interface IP to derive the route destination |
When both fields are present, the plugin computes one route per IPv4 address on the host
interface and injects them into the static IPAM routes array. SBR then installs these
routes into the per-source policy routing table it manages.
If either field is absent, no routes are injected. Note that this is not identical to the
previous release for /31 links: the implicit /31 gateway injection has been removed
(see Removed Behaviour below), so a /31 NAD without these fields no longer produces a
route.
Route Derivation Algorithm#
For each IPv4 address on the host interface:
Destination:
dstIP = interfaceIP & CIDRMask(routeDstPrefixLen, 32)
dstNet = dstIP / routeDstPrefixLen
Nexthop — two cases:
/31 point-to-point links:
nexthopNetAddrOffsetis ignored. The nexthop is the kernel peer when one is present, otherwise the XOR of the local address (interfaceIP ^ 1on the last octet).All other prefix lengths: the nexthop is derived as:
networkAddr = interfaceIP & subnetMask nexthop = networkAddr + nexthopNetAddrOffset
The resulting route entry added to the IPAM routes array:
{ "dst": "<dstNet>", "gw": "<nexthop>" }
Examples#
Non-/31 — spine supernet route#
NIC: 10.1.2.130/25, nexthopNetAddrOffset=1, routeDstPrefixLen=19
networkAddr = 10.1.2.128
nexthop = 10.1.2.128 + 1 = 10.1.2.129
dstIP = 10.1.2.130 & /19 mask = 10.1.0.0
route → 10.1.0.0/19 via 10.1.2.129
SBR installs in the policy table:
ip rule: from 10.1.2.130 lookup table 100
table 100: 10.1.0.0/19 via 10.1.2.129
Traffic sourced from 10.1.2.130 destined for anything in 10.1.0.0/19 is
policy-routed via the ToR peer 10.1.2.129.
Non-/31 — explicit default route#
NIC: 10.1.2.130/25, nexthopNetAddrOffset=1, routeDstPrefixLen=0
nexthop = 10.1.2.129
dstNet = 0.0.0.0/0
route → 0.0.0.0/0 via 10.1.2.129
/31 — spine supernet route#
NIC: 10.1.2.5/31, nexthopNetAddrOffset=0 (ignored), routeDstPrefixLen=16
nexthop = 10.1.2.5 XOR 1 = 10.1.2.4
dstIP = 10.1.2.5 & /16 mask = 10.1.0.0
route → 10.1.0.0/16 via 10.1.2.4
Works correctly regardless of whether the NIC holds the lower (.4) or upper (.5)
address of the /31 pair.
/31 — explicit default route#
nexthopNetAddrOffset=0, routeDstPrefixLen=0 → 0.0.0.0/0 via 10.1.2.4
Removed Behaviour#
The previous implicit /31 gateway injection — where the XOR peer was placed in the
gateway field of the IPAM addresses entry causing SBR to install an implicit default
route — has been removed. The routeDstPrefixLen=0 mechanism replaces it explicitly and
extends the same capability to non-/31 prefixes. This makes all routing behaviour opt-in
and fully visible in the NAD, with no implicit defaults.
NAD Example#
{
"cniVersion": "0.3.1",
"name": "vf-amd-host-device-sbr-nad",
"plugins": [
{
"type": "amd-host-device",
"nexthopNetAddrOffset": 1,
"routeDstPrefixLen": 19
},
{ "type": "sbr" }
]
}
Notes#
Both fields must be specified together; if either is absent no routes are injected.
routeDstPrefixLenmust be strictly less than the interface prefix length. Equal or longer values skip that address (logged); they would be a connected-subnet or host route via the peer, which is not useful.0is always a supernet and installs a default route (0.0.0.0/0), which is how you restore the old/31+ SBR gateway.When the interface prefix length is 30 or less (
/30,/24,/16, and so on),nexthopNetAddrOffsetmust fall in[1, 2^(32 − interface prefix) − 2]. Offset0is the subnet address and the top of the old range is the broadcast address; the kernel rejects both as a gateway, so those offsets skip that address (logged). A typical ToR peer is1. Any offset outside the host bits also skips that address (logged). On/31the offset is ignored. The gateway is the kernel peer when one is present, otherwise the XOR of the local address.Only IPv4 addresses are processed; IPv6 addresses on the interface are unaffected.
Multiple IPv4 addresses on the same interface each produce an independent route entry.