[GH-ISSUE #198] AWG 3.0: PersistentKeepalive always rejected with EINVAL (netlink policy says NLA_U64, code reads u32) #115

Closed
opened 2026-10-13 21:06:41 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @nd4y on GitHub (Jul 31, 2026).
Original GitHub issue: https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/issues/198

Since the AmneziaWG 3.0 merge (b6ca11f), setting PersistentKeepalive on any peer fails:

$ awg setconf awg0 /etc/amnezia/amneziawg/awg0.conf
Unable to modify interface: Invalid argument

Removing the PersistentKeepalive line from the peer makes the very same config apply cleanly, so nothing else in the config is involved. Every value and syntax is rejected — 25, 25-25, 20-30, 15-25.

Root cause

The netlink policy declares the attribute as NLA_U64:

https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/blob/master/src/netlink.c#L77

[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]	= { .type = NLA_U64 },

but the value is read as a 32-bit u16_range_t:

const u16_range_t persistent_keepalive_interval = nla_get_u32(
		attrs[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]);

and amneziawg-tools sends 4 bytes for protocol version >= 3 (src/ipc-linux.h):

if (nlg->version < 3)
	mnl_attr_put_u16(nlh, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, u16_range_lo(peer->persistent_keepalive_interval));
else
	mnl_attr_put_u32(nlh, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval);

NLA_U64 makes nla_parse_nested() require an 8-byte payload, so the 4-byte attribute is rejected before the handler ever runs — hence EINVAL for every value.

For comparison, the device-level range attributes that are also u16_range_t are declared NLA_U32 in device_policy, which is consistent with how they are read.

Fix

-	[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]	= { .type = NLA_U64 },
+	[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]	= { .type = NLA_U32 },

With that one-line change applied before building, PersistentKeepalive is accepted and shows up correctly:

$ awg showconf awg0 | grep PersistentKeepalive
PersistentKeepalive = 20-30

Environment

  • module 3.0.20260730 built from master via DKMS
  • amneziawg-tools v3.0.20260730
  • reproduced on Ubuntu 24.04 (kernel 6.8.0-60-generic) and Ubuntu 24.04 (kernel 6.17.0-1019-aws), both x86_64
Originally created by @nd4y on GitHub (Jul 31, 2026). Original GitHub issue: https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/issues/198 Since the AmneziaWG 3.0 merge (b6ca11f), setting `PersistentKeepalive` on any peer fails: ``` $ awg setconf awg0 /etc/amnezia/amneziawg/awg0.conf Unable to modify interface: Invalid argument ``` Removing the `PersistentKeepalive` line from the peer makes the very same config apply cleanly, so nothing else in the config is involved. Every value and syntax is rejected — `25`, `25-25`, `20-30`, `15-25`. ### Root cause The netlink policy declares the attribute as `NLA_U64`: https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/blob/master/src/netlink.c#L77 ```c [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NLA_U64 }, ``` but the value is read as a 32-bit `u16_range_t`: ```c const u16_range_t persistent_keepalive_interval = nla_get_u32( attrs[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL]); ``` and `amneziawg-tools` sends 4 bytes for protocol version >= 3 (`src/ipc-linux.h`): ```c if (nlg->version < 3) mnl_attr_put_u16(nlh, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, u16_range_lo(peer->persistent_keepalive_interval)); else mnl_attr_put_u32(nlh, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval); ``` `NLA_U64` makes `nla_parse_nested()` require an 8-byte payload, so the 4-byte attribute is rejected before the handler ever runs — hence `EINVAL` for every value. For comparison, the device-level range attributes that are also `u16_range_t` are declared `NLA_U32` in `device_policy`, which is consistent with how they are read. ### Fix ```diff - [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NLA_U64 }, + [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NLA_U32 }, ``` With that one-line change applied before building, `PersistentKeepalive` is accepted and shows up correctly: ``` $ awg showconf awg0 | grep PersistentKeepalive PersistentKeepalive = 20-30 ``` ### Environment - module `3.0.20260730` built from `master` via DKMS - `amneziawg-tools v3.0.20260730` - reproduced on Ubuntu 24.04 (kernel 6.8.0-60-generic) and Ubuntu 24.04 (kernel 6.17.0-1019-aws), both x86_64
Author
Owner

@ygurov commented on GitHub (Jul 31, 2026):

OMG, thanks for finding this one. Let me put a hotfix PR

<!-- gh-comment-id:5137844961 --> @ygurov commented on GitHub (Jul 31, 2026): OMG, thanks for finding this one. Let me put a hotfix PR
Author
Owner

@ygurov commented on GitHub (Jul 31, 2026):

Fixed by https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/pull/199
Thanks @nd4y for your contribution!

<!-- gh-comment-id:5137890824 --> @ygurov commented on GitHub (Jul 31, 2026): Fixed by https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/pull/199 Thanks @nd4y for your contribution!
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amneziawg-linux-kernel-module#115
No description provided.