[PR #170] fix: per-peer S3-S4 junk offset detection for backward compatibility #216

Open
opened 2026-10-13 21:07:43 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/pull/170
Author: @otternetwork
Created: 4/30/2026
Status: 🔄 Open

Base: master ← Head: fix/per-peer-junk-offsets


📝 Commits (4)

  • 27c4269 fix: restore per-peer legacy WireGuard client support
  • 7eb04a3 fix: detect AWG 1.0 vs 2.0 peers for header generation
  • 419d1a8 fix: per-peer S3-S4 junk offset detection for backward compatibility
  • f78f526 feat: export per-peer last data receive timestamp via netlink

📊 Changes

9 files changed (+251 additions, -56 deletions)

View changed files

📝 src/magic_header.h (+9 -0)
📝 src/netlink.c (+48 -2)
📝 src/netlink.h (+2 -1)
📝 src/noise.c (+16 -1)
📝 src/peer.c (+7 -0)
📝 src/peer.h (+3 -0)
📝 src/receive.c (+71 -16)
📝 src/send.c (+79 -36)
📝 src/uapi/wireguard.h (+16 -0)

📄 Description

Summary

When an AWG 2.0 server has S3-S4 junk offsets configured (S3>0, S4>0), it prepends S3/S4 junk to cookie and transport packets for all AWG peers unconditionally. This breaks AWG clients that don't use S3-S4 offsets — they can't find the header at the expected offset and drop the packet.

This PR adds per-peer detection of S3-S4 junk offset support, following the established pattern of advanced_security (#164) and ranged_headers (#165):

  • Add junk_offsets field to struct wg_peer
  • In prepare_awg_message(), add fallback parsing: try AWG headers at standard offsets (without S3/S4 prefix) when the S3/S4-prefixed match fails
  • Thread detection result via function arguments to wg_packet_consume_data(), which updates peer->junk_offsets after peer identification via key_idx
  • Gate S4 junk in wg_packet_encrypt_worker on peer->advanced_security && peer->junk_offsets
  • Only downgrade junk_offsets from true to false on real data packets (skb->len > MESSAGE_MINIMUM_LENGTH), not keepalives — some client implementations (notably wireguard-go based) omit S4 junk from keepalive packets while correctly adding it to data packets
  • Cookie S3 path left unchanged — no peer context during cookie send, failure mode is benign (rare, temporary)
  • S1/S2 paths left unchanged — #164 already handles it

Transient packet loss while switching to legacy mode

Because the handshake carries no information about whether the client supports S3/S4, the server cannot know the peer's capability until it observes actual transport packets. New peers optimistically inherit junk_offsets = true from the device configuration. When a client without S3/S4 connects, this creates a brief detection window:

  1. Handshake completes successfully (unaffected by S3/S4)
  2. Server sends transport data with S4 junk prefix — client cannot parse it and drops the packet
  3. Client sends its own transport data without S4 junk prefix — server's fallback parsing accepts it and sets peer->junk_offsets = false
  4. Server now sends without S4 junk — communication proceeds normally

During the step 2, server-to-client packets are dropped.

There is no workaround for this within the current design — S3/S4 junk is a transport-layer feature invisible at handshake time. However, this is a very minor issue in practice for several reasons:

  • The primary mode is unaffected. When the client matches the server's S3/S4 configuration (the expected deployment), junk_offsets is correctly true from the start and no packets are dropped.
  • Only the fallback mode is affected — specifically, AWG clients that lack S3/S4 support connecting to a server that has S3/S4 configured. Without this PR, that scenario doesn't work at all (every packet is dropped indefinitely). A brief detection window that self-resolves with the first data packet received from the client is strictly better than permanent failure.
  • Client is usually first to communicate — in most real life scenarios the issue is non-existent.

UAPI extension

Expose the new flag to userspace via WGPEER_A_JUNK_OFFSETS netlink attribute (backward compatible — old userspace ignores unknown attributes). Combined with the existing flags, userspace can now fully distinguish peer capabilities:

advanced_security ranged_headers junk_offsets Peer type
false - - Legacy WireGuard
true false false AWG 1.0 (fixed headers, no S3-S4)
true false true AWG 1.0 (fixed headers, with S3-S4)
true true false AWG 2.0 (ranged headers, no S3-S4)
true true true AWG 2.0 (ranged headers, with S3-S4)

Depends on #165 — must be merged first.

Test plan

  • AWG client without S3-S4 connects to AWG server with S3-S4 configured
  • AWG client with S3-S4 connects to AWG server with S3-S4 configured
  • Legacy WireGuard client still connects (unchanged from #164)
  • AWG 1.0 client (fixed headers) still connects (unchanged from #165)
  • Userspace can read WGPEER_A_JUNK_OFFSETS attribute for connected peers
  • Client mode not broken — clients follow their own device configuration

Closes #168


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/pull/170 **Author:** [@otternetwork](https://github.com/otternetwork) **Created:** 4/30/2026 **Status:** 🔄 Open **Base:** `master` ← **Head:** `fix/per-peer-junk-offsets` --- ### 📝 Commits (4) - [`27c4269`](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/commit/27c426989ca6556b9ee07630cfb2955d255a525d) fix: restore per-peer legacy WireGuard client support - [`7eb04a3`](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/commit/7eb04a36ce20dfbf76adfa831fc4cd621f21d26f) fix: detect AWG 1.0 vs 2.0 peers for header generation - [`419d1a8`](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/commit/419d1a8aeacd31ce82d9a0209d2f5830f267eecf) fix: per-peer S3-S4 junk offset detection for backward compatibility - [`f78f526`](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/commit/f78f526433c730406d65c4da02b8a4ef46bba161) feat: export per-peer last data receive timestamp via netlink ### 📊 Changes **9 files changed** (+251 additions, -56 deletions) <details> <summary>View changed files</summary> 📝 `src/magic_header.h` (+9 -0) 📝 `src/netlink.c` (+48 -2) 📝 `src/netlink.h` (+2 -1) 📝 `src/noise.c` (+16 -1) 📝 `src/peer.c` (+7 -0) 📝 `src/peer.h` (+3 -0) 📝 `src/receive.c` (+71 -16) 📝 `src/send.c` (+79 -36) 📝 `src/uapi/wireguard.h` (+16 -0) </details> ### 📄 Description ## Summary When an AWG 2.0 server has S3-S4 junk offsets configured (S3>0, S4>0), it prepends S3/S4 junk to cookie and transport packets for all AWG peers unconditionally. This breaks AWG clients that don't use S3-S4 offsets — they can't find the header at the expected offset and drop the packet. This PR adds per-peer detection of S3-S4 junk offset support, following the established pattern of `advanced_security` (#164) and `ranged_headers` (#165): - Add `junk_offsets` field to `struct wg_peer` - In `prepare_awg_message()`, add fallback parsing: try AWG headers at standard offsets (without S3/S4 prefix) when the S3/S4-prefixed match fails - Thread detection result via function arguments to `wg_packet_consume_data()`, which updates `peer->junk_offsets` after peer identification via `key_idx` - Gate S4 junk in `wg_packet_encrypt_worker` on `peer->advanced_security && peer->junk_offsets` - Only downgrade `junk_offsets` from true to false on real data packets (`skb->len > MESSAGE_MINIMUM_LENGTH`), not keepalives — some client implementations (notably wireguard-go based) omit S4 junk from keepalive packets while correctly adding it to data packets - Cookie S3 path left unchanged — no peer context during cookie send, failure mode is benign (rare, temporary) - S1/S2 paths left unchanged — #164 already handles it ### Transient packet loss while switching to legacy mode Because the handshake carries no information about whether the client supports S3/S4, the server cannot know the peer's capability until it observes actual transport packets. New peers optimistically inherit junk_offsets = true from the device configuration. When a client without S3/S4 connects, this creates a brief detection window: 1. Handshake completes successfully (unaffected by S3/S4) 2. Server sends transport data with S4 junk prefix — client cannot parse it and drops the packet 3. Client sends its own transport data without S4 junk prefix — server's fallback parsing accepts it and sets peer->junk_offsets = false 4. Server now sends without S4 junk — communication proceeds normally During the step 2, server-to-client packets are dropped. There is no workaround for this within the current design — S3/S4 junk is a transport-layer feature invisible at handshake time. However, this is a very minor issue in practice for several reasons: - The primary mode is unaffected. When the client matches the server's S3/S4 configuration (the expected deployment), junk_offsets is correctly true from the start and no packets are dropped. - Only the fallback mode is affected — specifically, AWG clients that lack S3/S4 support connecting to a server that has S3/S4 configured. Without this PR, that scenario doesn't work at all (every packet is dropped indefinitely). A brief detection window that self-resolves with the first data packet received from the client is strictly better than permanent failure. - Client is usually first to communicate — in most real life scenarios the issue is non-existent. ### UAPI extension Expose the new flag to userspace via `WGPEER_A_JUNK_OFFSETS` netlink attribute (backward compatible — old userspace ignores unknown attributes). Combined with the existing flags, userspace can now fully distinguish peer capabilities: | `advanced_security` | `ranged_headers` | `junk_offsets` | Peer type | |---|---|---|---| | false | - | - | Legacy WireGuard | | true | false | false | AWG 1.0 (fixed headers, no S3-S4) | | true | false | true | AWG 1.0 (fixed headers, with S3-S4) | | true | true | false | AWG 2.0 (ranged headers, no S3-S4) | | true | true | true | AWG 2.0 (ranged headers, with S3-S4) | **Depends on #165** — must be merged first. ## Test plan - [x] AWG client without S3-S4 connects to AWG server with S3-S4 configured - [x] AWG client with S3-S4 connects to AWG server with S3-S4 configured - [x] Legacy WireGuard client still connects (unchanged from #164) - [x] AWG 1.0 client (fixed headers) still connects (unchanged from #165) - [x] Userspace can read `WGPEER_A_JUNK_OFFSETS` attribute for connected peers - [x] Client mode not broken — clients follow their own device configuration Closes #168 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#216
No description provided.