Packet types for the NETLINK_RDMA protocol (protocol number 20).
RDMA netlink is neither a generic netlink family nor part of rtnetlink. It is
its own netlink protocol, and it has no family header — the client id and the
operation are packed into nlmsg_type instead:
nlmsg_type = (client << 10) | op
This crate models the RDMA_NL_NLDEV client (device and port enumeration).
Other clients (RDMA_NL_LS, RDMA_NL_IWCM) round-trip as raw bytes, because
they prefix their payloads with C structs rather than starting with attributes.
Read-only nldev operations: GET, PORT_GET, SYS_GET, RES_GET,
RES_{QP,CM_ID,CQ,MR,PD,CTX,SRQ}_GET and STAT_GET. Everything else passes
through as NldevMessage::Other.
86 attributes have dedicated variants; the rest (driver-private data, the
*_GET_RAW payloads) parse into NldevAttr::Other and are preserved byte for
byte.
The command list and the enum are generated from a single table by the
nldev_messages! macro, so a command cannot be added to one and forgotten in
the other.
Resource and statistics responses nest: RES_QP contains many RES_QP_ENTRY,
each holding one queue pair's fields. Those variants carry Vec<NldevAttr> and
recurse.
Recursion is capped at MAX_NESTING_DEPTH (8). nldev nests three deep at most,
but a level costs only 4 bytes to encode, so a 64 KiB message could otherwise
drive 16 000 levels and overflow the stack. Anything past the cap is kept as an
opaque Other.
rdma_netlink.h carries this note next to RDMA_NLDEV_ATTR_NDEV_INDEX:
The netdevices which are associated with containers are supposed to be exported together with GID table once it will be exposed through the netlink. Because the associated netdevices are properties of GIDs.
That never happened. As of Linux 6.18 there is still no RDMA_NLDEV_ATTR_*GID*
attribute of any kind. GIDs, GID types and their per-GID netdevice bindings come
from sysfs:
/sys/class/infiniband/<dev>/ports/<n>/gids/<i>
/sys/class/infiniband/<dev>/ports/<n>/gid_attrs/types/<i>
/sys/class/infiniband/<dev>/ports/<n>/gid_attrs/ndevs/<i>
This is also where libibverbs reads them (libibverbs/cmd_device.c), so there
is no lower-level netlink path being missed.
cargo test --features bolero # property tests
cargo run --example dump_devices # against the running kernelThe bolero feature derives TypeGenerator on the public message and attribute
types, which drives four properties in tests/roundtrip.rs:
- canonical attributes survive emit → parse unchanged
- canonical messages survive serialize → deserialize unchanged, exercising the
(client << 10) | oppacking rdma_nl_get_type/_client/_opare mutually consistent- arbitrary bytes never panic the parser, and decoding is idempotent
- every variant's kind appears in
MODELLED_KINDS— the guard against that table drifting fromNldevAttr::kind - 4096 levels of nesting terminate at the depth cap instead of overflowing
Under a fuzzing engine the same properties become coverage-guided targets:
cargo bolero test --features bolero arbitrary_bytes_decode_idempotentlySeveral values emit correctly but do not parse back to themselves, so the
round-trip properties are stated over canonical values only. is_canonical()
reports which:
- an
Othervariant holding a kind or command the crate models natively - an
OtherNLA kind withNLA_F_NESTED/NLA_F_NET_BYTEORDERset, sinceNlaBuffer::kindmasks those off on the way in - a string attribute containing an interior NUL, which truncates
- an op that does not fit in the 10 bits
nlmsg_typeallots it - a typed sub-enum
Othernaming a value the enum models, e.g.NodeType::Other(RDMA_NODE_RNIC)
Each of these was found by the property tests rather than by inspection.