Re: [REGRESSION] Discussion on "xfrm: Duplicate SPI Handling"

Nathan Harold
2026-02-24T23:53:45Z

> That should still be allowed when using the intended APIs (i.e. ALLOCSPI
> for the inbound and NEWSA for the outbound SA). ALLOCSPI might enforce
> a unique SPI without considering the address, as that's intended for
> local, inbound SAs, where the kernel has full control (looking at the
> the patch, it's certainly not ideal, as it goes through all installed
> SAs to find a duplicate and it prevents an inbound SPI that matches an
> existing outbound SPI - I guess that could be resolved by using separate
> tables for in- and outbound SAs). But that must not prevent installing
> outbound SAs with the same SPI to another peer using NEWSA, which still
> uses a hash that includes the destination address (that must always be
> the case because peers are free to allocate whatever SPI they want).

Agreed that there are some unfortunate limitations with the current
patch. Keying off the inclusion of XFRM_SA_DIR would resolve the issue
you noted (conflating inbound and outbound SPIs) and function as an
opt-in for this enforcement. Whatever the mechanism though, the new
behavior should be opt-in rather than opt-out in order to maintain
backwards compatibility.

> In my opinion, you are using the API incorrectly... I also
> don't think there are any benefits in that "consistent larval lifecycle"
> (if you found any, please let us know).

The Android architecture is multi-tenant and allows userspace apps to
establish SAs. At the time we designed it, this felt like the cleanest
way to facilitate leak-free resource management because the chain of
associations between kernel resources could be symmetrical (and
managing them was already quite complicated). Mea culpa (Nathan). But,
correctly or not, it has/had worked for many years.

> By the way, are you using the min/max option for inbound SAs as well,
> with an SPI generated in userland? That would seem like a violation of
> the intention of the API as well (i.e. letting the kernel control the
> local SPIs).

We provide following two Android APIs for app developers:
https://developer.android.com/reference/android/net/IpSecManager#allocateSecurityParameterIndex(java.net.InetAddress)
https://developer.android.com/reference/android/net/IpSecManager#allocateSecurityParameterIndex(java.net.InetAddress,%20int)

Indeed, allocateSecurityParameterIndex is direction-agnostic; both
overloads are implemented internally by including the min and max
values in ALLOCSPI. For the variant where app developers provide a
specific SPI (which is also useful in testing), Android simply sets
both the min and max parameters to that exact value. Our understanding
of #xfrm_alloc_spi is that min/max are required for ALLOCSPI, and
otherwise ENOENT will be returned.

Note that we also use the DADDR as a mandatory part of the tuple
because of the issue mentioned above: SPIs are only unique in
conjunction with a DADDR, regardless of direction, and accordingly,
that’s how Android is expecting the uniqueness requirement be
enforced. In this way, 5 duplicate SPIs can be used on 5 unique IP
addresses on the same machine; therefore, a strict "SPI only"
interpretation for ALLOCSPI (or SPI handling in general) is curious.
We feel that ALLOCSPI should really enforce the same uniqueness
requirements as the SAD.

Best,

Nathan and Yan

-Nathan

On Wed, Feb 18, 2026 at 12:42 AM Tobias Brunner <tobias@strongswan.org> wrote:
>
> Hi Yan,
>
> > For every inbound SA, we allocate SPIs before negotiation. For
> > outbound SAs, we allocate SPIs once requested by the peer. We only
> > require the (SPI, destination address) combo to be unique. Thus, we
> > may have an inbound and outbound SA sharing an SPI with different
> > destinations, or multiple outbound SAs to different peers sharing an
> > SPI.
>
> That should still be allowed when using the intended APIs (i.e. ALLOCSPI
> for the inbound and NEWSA for the outbound SA). ALLOCSPI might enforce
> a unique SPI without considering the address, as that's intended for
> local, inbound SAs, where the kernel has full control (looking at the
> the patch, it's certainly not ideal, as it goes through all installed
> SAs to find a duplicate and it prevents an inbound SPI that matches an
> existing outbound SPI - I guess that could be resolved by using separate
> tables for in- and outbound SAs). But that must not prevent installing
> outbound SAs with the same SPI to another peer using NEWSA, which still
> uses a hash that includes the destination address (that must always be
> the case because peers are free to allocate whatever SPI they want).
>
> >> If so, why would you use ALLOCSPI and not just install the outbound SA? Is it to avoid differences for in- and outbound SAs (ALLOCSPI+UPDSA vs. NEWSA)?"
> >
> > Exactly—it is primarily for code symmetry. By using ALLOCSPI + UPDSA
> > for both directions, we maintain a consistent larval lifecycle and
> > make it easier to maintain.
>
> In my opinion, you are using the API incorrectly. ALLOCSPI is intended
> to allocate a free local SPI for an inbound SA. That is, reserve it
> before and while the details of the SA are negotiated with the peer
> using IKE. This step isn't necessary for outbound SAs and forcing such
> an allocation, after all the details are known, to the responder's SPI
> (which I assume you do via min/max option) doesn't feel right. I also
> don't think there are any benefits in that "consistent larval lifecycle"
> (if you found any, please let us know). And the difference between
> UPDSA and NEWSA is the nlmsg_type (there are some attributes that are
> different for in- and outbound SAs, especially if you set the direction
> in newer kernels, but that's the case regardless of the message type).
>
> By the way, are you using the min/max option for inbound SAs as well,
> with an SPI generated in userland? That would seem like a violation of
> the intention of the API as well (i.e. letting the kernel control the
> local SPIs).
>
> As the XFRM API basically mirrors PF_KEYv2 here, you can find more about
> the two ways to install SAs in RFC 2367 (SADB_GETSPI/UPDATE vs. SADB_ADD).
>
> Regards,
> Tobias
>