From 1e5b0a5c892750c36d3f53e2d825632a81ed98d1 Mon Sep 17 00:00:00 2001
From: Riccardo Manfrin <3090891+riccardomanfrin@users.noreply.github.com>
Date: Fri, 24 Jul 2026 15:23:22 +0200
Subject: [PATCH] [client] Make Test_ConnectPeers deterministic under
Docker/eBPF kernel / Darwin CI (#6884)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Describe your changes
Make `Test_ConnectPeers` deterministic. Two issues, both surfaced once
the
privileged suite moved into a `--privileged` Docker container (#6425):
1. The peers used `getLocalIP()` as their WireGuard endpoint, i.e. the
host's
routable NIC IP (the docker bridge IP `172.17.0.2` in CI). That address
might
not hairpin reliably inside the container, so the handshake
intermittently
timed out (flaky). Use loopback (`127.1.0.x`) instead — always
self-reachable.
2. On a Linux runner with the WG kernel module the iface uses the eBPF
proxy
factory. Its manager is a singleton with one shared XDP program +
settings
map, so bringing up the two ifaces makes the second factory overwrite
the
first's `wg_port`/`proxy_port` and the handshake is dropped. The test is
incompatible with the eBPF factory, so disable it via
`NB_DISABLE_EBPF_WG_PROXY` (peers then handshake directly over
loopback).
Running the suite across all three modes (eBPF / UDP proxy / ICE bind)
would
need a larger refactor.
Also fixes a typo in the `ErrSharedSockStopped` message (`socked` →
`socket`).
## Issue ticket number and link
No public issue — CI flakiness follow-up to #6871 on `Test_ConnectPeers`
(https://github.com/netbirdio/netbird/blob/main/client/iface/iface_test.go).
## Stack
### Checklist
- [x] Is it a bug fix
- [ ] Is a typo/documentation fix
- [ ] Is a feature enhancement
- [ ] It is a refactor
- [ ] Created tests that fail without the change (if possible)
> By submitting this pull request, you confirm that you have read and
agree to the terms of the [Contributor License
Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md).
## Documentation
Select exactly one:
- [ ] I added/updated documentation for this change
- [x] Documentation is **not needed** for this change (explain why)
Test-only change (plus a log-string typo). No public API, CLI, config,
or
behavior change.
### Docs PR URL (required if "docs added" is checked)
Paste the PR link from https://github.com/netbirdio/docs here:
N/A
---
Need help on this PR? Tag @codesmith-bot with what you
need. Autofix is disabled.
## Summary by CodeRabbit
* **Bug Fixes**
* Corrected the “shared socket stopped” error message for clearer
output.
* **Tests**
* Improved peer connection test reliability in privileged CI by
disabling the eBPF WireGuard proxy and using fixed loopback UDP
endpoints for deterministic setup.
---
client/iface/iface_test.go | 38 ++++++--------------------------------
sharedsock/sock_linux.go | 2 +-
2 files changed, 7 insertions(+), 33 deletions(-)
diff --git a/client/iface/iface_test.go b/client/iface/iface_test.go
index 43b3d8168..89c8cd16e 100644
--- a/client/iface/iface_test.go
+++ b/client/iface/iface_test.go
@@ -464,6 +464,8 @@ func Test_RemovePeer(t *testing.T) {
}
func Test_ConnectPeers(t *testing.T) {
+ t.Setenv("NB_DISABLE_EBPF_WG_PROXY", "true")
+
peer1ifaceName := fmt.Sprintf("utun%d", WgIntNumber+400)
peer1wgIP := netip.MustParsePrefix("10.99.99.17/30")
peer1Key, _ := wgtypes.GeneratePrivateKey()
@@ -505,12 +507,8 @@ func Test_ConnectPeers(t *testing.T) {
t.Fatal(err)
}
- localIP, err := getLocalIP()
- if err != nil {
- t.Fatal(err)
- }
-
- peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", localIP, peer1wgPort))
+ localIP1 := "127.0.0.1"
+ peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", localIP1, peer1wgPort))
if err != nil {
t.Fatal(err)
}
@@ -546,7 +544,8 @@ func Test_ConnectPeers(t *testing.T) {
t.Fatal(err)
}
- peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", localIP, peer2wgPort))
+ localIP2 := "127.0.0.1"
+ peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", localIP2, peer2wgPort))
if err != nil {
t.Fatal(err)
}
@@ -621,28 +620,3 @@ func getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
}
return wgtypes.Peer{}, fmt.Errorf("peer not found")
}
-
-func getLocalIP() (string, error) {
- // Get all interfaces
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- return "", err
- }
-
- for _, addr := range addrs {
- ipNet, ok := addr.(*net.IPNet)
- if !ok {
- continue
- }
- if ipNet.IP.IsLoopback() {
- continue
- }
-
- if ipNet.IP.To4() == nil {
- continue
- }
- return ipNet.IP.String(), nil
- }
-
- return "", fmt.Errorf("no local IP found")
-}
diff --git a/sharedsock/sock_linux.go b/sharedsock/sock_linux.go
index 4855e1aed..150e8a722 100644
--- a/sharedsock/sock_linux.go
+++ b/sharedsock/sock_linux.go
@@ -24,7 +24,7 @@ import (
)
// ErrSharedSockStopped indicates that shared socket has been stopped
-var ErrSharedSockStopped = fmt.Errorf("shared socked stopped")
+var ErrSharedSockStopped = fmt.Errorf("shared socket stopped")
// SharedSocket is a net.PacketConn that initiates two raw sockets (ipv4 and ipv6) and listens to UDP packets filtered
// by BPF instructions (e.g., IncomingSTUNFilter that checks and sends only STUN packets to the listeners (ReadFrom)).