Skip to content

fix(stats): report bytes received from a WireGuard peer as uplink - #131

Open
Free-Guy-IR wants to merge 2 commits into
PasarGuard:devfrom
Free-Guy-IR:upn/stats-direction-w
Open

Free-Guy-IR wants to merge 2 commits into
PasarGuard:devfrom
Free-Guy-IR:upn/stats-direction-w

Conversation

@Free-Guy-IR

@Free-Guy-IR Free-Guy-IR commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #126

Found by code review and confirmed with real wireguard-go counters; never seen in production. Checked against node dev 9ccd263.

What

buildDeltaStats (pkg/stats/interface_counters.go:51-75 at 9ccd263) emitted tx as uplink and rx as downlink. For WireGuard, Rx is filled from wgctrl's peer.ReceiveBytes (backend/wireguard/jobs.go:104-110), which is the client's upload. So per-user stats and the interface outbound stats reported the directions swapped compared with the xray backend, where uplink is client-to-server traffic.

This PR emits rx as uplink and tx as downlink.

Impact

Display only. Totals do not change, so billing and limits are not affected: the panel (dev f92acaf51cec) sums per-user stats regardless of direction, and a node's limit uses downlink + uplink. Only the uplink/downlink split shown for WireGuard nodes and users changes. The panel has no WireGuard-specific handling of directions, so there is no double swap.

This corrects new samples only. History already recorded for WireGuard nodes keeps the swapped split; the PR does not rewrite past data.

MTProto (on dev since #120) is not affected. It builds its own stats in backend/mtproto/tracker.go:265-274, with uplink = telemt's octets from the client, and does not import pkg/stats. Its tests pass on the merged tree (below).

Changes

diff --git a/pkg/stats/interface_counters.go b/pkg/stats/interface_counters.go
index 4bdadb8f..0b7bd50d 100644
--- a/pkg/stats/interface_counters.go
+++ b/pkg/stats/interface_counters.go
@@ -54,20 +54,20 @@ func buildDeltaStats(name, link string, rx, tx int64) []*common.Stat {
 	}
 
 	stats := make([]*common.Stat, 0, 2)
-	if tx > 0 {
+	if rx > 0 {
 		stats = append(stats, &common.Stat{
 			Name:  name,
 			Type:  "uplink",
 			Link:  link,
-			Value: tx,
+			Value: rx,
 		})
 	}
-	if rx > 0 {
+	if tx > 0 {
 		stats = append(stats, &common.Stat{
 			Name:  name,
 			Type:  "downlink",
 			Link:  link,
-			Value: rx,
+			Value: tx,
 		})
 	}
 

Existing tests: nine tests (six in pkg/stats/tracker_test.go, three in backend/wireguard/stats_outbound_test.go) asserted the swapped mapping and fail with only the fix applied. This PR exchanges their uplink/downlink labels, and in stats_outbound_test.go also the direction named in the failure messages. The expected values are unchanged.

New test, pkg/stats/direction_test.go: TestBytesReceivedFromAWireGuardPeerAreReportedAsUplink and TestInterfaceBytesReceivedAreReportedAsUplink. They use small fixed numbers (Rx 1000, Tx 7). The direction itself was checked with real counters (next section).

Real counters

Real userspace wireguard-go (golang.zx2c4.com/wireguard v0.0.0-20261024220952-b3bc704ef0ee, from the node's go.mod): two devices with gVisor netstack TUNs, UDP on 127.0.0.1, macOS arm64. The client uploaded 1 MiB over TCP through the tunnel. The server device's counters for the client peer were fed into the node's pkg/stats.

On dev 9ccd263:

after 1 MiB client upload: server peer(client) rx_bytes=1114692 tx_bytes=5372 -> node GetUsersStats uplink=5372 downlink=1114692; BuildInterfaceStats uplink=5372 downlink=1114692

With this PR (626952c):

after 1 MiB client upload: server peer(client) rx_bytes=1114692 tx_bytes=4316 -> node GetUsersStats uplink=1114692 downlink=4316; BuildInterfaceStats uplink=1114692 downlink=4316

On the tree of this PR merged onto dev: uplink=1114788 downlink=7964. The counts include WireGuard and TCP overhead (rx_bytes 1114692 for 1048576 bytes of payload) and differ slightly between runs.

Real Xray 26.3.27 with the same 1 MiB upload reports inbound and outbound uplink = 1048576.

Not measured: Linux kernel WireGuard, and the netlink interface counters that feed BuildInterfaceStats in the backend. In the run above BuildInterfaceStats was fed the peer counters, to show its mapping only.

Verification

go test -race -count=1 -v, Go 1.27.3, macOS arm64.

Before, on dev 9ccd263 with only this PR's test files:

    direction_test.go:17: peer Rx=1000 (client upload) Tx=7 (client download) reported as uplink=7 downlink=1000
--- FAIL: TestBytesReceivedFromAWireGuardPeerAreReportedAsUplink (0.00s)
    direction_test.go:27: interface rx=1000 tx=7 reported as uplink=7 downlink=1000
--- FAIL: TestInterfaceBytesReceivedAreReportedAsUplink (0.00s)

The nine relabelled tests fail there too (11 failures in total).

After, on 626952c:

ok  	github.com/pasarguard/node/pkg/stats	1.614s
ok  	github.com/pasarguard/node/backend/wireguard	1.550s

On this PR merged onto dev 9ccd263 (git merge-tree --write-tree is clean):

ok  	github.com/pasarguard/node/pkg/stats	1.242s
ok  	github.com/pasarguard/node/backend/wireguard	1.961s
ok  	github.com/pasarguard/node/backend/mtproto	65.236s

@coderabbitai

coderabbitai Bot commented Nov 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2b78c5b0-df0e-4d6e-a72f-63620ae5106b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant