[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250602181948.129956-1-saakashkumar@marvell.com>
Date: Mon, 2 Jun 2025 23:49:48 +0530
From: Aakash Kumar S <saakashkumar@...vell.com>
To: <netdev@...r.kernel.org>
CC: <steffen.klassert@...unet.com>, <herbert@...dor.apana.org.au>,
<davem@...emloft.net>, <edumazet@...gle.com>, <kuba@...nel.org>,
<pabeni@...hat.com>, <horms@...nel.org>, <saakashkumar@...vell.com>,
<akamaluddin@...vell.com>
Subject: [PATCH] xfrm: Duplicate SPI Handling – IPsec-v3 Compliance Concern
The issue originates when Strongswan initiates an XFRM_MSG_ALLOCSPI
Netlink message, which triggers the kernel function xfrm_alloc_spi().
This function is expected to ensure uniqueness of the Security Parameter
Index (SPI) for inbound Security Associations (SAs). However, it can
return success even when the requested SPI is already in use, leading
to duplicate SPIs assigned to multiple inbound SAs, differentiated
only by their destination addresses.
This behavior causes inconsistencies during SPI lookups for inbound packets.
Since the lookup may return an arbitrary SA among those with the same SPI,
packet processing can fail, resulting in packet drops.
According to RFC 6071, in IPsec-v3, a unicast SA is uniquely identified
by the SPI alone. Therefore, relying on additional fields
(such as destination addresses, proto) to disambiguate SPIs contradicts
the RFC and undermines protocol correctness.
Current implementation:
xfrm_spi_hash() lookup function computes hash using daddr, proto, and family.
So if two SAs have the same SPI but different destination addresses or protocols,
they will:
a. Hash into different buckets
b. Be stored in different linked lists (byspi + h)
c. Not be seen in the same hlist_for_each_entry_rcu() iteration.
As a result, the lookup will result in NULL and kernel allows that Duplicate SPI
Proposed Change:
xfrm_state_lookup_byspi() does a truly global search - across all states,
regardless of hash bucket and matches SPI for a specified family
Signed-off-by: Aakash Kumar S <saakashkumar@...vell.com>
---
net/xfrm/xfrm_state.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 341d79ecb5c2..d0b221a4a625 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -2550,7 +2550,6 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high,
__be32 minspi = htonl(low);
__be32 maxspi = htonl(high);
__be32 newspi = 0;
- u32 mark = x->mark.v & x->mark.m;
spin_lock_bh(&x->lock);
if (x->km.state == XFRM_STATE_DEAD) {
@@ -2565,7 +2564,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high,
err = -ENOENT;
if (minspi == maxspi) {
- x0 = xfrm_state_lookup(net, mark, &x->id.daddr, minspi, x->id.proto, x->props.family);
+ x0 = xfrm_state_lookup_byspi(net, minspi, x->props.family);
if (x0) {
NL_SET_ERR_MSG(extack, "Requested SPI is already in use");
xfrm_state_put(x0);
@@ -2576,7 +2575,7 @@ int xfrm_alloc_spi(struct xfrm_state *x, u32 low, u32 high,
u32 spi = 0;
for (h = 0; h < high-low+1; h++) {
spi = get_random_u32_inclusive(low, high);
- x0 = xfrm_state_lookup(net, mark, &x->id.daddr, htonl(spi), x->id.proto, x->props.family);
+ x0 = xfrm_state_lookup_byspi(net, htonl(spi), x->props.family);
if (x0 == NULL) {
newspi = htonl(spi);
break;
--
2.48.1
Powered by blists - more mailing lists