Skip to content

set: fix byte order metadata for host-endian types and map data#353

Closed
anthonyrisinger wants to merge 1 commit intogoogle:mainfrom
anthonyrisinger:fix-set-byteorder-metadata
Closed

set: fix byte order metadata for host-endian types and map data#353
anthonyrisinger wants to merge 1 commit intogoogle:mainfrom
anthonyrisinger:fix-set-byteorder-metadata

Conversation

@anthonyrisinger
Copy link

Fix two issues with set/map userdata byte order metadata that cause nft list ruleset to misinterpret element keys and data values on little-endian systems. The kernel-level data is always correct — these are display/round-trip issues affecting the userdata TLVs that the nft CLI uses to reconstruct human-readable output.

Bug 1: Anonymous sets ignore explicit KeyByteOrder

Anonymous/constant/interval sets unconditionally emit KEYBYTEORDER=2 (big-endian), ignoring the KeyByteOrder field. The nft C tool always uses the key expression's actual byte order (mnl.c:mnl_nft_set_add), not a blanket default.

For host-endian types like mark_type (BYTEORDER_HOST_ENDIAN) and ifname_type (BYTEORDER_HOST_ENDIAN), this causes nft list ruleset to misinterpret the element data on LE systems:

  • Mark sets: meta mark { 0x00000000, 0x01000000 } instead of { 0x00000000, 0x00000001 }
  • Interface name vmaps: iifname vmap { "" : jump chain1 } instead of { "eth0" : jump chain1 }

The existing comment "Semantically useless - kept for binary compatability with nft" is incorrect — KEYBYTEORDER IS semantically meaningful for host-endian types. The nft C tool uses it in netlink_delinearize_setelem to decide whether to call mpz_switch_byteorder on element keys.

Fix: When the anonymous/constant/interval condition fires, check if KeyByteOrder is explicitly set to NativeEndian and emit 1 (host) instead of 2 (big). Falls back to the original big-endian default when KeyByteOrder is unset (zero value), preserving backwards compatibility.

Bug 2: Maps never emit DATABYTEORDER

The NFTNL_UDATA_SET_DATABYTEORDER constant exists in userdata.go but is never used. The Set struct has no DataByteOrder field, and the userdata construction has no code path that emits this TLV.

The nft C tool emits DATABYTEORDER for all datamaps:

if (set_is_datamap(set->flags))
    nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_DATABYTEORDER,
                         set->data->byteorder);

Without it, nft list ruleset uses BYTEORDER_INVALID for map data, skips the mpz_switch_byteorder call, and displays native-endian values as byte-swapped on LE systems:

# Expected:
elements = { ... : 0x00000001 }

# Without DATABYTEORDER on LE:
elements = { ... : 0x01000000 }

Fix: Add DataByteOrder field to Set struct and emit NFTNL_UDATA_SET_DATABYTEORDER in the userdata construction for maps when set.

Discovery context

Found while debugging a WireGuard mesh overlay that uses meta mark sets and maps for zone-based connection latching. The incorrect display made it extremely difficult to diagnose whether mark values were being set correctly, since nft list ruleset showed byte-swapped values but the kernel data was actually correct (verified via nft --debug=netlink list ruleset).

Testing

Verified on linux/arm64 (little-endian) that after this patch:

  • Anonymous TypeMark sets display correct values (0x00000001 not 0x01000000)
  • Map data with TypeMark displays correct values
  • nft --debug=netlink list ruleset shows identical kernel-level bytes before and after (no functional change)
  • GOOS=linux go build . and GOOS=linux go vet . pass cleanly

@google-cla
Copy link

google-cla bot commented Feb 11, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@anthonyrisinger anthonyrisinger force-pushed the fix-set-byteorder-metadata branch 2 times, most recently from 70c670c to 5ef1e8e Compare February 11, 2026 03:45
Fix two bugs in the userdata byte order metadata emitted by AddSet:

1. KEYBYTEORDER override: Anonymous, constant, and interval sets
   unconditionally emitted KEYBYTEORDER=2 (big-endian), ignoring the
   actual key type. Host-endian types like mark and ifname need
   KEYBYTEORDER=1 for correct display in `nft list ruleset` on
   little-endian systems. The nft C tool always uses the key
   expression's actual byte order (mnl.c:mnl_nft_set_add), not a
   blanket big-endian default.

2. Missing DATABYTEORDER: The library never emitted
   NFTNL_UDATA_SET_DATABYTEORDER for maps despite the constant
   existing in userdata.go. Without it, `nft list ruleset` cannot
   determine the data byte order, displaying host-endian values
   (like marks) as byte-swapped on little-endian systems.

Add a DataByteOrder field to the Set struct and simplify the
KEYBYTEORDER logic to a switch statement where explicit
KeyByteOrder=NativeEndian always takes precedence.

Add TestSetByteOrder with 6 subtests covering anonymous/named sets
and maps with various byte order combinations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments