[PR #122] [CLOSED] fix: handle ErrUDPGSODisabled in handshake send paths (kernel 6.17 regression) #150

Closed
opened 2026-10-13 21:04:03 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/amnezia-vpn/amneziawg-go/pull/122
Author: @crispuscrew
Created: 3/9/2026
Status: ❌ Closed

Base: master ← Head: master


📝 Commits (1)

  • f61b5ed fix, auto test & manual test

📊 Changes

2 files changed (+155 additions, -0 deletions)

View changed files

➕ device/gso_fallback_test.go (+141 -0)
📝 device/send.go (+14 -0)

📄 Description

Fixes #119

Problem

On Linux kernel 6.17 (Fedora 41), sendmmsg returns EINVAL when UDP GSO is used. The conn layer correctly catches this, disables GSO, retries without it, and returns ErrUDPGSODisabled{RetryErr: nil} - meaning the packet was actually sent successfully via the fallback path.

However, SendHandshakeInitiation and SendHandshakeResponse treated any non-nil error from SendBuffers as fatal. This caused the handshake to fail and the tunnel to never establish, even though the packet went through fine.

The error message users saw:

disabled UDP GSO on , NIC(s) may not support checksum offload or peer MTU with protocol headers is greater than path MTU

Fix

Mirror the pattern already used in RoutineSequentialSender: unwrap ErrUDPGSODisabled and only propagate the inner RetryErr. If RetryErr is nil (retry succeeded), the error is silently dropped and the handshake proceeds normally.

Tests

Unit tests (device/gso_fallback_test.go):

  • TestSendHandshakeInitiationGSODisabledFallback — wraps the bind to return ErrUDPGSODisabled{RetryErr: nil} on the first Send call, asserts that SendHandshakeInitiation returns nil.
  • TestSendHandshakeResponseGSODisabledFallback — same for SendHandshakeResponse, asserts that ErrUDPGSODisabled is not leaked.

Both tests fail on the unpatched code and pass after the fix.

Integration test (manual, Linux kernel 6.17.10, Fedora 41):

Ran the userspace amneziawg-go daemon (WG_PROCESS_FOREGROUND=1) against a real AmneziaWG server with full obfuscation config (jc=3, jmin=10, jmax=30, s1-s4, h1-h4). Verified via UAPI query that last_handshake_time_sec is non-zero and both tx_bytes / rx_bytes are exchanged - tunnel established successfully. Before the fix, the handshake failed and no traffic flowed.


🔄 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-go/pull/122 **Author:** [@crispuscrew](https://github.com/crispuscrew) **Created:** 3/9/2026 **Status:** ❌ Closed **Base:** `master` ← **Head:** `master` --- ### 📝 Commits (1) - [`f61b5ed`](https://github.com/amnezia-vpn/amneziawg-go/commit/f61b5ed59c54bd8e555e5acad6c0768320bd6778) fix, auto test & manual test ### 📊 Changes **2 files changed** (+155 additions, -0 deletions) <details> <summary>View changed files</summary> ➕ `device/gso_fallback_test.go` (+141 -0) 📝 `device/send.go` (+14 -0) </details> ### 📄 Description Fixes #119 ## Problem On Linux kernel 6.17 (Fedora 41), `sendmmsg` returns `EINVAL` when UDP GSO is used. The conn layer correctly catches this, disables GSO, retries without it, and returns `ErrUDPGSODisabled{RetryErr: nil}` - meaning the packet was actually sent successfully via the fallback path. However, `SendHandshakeInitiation` and `SendHandshakeResponse` treated any non-nil error from `SendBuffers` as fatal. This caused the handshake to fail and the tunnel to never establish, even though the packet went through fine. The error message users saw: ```bash disabled UDP GSO on , NIC(s) may not support checksum offload or peer MTU with protocol headers is greater than path MTU ``` ## Fix Mirror the pattern already used in `RoutineSequentialSender`: unwrap `ErrUDPGSODisabled` and only propagate the inner `RetryErr`. If `RetryErr` is nil (retry succeeded), the error is silently dropped and the handshake proceeds normally. ## Tests **Unit tests** (`device/gso_fallback_test.go`): - `TestSendHandshakeInitiationGSODisabledFallback` — wraps the bind to return `ErrUDPGSODisabled{RetryErr: nil}` on the first Send call, asserts that `SendHandshakeInitiation` returns nil. - `TestSendHandshakeResponseGSODisabledFallback` — same for `SendHandshakeResponse`, asserts that `ErrUDPGSODisabled` is not leaked. Both tests fail on the unpatched code and pass after the fix. **Integration test** (manual, Linux kernel 6.17.10, Fedora 41): Ran the userspace `amneziawg-go` daemon (`WG_PROCESS_FOREGROUND=1`) against a real AmneziaWG server with full obfuscation config (jc=3, jmin=10, jmax=30, s1-s4, h1-h4). Verified via UAPI query that `last_handshake_time_sec` is non-zero and both `tx_bytes` / `rx_bytes` are exchanged - tunnel established successfully. Before the fix, the handshake failed and no traffic flowed. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-13 21:04:03 +00:00
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-go#150
No description provided.