Problem
XfrmMark accepts arbitrary public value and mask fields at SA and policy install, query, rekey, and removal boundaries. Linux does not use pair equality for SA lookup:
xfrm_mark_get returns the incoming lookup value as value & mask while retaining the raw pair on the state;
__xfrm_state_lookup matches when (incoming_lookup_value & existing.mask) == existing.value.
A noncanonical stored lookup mark with bits outside its mask can therefore become unaddressable: the masked left-hand side can never equal the raw existing value. The same noncanonical CREATE request can also behave differently from an exact-pair mock map check.
Canonical marks with different masks still have an asymmetric overlap case. For one SA destination/protocol/SPI tuple:
- existing
{ value: 0x10, mask: 0xf0 }, then incoming { value: 0x11, mask: 0xff } collides;
- existing
{ value: 0x11, mask: 0xff }, then incoming { value: 0x10, mask: 0xf0 } can be accepted.
After the second order, a later GETSA or DELSA lookup can match more than one stored state, so pair equality and a caller-supplied mark do not by themselves make deletion exact. This is material to the exact readback/removal and staged-recovery contract introduced around #409.
This issue is distinct from #416. That issue repairs Mock CREATE|EXCL duplicate preservation and Linux collision-predicate parity. This issue defines which lookup marks are valid and when an SA removal can honestly be described as exact.
Required contract
- Lookup marks must be canonical:
value & mask == value.
Some(XfrmMark { mask: 0, .. }) must be rejected for lookup identity; None is the unmarked representation.
- Enforce the invariant through a validated construction surface or at every applicable Linux and Mock install, rekey, query, relocation, and removal boundary. Do not apply lookup-identity restrictions blindly to the separate SA output-mark semantics.
- Define the safety contract for overlapping canonical masks on the same SA destination/protocol/SPI tuple. An API named exact must either prove a non-overlapping lookup domain, restrict accepted marks to a profile that makes overlap impossible, or fail closed before deletion when that proof is unavailable.
- Do not treat exact Rust
Option<XfrmMark> equality as proof that Linux GETSA or DELSA can select only that stored object.
- Keep Debug, Display, and validation errors redaction-safe; report only stable field/reason labels.
Required tests
- Reject lookup marks whose value contains bits outside the mask without issuing a Linux netlink request or mutating Mock state.
- Reject zero-mask
Some lookup marks and preserve None as the only unmarked form.
- Cover SA install, rekey, query, legacy removal, exact query, and exact removal on both Linux request encoding and the public
MockXfrmBackend; cover policy boundaries where the same canonical lookup-mark invariant applies.
- Cover the asymmetric canonical pair above in both insertion orders and lock the documented Linux result.
- In the order Linux can admit both overlapping states, prove the exact-removal boundary does not claim or perform an unproven single-object delete.
- Prove same-mask disjoint values and canonical full-mask values remain supported.
- Qualify public documentation so marked/unmarked and distinct mark pairs are not described as unconditionally separate exact SA identities.
Current downstream impact
The public ePDG intent code uses unmarked SA lookup identities (mark: None) for the initial/default SWu Child SAs. When optional per-bearer steering is engaged, it uses canonical full-mask marks for dedicated outbound SAs and policies; the default outbound SA remains unmarked. This issue therefore does not block the initial unmarked SWu call path, but it does block a broad claim that arbitrary marked SA recovery is exact.
Problem
XfrmMarkaccepts arbitrary publicvalueandmaskfields at SA and policy install, query, rekey, and removal boundaries. Linux does not use pair equality for SA lookup:xfrm_mark_getreturns the incoming lookup value asvalue & maskwhile retaining the raw pair on the state;__xfrm_state_lookupmatches when(incoming_lookup_value & existing.mask) == existing.value.A noncanonical stored lookup mark with bits outside its mask can therefore become unaddressable: the masked left-hand side can never equal the raw existing value. The same noncanonical CREATE request can also behave differently from an exact-pair mock map check.
Canonical marks with different masks still have an asymmetric overlap case. For one SA destination/protocol/SPI tuple:
{ value: 0x10, mask: 0xf0 }, then incoming{ value: 0x11, mask: 0xff }collides;{ value: 0x11, mask: 0xff }, then incoming{ value: 0x10, mask: 0xf0 }can be accepted.After the second order, a later GETSA or DELSA lookup can match more than one stored state, so pair equality and a caller-supplied mark do not by themselves make deletion exact. This is material to the exact readback/removal and staged-recovery contract introduced around #409.
This issue is distinct from #416. That issue repairs Mock
CREATE|EXCLduplicate preservation and Linux collision-predicate parity. This issue defines which lookup marks are valid and when an SA removal can honestly be described as exact.Required contract
value & mask == value.Some(XfrmMark { mask: 0, .. })must be rejected for lookup identity;Noneis the unmarked representation.Option<XfrmMark>equality as proof that Linux GETSA or DELSA can select only that stored object.Required tests
Somelookup marks and preserveNoneas the only unmarked form.MockXfrmBackend; cover policy boundaries where the same canonical lookup-mark invariant applies.Current downstream impact
The public ePDG intent code uses unmarked SA lookup identities (
mark: None) for the initial/default SWu Child SAs. When optional per-bearer steering is engaged, it uses canonical full-mask marks for dedicated outbound SAs and policies; the default outbound SA remains unmarked. This issue therefore does not block the initial unmarked SWu call path, but it does block a broad claim that arbitrary marked SA recovery is exact.