Why does IPsecSaContextSetSpi0 from the WFP API seem to only accept even SPI values?

Nathan 0 Reputation points
2025-02-13T19:39:45.3633333+00:00

When creating an IPsec SA using the WFP API, IpsecSaContextSetSpi0 doesn't seem to accept an odd SPI value. Why is this the case? RFC 4303 Section 2.1 talks about valid SPI values for an IPsec SA. It makes no mention of odd values being invalid, and states that the only IANA-reserved values are 0-255.

If we take the DLL file behind the WFP API, which is located at C:\Windows\System32\FWPUCLNT.DLL on my Windows 11 x86_64 system, and dump the assembly, we can see the following instructions:

$ gdb FWPUCLNT.DLL
(gdb) disassemble IPsecSaContextSetSpi0
Dump of assembler code for function fwpuclnt!IPsecSaContextSetSpi0:
   0x0000000180012140 <+0>:  mov    %r9d,0x20(%rsp)
   0x0000000180012145 <+5>:  push   %rbx
   0x0000000180012146 <+6>:  sub    $0x40,%rsp
   0x000000018001214a <+10>:  xor    %ebx,%ebx
   0x000000018001214c <+12>:  test   %r9d,%r9d
   0x000000018001214f <+15>:  je     0x1800121c2 <fwpuclnt!IPsecSaContextSetSpi0+130>
   0x0000000180012151 <+17>:  test   $0x1,%r9b
   0x0000000180012155 <+21>:  jne    0x1800121c2 <fwpuclnt!IPsecSaContextSetSpi0+130> 
[...]
   0x00000001800121c2 <+130>:  mov    $0x80320035,%eax
   0x00000001800121c7 <+135>:  add    $0x40,%rsp
   0x00000001800121cb <+139>:  pop    %rbx
   0x00000001800121cc <+140>:  retq   



At offset 17, we see a bitwise AND that checks if the least significant bit of a value (likely the SPI) is non-zero. If it's non-zero, we jump to offset 130, and load the value 0x80320035 (which is FWP_E_INVALID_PARAMETER) and return that. We can also see the same if we plug the DLL into a decompiler such as BinaryNinja:

uint64_t IPsecSaContextSetSpi0(int64_t* arg1, int64_t arg2, int64_t arg3, int32_t arg4)
{
    void var_48;
    int64_t rax_1 = __security_cookie ^ &var_48;
    int64_t rbx = 0;
    int32_t var_18 = arg4;
    uint64_t result;
    
    if (!arg4 || arg4 & 1)
    {
        void* rcx_2 = data_18007a000;
        
        if (rcx_2 != &data_18007a000 && *(rcx_2 + 0x19) >= 2 && *(rcx_2 + 0x1c) & 0x100)
        {
            int64_t var_28_2 = arg2;
            sub_18001ae58(*(rcx_2 + 0x10), arg2, arg3, arg4);
        }
        
        result = 0x80320035;
    }
    else
    {
        if (arg1)
        {
            int32_t rax_2;
            rax_2 = sub_18001f1ac(*arg1, arg1[1], arg2, arg3, &var_18);
            
            if (rax_2)
                rbx = sub_180006520(arg1, "FwppProxyBfeIPsecSaContextGetOrS…", rax_2);
        }
        else
            rbx = sub_180006520(arg1, "IPsecSaContextSetSpi0", 0x8032001c);
        
        result = sub_180002960(rbx);
    }
    
    __security_check_cookie(rax_1 ^ &var_48);
    return result;
}

Where arg4 is likely the SPI value as it's the only 32-bit argument.

Now the documentation on the method IPsecSaContextSetSpi0 makes no mention of odd SPI values being invalid, and it doesn't talk about FWP_E_INVALID_PARAMETER being a likely return value:

IPsecSaContextSetSpi0 function (fwpmu.h)

Even the documentation on WFP error codes provides little info:

WFP Error Codes

Why is this even/odd check present? Again, it goes against RFC 4303 Section 2.1 as linked earlier, and it seriously breaks interoperability with other IPsec implementations such as Linux's XFRM. Is it just an accidental bug or is it an odd, but intentional choice that hasn't been documented anywhere?

I am not the only one to encounter an issue with this. IKEv2 implementations such as strongSwan have also encountered this when interfacing with WFP. This issue was encountered many years ago on Windows Server 2012: Issue #2750: setting WFP SA SPI failed: 0x80320035

And it is still being encountered in recent times: "setting WFP SA SPI failed" and "unable to install IPsec policies (SPD) in kernel" on Windows #2567

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,735 questions
0 comments No comments
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.