Android 11 seems to support IKEv2/IPsec now, so I'm attempting to build a roadwarrior swanctl profile for it. So far I'm getting as far as having an SA established, but then immediately deleted. Any advice?

The Android VPN profile has:

  • Type: IKEv2/IPsec PSK
  • Server: moon.isuldor.com
  • IPsec Identifier: strongswan at isuldor.com
  • IPsec PSK: hunter2

My vpn gateway has:

$ swanctl --version
strongSwan swanctl 5.9.0

$ cat /etc/swanctl/conf.d/android11.conf
connections {
    rw-isuldor {
        local_addrs = moon.isuldor.com
        pools = android11_pool4, android11_pool6
        fragmentation = yes
        send_cert = always
        rekey_time = 0s
        dpd_delay = 30s
        local {
            auth = pubkey
            certs = moon.pem
            id = moon.isuldor.com
        remote {
            auth = psk
            id = strongswan at isuldor.com
        children {
            moon {
                local_ts  =,::/0
                rekey_time = 0s
                dpd_action = clear
secrets {
    ike-isuldor {
        id_isuldor = strongswan at isuldor.com
        secret = hunter2
pools {
    android11_pool4 {
        addrs =
        dns =,
    android11_pool6 {
        addrs = 2607:9cf3:0:ae::6:1300/120
        dns = 2606:4700:4700::1111,2606:4700:4700::1001

Relevant Logs from charon-systemd:

X.X.X.X is initiating an IKE_SA
IKE_SA (unnamed)[11] state change: CREATED => CONNECTING
selected proposal: IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_AES128_XCBC/MODP_3072
remote host is behind NAT
looking for peer configs matching Y.Y.Y.Y[moon.isuldor.com]...X.X.X.X[strongswan at isuldor.com]
selected peer config 'rw-isuldor'
authentication of 'strongswan at isuldor.com' with pre-shared key successful
peer requested virtual IP %any
assigning new lease to 'strongswan at isuldor.com'
assigning virtual IP to peer 'strongswan at isuldor.com'
peer requested virtual IP %any6
assigning virtual IP <redacted> to peer 'strongswan at isuldor.com'
CHILD_SA moon{4} established with SPIs cba17603_i 0f8dcc81_o and TS ::/0 ===
CHILD_SA moon{4} state change: INSTALLING => INSTALLED
generating IKE_AUTH response 1 [ IDr CERT AUTH CPRP(ADDR DNS DNS) SA TSi TSr ]
splitting IKE message (2416 bytes) into 3 fragments
generating IKE_AUTH response 1 [ EF(1/3) ]
generating IKE_AUTH response 1 [ EF(2/3) ]
generating IKE_AUTH response 1 [ EF(3/3) ]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733] (1236 bytes)
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733] (1236 bytes)
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733] (84 bytes)
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733]
checkin IKE_SA rw-isuldor[7]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733]
checkin of IKE_SA successful
received packet: from X.X.X.X[38733] to Y.Y.Y.Y[4500]
waiting for data on sockets
checkout IKEv2 SA by message with SPIs ce7fea937528e3ca_i 115e7e1303dd7bc4_r
IKE_SA rw-isuldor[7] successfully checked out
received packet: from X.X.X.X[38733] to Y.Y.Y.Y[4500] (80 bytes)
parsed INFORMATIONAL request 2 [ D ]
received DELETE for IKE_SA rw-isuldor[7]
deleting IKE_SA rw-isuldor[7] between Y.Y.Y.Y[moon.isuldor.com]...X.X.X.X[strongswan at isuldor.com]
IKE_SA rw-isuldor[7] state change: ESTABLISHED => DELETING
IKE_SA deleted
generating INFORMATIONAL response 2 [ ]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733] (80 bytes)
checkin and destroy IKE_SA rw-isuldor[7]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733]
IKE_SA rw-isuldor[7] state change: DELETING => DESTROYING
CHILD_SA moon{4} state change: INSTALLED => DESTROYING
deleting policy === out
deleting policy === in
deleting policy === fwd
deleting SAD entry with SPI cba17603
deleted SAD entry with SPI cba17603
deleting SAD entry with SPI 0f8dcc81
deleted SAD entry with SPI 0f8dcc81
lease by 'strongswan at isuldor.com' went offline
checkin and destroy of IKE_SA successful

Update: The issue became immediately apparent once I retrieved the android logs. Basically I used adb shell to access the device, then logcat with an appropriate filter. There's probably terminal apps that can do this as well. Root was not required.

130|sargo:/ $ whoami
130|sargo:/ $ logcat *:S IkeV2VpnRunner:V
--------- beginning of system
--------- beginning of main
[..] IkeV2VpnRunner: com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException: Expected the remote/server to use PSK-based authentication but they used: 14

Conclusion: the swanctl profile should have auth=psk under the local section and an additional line assigning the pre-shared key for the server like: id_moon = moon.isuldor.com under secrets.ike-isuldor. This worked exclusively for strongswan swanctl 5.9.0, but so far I was not able to reproduce the success with an earlier version 5.7.2. I suspect the syntax may have changed in some way. But the ultimate problem was the incorrect server authentication.

  • When adding a /64 to the pool, StrongSwan likely told you the range was too large, try assigning a subnet from your /64 instead. – Ginnungagap Nov 27 '20 at 7:33
  • 1
    While @Ginnungagap is right (the netmask is adjusted automatically, though), that's not the reason no IPv6 address is assigned. You need to define a separate pool for IPv6 and assign that to pools in addition to the IPv4 pool. If that's the reason for the deletion by Android we'll have to see. Actually, just noticed that your traffic selector looks weird (although, is still negotiated). Should probably be local_ts=,::/0. – ecdsa Nov 27 '20 at 8:45
  • Thanks, I fixed that local_ts up and tried assigning smaller IPV6 pool ranges: a /96, a /112, and a /128. I restarted strongswan-swanctl each time, but still to no effect. The client requests IPv6 and none is assigned. I've updated question to reflect that. – isuldor Nov 27 '20 at 17:18
  • I don't see any changes regarding the address pools in your updates. See here on the strongSwan wiki for details. – ecdsa Nov 30 '20 at 8:29
  • 1
    You don't have any logs of the client? Possible issues on the client: authentication via server certificate fails (the client appears to authenticate via PSK, possible it expects the server to authenticate with PSK as well). – ecdsa Dec 1 '20 at 8:11

As the client log confirms, it expected the server to authenticate with the PSK too, not a certificate. So instead of local.auth=pubkey you'd have to configure local.auth=psk.

Note that while using a certificate on the server and a PSK on the client is supported by the IKEv2 protocol, and it does protect against other hosts knowing the PSK impersonating the server (every client has to know it and could do so), it has the same issue as IKEv2's PSK authentication has in general: the client sends the AUTH payload before verifying the server's authentication. An active attacker could use this to determine a weak PSK via dictionary or brute force attacks. A far safer approach is to use certificate authentication for the server and username/password based EAP methods (e.g. EAP-MD5 or EAP-MSCHAPv2) for the client because then the client only sends its hashed password after verifying the server certificate.

  • Curiously, Google decided to leave out IPsec certificate verification altogether in Android 11. Hopefully we'll see that in a future update. i.stack.imgur.com/poksz.png – isuldor Dec 2 '20 at 13:48
  • Yeah, that's a weird one. You can actually select something there, but only if you install the CA certificate as part of a PKCS#12 container in the system credential store ("Install a certificate" > "VPN & app user certificate"). As the latter indicates, these are usually used to install client certificate and private key, which are precisely not needed to authenticate with username/password. – ecdsa Dec 2 '20 at 16:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.