From syzbot+9ca7674fa7521a3f1bc2 at syzkaller.appspotmail.com Mon Jun 1 14:33:31 2026 From: syzbot+9ca7674fa7521a3f1bc2 at syzkaller.appspotmail.com (syzbot) Date: Mon, 01 Jun 2026 07:33:31 -0700 Subject: [syzbot] [wireguard?] KCSAN: data-race in wg_socket_send_skb_to_peer / wg_socket_send_skb_to_peer (9) Message-ID: <6a1d983b.b111c304.35cd64.0028.GAE@google.com> Hello, syzbot found the following issue on: HEAD commit: 9215e74f228f Merge tag 'block-7.1-20260529' of git://git.k.. git tree: upstream console output: https://syzkaller.appspot.com/x/log.txt?x=10465ef2580000 kernel config: https://syzkaller.appspot.com/x/.config?x=f571f22917457cd8 dashboard link: https://syzkaller.appspot.com/bug?extid=9ca7674fa7521a3f1bc2 compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8 Unfortunately, I don't have any reproducer for this issue yet. Downloadable assets: disk image: https://storage.googleapis.com/syzbot-assets/1ddf3069118d/disk-9215e74f.raw.xz vmlinux: https://storage.googleapis.com/syzbot-assets/0913e4ffbdb8/vmlinux-9215e74f.xz kernel image: https://storage.googleapis.com/syzbot-assets/3fe3943ae796/bzImage-9215e74f.xz IMPORTANT: if you fix the issue, please add the following tag to the commit: Reported-by: syzbot+9ca7674fa7521a3f1bc2 at syzkaller.appspotmail.com ================================================================== BUG: KCSAN: data-race in wg_socket_send_skb_to_peer / wg_socket_send_skb_to_peer read-write to 0xffff88811af99028 of 8 bytes by task 310 on cpu 1: wg_socket_send_skb_to_peer+0xe8/0x130 drivers/net/wireguard/socket.c:182 wg_socket_send_buffer_to_peer+0xf1/0x120 drivers/net/wireguard/socket.c:199 wg_packet_send_handshake_initiation drivers/net/wireguard/send.c:40 [inline] wg_packet_handshake_send_worker+0x10d/0x160 drivers/net/wireguard/send.c:51 process_one_work kernel/workqueue.c:3314 [inline] process_scheduled_works+0x4f0/0x9c0 kernel/workqueue.c:3397 worker_thread+0x58a/0x780 kernel/workqueue.c:3478 kthread+0x22a/0x280 kernel/kthread.c:436 ret_from_fork+0x146/0x330 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 read-write to 0xffff88811af99028 of 8 bytes by task 15360 on cpu 0: wg_socket_send_skb_to_peer+0xe8/0x130 drivers/net/wireguard/socket.c:182 wg_packet_create_data_done drivers/net/wireguard/send.c:251 [inline] wg_packet_tx_worker+0x12d/0x330 drivers/net/wireguard/send.c:276 process_one_work kernel/workqueue.c:3314 [inline] process_scheduled_works+0x4f0/0x9c0 kernel/workqueue.c:3397 worker_thread+0x58a/0x780 kernel/workqueue.c:3478 kthread+0x22a/0x280 kernel/kthread.c:436 ret_from_fork+0x146/0x330 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 value changed: 0x0000000000000a2c -> 0x0000000000000ac0 Reported by Kernel Concurrency Sanitizer on: CPU: 0 UID: 0 PID: 15360 Comm: kworker/0:2 Tainted: G W syzkaller #0 PREEMPT(lazy) Tainted: [W]=WARN Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026 Workqueue: wg-crypt-wg2 wg_packet_tx_worker ================================================================== --- This report is generated by a bot. It may contain errors. See https://goo.gl/tpsmEJ for more information about syzbot. syzbot engineers can be reached at syzkaller at googlegroups.com. syzbot will keep track of this issue. See: https://goo.gl/tpsmEJ#status for how to communicate with syzbot. If the report is already addressed, let syzbot know by replying with: #syz fix: exact-commit-title If you want to overwrite report's subsystems, reply with: #syz set subsystems: new-subsystem (See the list of subsystem names on the web dashboard) If the report is a duplicate of another one, reply with: #syz dup: exact-subject-of-another-report If you want to undo deduplication, reply with: #syz undup From rfrohl at suse.com Mon Jun 8 13:36:10 2026 From: rfrohl at suse.com (Robert Frohl) Date: Mon, 8 Jun 2026 15:36:10 +0200 Subject: [PATCH] Revert "dns-hatchet: apply resolv.conf's selinux context to new resolv.conf" Message-ID: <20260608133610.108416-1-rfrohl@suse.com> This reverts commit 2ce4680bd34f371aacd3c09673c3c907274321cd. selinux does not allow every domain to set file contexts and will raise relabelto/relabelfrom AVCs and block these changes if a domain tries to update the selinux context. It is better to ignore selinux and leave the proper labeling to the selinux policy, which can add proper file transitions for the right context. This also allows for a cleaner change in the selinux policy, because otherwise it will need infrastructure to hide the relabel AVCs as well. For reference please see the selinux policy PR: https://github.com/fedora-selinux/selinux-policy/pull/3030 Signed-off-by: Robert Frohl --- contrib/dns-hatchet/hatchet.bash | 2 -- 1 file changed, 2 deletions(-) diff --git a/contrib/dns-hatchet/hatchet.bash b/contrib/dns-hatchet/hatchet.bash index bc4d090..6f167cc 100644 --- a/contrib/dns-hatchet/hatchet.bash +++ b/contrib/dns-hatchet/hatchet.bash @@ -20,11 +20,9 @@ set_dns() { [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}" } | unshare -m --propagation shared bash -c "$(cat <<-_EOF set -e - context="\$(stat -c %C /etc/resolv.conf 2>/dev/null)" || unset context mount --make-private /dev/shm mount -t tmpfs none /dev/shm cat > /dev/shm/resolv.conf - [[ -z \$context || \$context == "?" ]] || chcon "\$context" /dev/shm/resolv.conf 2>/dev/null || true mount -o remount,ro /dev/shm mount -o bind,ro /dev/shm/resolv.conf /etc/resolv.conf _EOF -- 2.53.0 From Jason at zx2c4.com Fri Jun 12 17:04:47 2026 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Fri, 12 Jun 2026 19:04:47 +0200 Subject: [PATCH] Revert "dns-hatchet: apply resolv.conf's selinux context to new resolv.conf" In-Reply-To: <20260608133610.108416-1-rfrohl@suse.com> References: <20260608133610.108416-1-rfrohl@suse.com> Message-ID: On Mon, Jun 08, 2026 at 03:36:10PM +0200, Robert Frohl wrote: > This reverts commit 2ce4680bd34f371aacd3c09673c3c907274321cd. > > selinux does not allow every domain to set file contexts and will raise > relabelto/relabelfrom AVCs and block these changes if a domain tries to update > the selinux context. > > It is better to ignore selinux and leave the proper labeling to the > selinux policy, which can add proper file transitions for the right > context. Does any existent selinux policy have anything to handle this? Or is this purely speculative/future-facing? Also, wondering if any distros are still shipping the hatchet. From rfrohl at suse.com Wed Jun 17 15:31:48 2026 From: rfrohl at suse.com (Robert Frohl) Date: Wed, 17 Jun 2026 17:31:48 +0200 Subject: [PATCH] Revert "dns-hatchet: apply resolv.conf's selinux context to new resolv.conf" In-Reply-To: References: <20260608133610.108416-1-rfrohl@suse.com> Message-ID: Sorry about the delay, the message ended up in spam and I only found it today. On 6/12/26 7:04 PM, Jason A. Donenfeld wrote: > On Mon, Jun 08, 2026 at 03:36:10PM +0200, Robert Frohl wrote: >> This reverts commit 2ce4680bd34f371aacd3c09673c3c907274321cd. >> >> selinux does not allow every domain to set file contexts and will raise >> relabelto/relabelfrom AVCs and block these changes if a domain tries to update >> the selinux context. >> >> It is better to ignore selinux and leave the proper labeling to the >> selinux policy, which can add proper file transitions for the right >> context. > > Does any existent selinux policy have anything to handle this? Or is this > purely speculative/future-facing? The Fedora selinux policy (which is also the openSUSE upstream) does not have anything to handle the DNS hatchet [0]. I did also check the refpolicy. There a slightly different wireguard implementation exists [1], but the dns-hatchet is not yet covered. Beyond that I am not sure who else is maintaining a selinux policy and may have implemented a solution for wireguard. I briefly checked android's sepolicy and I could not find anything regarding wireguard. More generically I do not think that the manual relabeling in hatchet.bash could have ever worked without a change in the respective selinux policies, because a process labeling files would need to have special privileges to do so. Which would mean in practice that it needs specific allow rules in the policy for changing the labels. At the same time a file transition could automatically assign the correct context to the new 'resolv.conf' and would be more concise, as it only needs one rule. Meaning for both the SELinux policy needs to be adjusted. > > Also, wondering if any distros are still shipping the hatchet. We are shipping a slightly modified version with oS TW [2] and I just learned that Fedora disabled the hatchet with Fedora 33 [3]. In that case I would switch my approach to have the DNS hatchet policy addition only in our downstream SELinux policy, as it might also explain why Fedora was not to keen to have the policy change. [0] https://github.com/fedora-selinux/selinux-policy/blob/rawhide/policy/modules/contrib/wireguard.te [1] https://github.com/SELinuxProject/refpolicy/blob/main/policy/modules/services/wireguard.te [2] https://build.opensuse.org/package/show/network:vpn:wireguard/wireguard-tools [3] https://src.fedoraproject.org/rpms/wireguard-tools/blob/rawhide/f/wireguard-tools.spec#_33 -- SELinux Security Engineer, SUSE Software Solutions Germany GmbH, Frankenstra?e 146, 90461 N?rnberg, Germany, GF: Jochen Jaser, Andrew McDonald, Werner Knoblich GPG: D29F 82AA 9FD5 9D6E 74B1 6370 089E DB3D 230A 2404 From toke at toke.dk Fri Jun 19 15:56:34 2026 From: toke at toke.dk (Toke =?utf-8?Q?H=C3=B8iland-J=C3=B8rgensen?=) Date: Fri, 19 Jun 2026 17:56:34 +0200 Subject: Wireguard head of line blocking when CPUs saturate Message-ID: <874iiyfrrh.fsf@toke.dk> Hey everyone I'm running Wireguard on my main gateway, which is a not-super-high powered ARM box with eight cores (based on the NXP LS1088A SoC). The box does, however, also have eight hardware queues for its networking, which means regular network traffic can be spread nicely across the cores. However, the per-core performance is limited, making it pretty trivial to saturate a single core by just running a fat TCP flow through it. And when this happens, Wireguard traffic just... stalls. I.e., no traffic gets through the Wireguard interface until the (unrelated) flow saturating one of the cores subsides. I suspect what happens is that Wireguard spreads out traffic to all cores for encryption, but has to wait for the respective CPUs to finish encrypting the packets in order before they can actually be transmitted. And because one CPU is now suddenly saturated in softirq context, the Wireguard work queue never gets a chance to run on that CPU, stalling TX progress for the Wireguard device entirely. I'm sending this message to (a) see if anyone else is seeing the same kind of stalling, and (b) to get input on whether the explanation outlined above seems plausible. And, in the case of affirmative answers to both (a) and (b), to hopefully start a discussion on what to do about this :) -Toke From tytso at mit.edu Mon Jun 29 02:34:17 2026 From: tytso at mit.edu (Theodore Tso) Date: Sun, 28 Jun 2026 22:34:17 -0400 Subject: [PATCH] Wireguard: Fix data-race in rx/tx counter In-Reply-To: References: <20260628203823.144789-1-rafael@rcpassos.me> Message-ID: On Sun, Jun 28, 2026 at 11:02:05PM -0500, Andrew Lunn wrote: > On Sun, Jun 28, 2026 at 05:38:23PM -0300, Rafael Passos wrote: > > fixes data-race in {rx/tx}_bytes counter for wireguard connection. > > these values were incremented inside a read_lock_bh block, but write > > protections were missing. making them atomic was the simplest way out. > > This was found by syzbot with kcsan. > > Atomics are expensive in general, especially on high CPU count > systems. > > Statistic counters tend to be very asymmetric in usage. They are > incremented frequently, maybe per packet, but reported very > infrequently, maybe every minute when an SNMP agent reads them. One of the reasons why kcsan and syzbot can be quite noisy is that a human being needs to *think* and consider whether or not this is actually important. (One of the reasons why I'm not all that worried about our new AI overlords taking over the world. :-) Consider what is the worst that might happen if the tx/rx_bytes counter might not be completely accurate? Is it worth the performance penalty of using atomics (or the memory overhead of per-CPU counters)? - Ted From akawolf0 at gmail.com Mon Jun 8 00:33:20 2026 From: akawolf0 at gmail.com (Artjom Vejsel) Date: Mon, 08 Jun 2026 00:33:20 -0000 Subject: [PATCH wireguard-go 0/3] conn: fix sticky source address on multi-IP hosts Message-ID: <20260608003307.2598372-1-akawolf0@gmail.com> Three independent bugs in conn/bind_std.go cause the sticky source address feature to silently fail on hosts with multiple addresses on the listening interface. With UDP_GRO enabled (Linux 5.12+) the brokenness compounds: even after fixing PKTINFO delivery on the PacketConn, coalesced reads drop the cmsg on every frame past the first. Symptom on a real-world setup: a Linux box with a static 2001:db8::100 and a DHCPv6-assigned 2001:db8::943 on enp1s0, where the default route's preferred source is ::943. A peer initiates a handshake to [::100]:port; the server responds from ::943; the peer rejects the response because the source IP does not match its configured endpoint; the session never establishes. The user has to install a route hack (ip -6 route ... src ::100) as a workaround. After this series, the kernel's PKTINFO ancillary data correctly threads through the PacketConn, recvmmsg/UDP_GRO split path, and back into the outgoing sendmsg cmsg, so the response is emitted from the address the peer reached, regardless of route source preference. Reproduction via netns, verified end-to-end: sudo ip netns add srvns && sudo ip netns add cltns sudo ip link add veth-srv type veth peer name veth-srvNS sudo ip link add veth-clt type veth peer name veth-cltNS sudo ip link set veth-srvNS netns srvns sudo ip link set veth-cltNS netns cltns sudo ip link add br-test type bridge sudo ip link set veth-srv master br-test sudo ip link set veth-clt master br-test sudo ip link set veth-srv up sudo ip link set veth-clt up sudo ip link set br-test up sudo ip netns exec srvns ip link set veth-srvNS up sudo ip netns exec srvns ip -6 addr add fd00:1::100/64 dev veth-srvNS sudo ip netns exec srvns ip -6 addr add fd00:1::943/64 dev veth-srvNS sudo ip netns exec cltns ip link set veth-cltNS up sudo ip netns exec cltns ip -6 addr add fd00:1::1/64 dev veth-cltNS sleep 3 # wait for DAD sudo ip netns exec srvns ip -6 route get fd00:1::1 # confirms: src fd00:1::943 (the preferred source from /64) Run wireguard-go in each netns with the client's Endpoint set to [fd00:1::100]:port. Before the series, tcpdump -i br-test shows the handshake response from ::943 and the client immediately roams its endpoint to ::943. After the series, the response is from ::100 and the client's endpoint stays as configured. Each patch is independent and reviewable on its own: 1/3 enables PKTINFO delivery on the receive path so ep.src actually gets populated; 2/3 fixes a slice-length pool-reuse bug in Send() that truncates the destination address after v4/v6 transitions; 3/3 restores PKTINFO on each frame produced by splitCoalescedMessages so UDP_GRO offload no longer defeats the fix from 1/3. conn/bind_std.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) -- 2.54.0 From akawolf0 at gmail.com Mon Jun 8 00:33:21 2026 From: akawolf0 at gmail.com (Artjom Vejsel) Date: Mon, 08 Jun 2026 00:33:21 -0000 Subject: [PATCH wireguard-go 1/3] conn: enable IP_PKTINFO ancillary data on PacketConn In-Reply-To: <20260608003307.2598372-1-akawolf0@gmail.com> References: <20260608003307.2598372-1-akawolf0@gmail.com> Message-ID: <20260608003307.2598372-2-akawolf0@gmail.com> The IPV6_RECVPKTINFO/IP_PKTINFO setsockopts are set during socket creation via the ListenConfig control function. However, x/net/ipv4 and x/net/ipv6 PacketConn instances maintain their own internal control message option tracking (rawOpt), which is consulted during recvmmsg unpacking. Without calling SetControlMessage(FlagDst|FlagInterface, true), the PacketConn does not advertise these flags to the unpack path, and the resulting Message.NN remains zero on every read. Because Message.NN is zero, getSrcFromControl never finds a PKTINFO cmsg, StdNetEndpoint.src is never populated, and setSrcControl writes empty control bytes on send. The kernel then selects the outgoing source address by route lookup (RFC 6724) rather than honoring the destination address of the incoming packet. The sticky source address behavior is silently broken on hosts with multiple addresses on the listening interface. Reproduced with a userspace bind on a Linux interface holding two global IPv6 addresses, where the route preferred source differs from the address the peer initiated the handshake on: the server responded from the route-preferred address, the peer rejected the response, and the session never established. Signed-off-by: Artjom Vejsel --- conn/bind_std.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conn/bind_std.go b/conn/bind_std.go index f5c8816..c88ed4e 100644 --- a/conn/bind_std.go +++ b/conn/bind_std.go @@ -177,6 +177,7 @@ again: s.ipv4TxOffload, s.ipv4RxOffload = supportsUDPOffload(v4conn) if runtime.GOOS == "linux" || runtime.GOOS == "android" { v4pc = ipv4.NewPacketConn(v4conn) + _ = v4pc.SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true) s.ipv4PC = v4pc } fns = append(fns, s.makeReceiveIPv4(v4pc, v4conn, s.ipv4RxOffload)) @@ -186,6 +187,7 @@ again: s.ipv6TxOffload, s.ipv6RxOffload = supportsUDPOffload(v6conn) if runtime.GOOS == "linux" || runtime.GOOS == "android" { v6pc = ipv6.NewPacketConn(v6conn) + _ = v6pc.SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true) s.ipv6PC = v6pc } fns = append(fns, s.makeReceiveIPv6(v6pc, v6conn, s.ipv6RxOffload)) -- 2.54.0 From akawolf0 at gmail.com Mon Jun 8 00:33:23 2026 From: akawolf0 at gmail.com (Artjom Vejsel) Date: Mon, 08 Jun 2026 00:33:23 -0000 Subject: [PATCH wireguard-go 2/3] conn: reset udpAddrPool slice length before copy In-Reply-To: <20260608003307.2598372-1-akawolf0@gmail.com> References: <20260608003307.2598372-1-akawolf0@gmail.com> Message-ID: <20260608003307.2598372-3-akawolf0@gmail.com> The pooled net.UDPAddr is allocated once with IP = make([]byte, 16) and reused indiscriminately for both IPv4 and IPv6 sends. The current code copies the destination address first and then truncates the slice to the desired length, which works in isolation but corrupts the address on pool reuse: 1. v4 send: copy(ua.IP, as4[:]) writes 4 bytes, then ua.IP = ua.IP[:4] truncates the slice. 2. ua is returned to the pool with len(IP) == 4. 3. v6 send: copy(ua.IP, as16[:]) copies only min(len(dst), len(src)) == 4 bytes (the slice header still says len == 4), then ua.IP = ua.IP[:16] re-extends the slice over the stale tail. The result is an outgoing IPv6 datagram with the upper 4 bytes of the intended destination address followed by 12 stale bytes from prior sends. tcpdump shows truncated destinations like 2001:db8:: instead of 2001:db8::aaa, and either drops the packet or routes it to a wrong peer. Resize the slice before copy so the destination is written into the full address space and any stale bytes are overwritten. Signed-off-by: Artjom Vejsel --- conn/bind_std.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conn/bind_std.go b/conn/bind_std.go index c88ed4e..72f3047 100644 --- a/conn/bind_std.go +++ b/conn/bind_std.go @@ -368,13 +368,13 @@ func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error { ua := s.udpAddrPool.Get().(*net.UDPAddr) defer s.udpAddrPool.Put(ua) if is6 { + ua.IP = ua.IP[:16] as16 := endpoint.DstIP().As16() copy(ua.IP, as16[:]) - ua.IP = ua.IP[:16] } else { + ua.IP = ua.IP[:4] as4 := endpoint.DstIP().As4() copy(ua.IP, as4[:]) - ua.IP = ua.IP[:4] } ua.Port = int(endpoint.(*StdNetEndpoint).Port()) var ( -- 2.54.0 From akawolf0 at gmail.com Mon Jun 8 00:33:24 2026 From: akawolf0 at gmail.com (Artjom Vejsel) Date: Mon, 08 Jun 2026 00:33:24 -0000 Subject: [PATCH wireguard-go 3/3] conn: propagate ancillary data to coalesced split frames In-Reply-To: <20260608003307.2598372-1-akawolf0@gmail.com> References: <20260608003307.2598372-1-akawolf0@gmail.com> Message-ID: <20260608003307.2598372-4-akawolf0@gmail.com> splitCoalescedMessages divides a single coalesced read into individual frames by copying Buffers[0][start:end], setting N, and propagating Addr to each child slot, but it does not propagate the control buffer (OOB) or its length (NN). The parent buffer holds the cmsg returned by recvmmsg for the entire coalesced datagram, including the PKTINFO that identifies the destination address of the original packet. The child slots retain whatever control content was left over from a previous recv, with NN == 0 for unused slots and stale data for reused ones. receiveIP then calls getSrcFromControl(msg.OOB[:msg.NN], ep) on each emitted message. Because NN is zero for every split frame except the first (which still aliases the parent's storage), StdNetEndpoint.src is never populated for the rest, and setSrcControl on the corresponding send writes empty control bytes. The kernel falls back to route-based source selection, defeating the sticky source address mechanism whenever UDP_GRO coalesces packets ? which is the common case on Linux 5.12+. Copy the parent's control buffer and length into each split child. The n == i case is skipped because the destination slot is the parent itself and the slices already alias. Signed-off-by: Artjom Vejsel --- conn/bind_std.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/conn/bind_std.go b/conn/bind_std.go index 72f3047..88c0ce3 100644 --- a/conn/bind_std.go +++ b/conn/bind_std.go @@ -528,6 +528,11 @@ func splitCoalescedMessages(msgs []ipv6.Message, firstMsgAt int, getGSO getGSOFu copied := copy(msgs[n].Buffers[0], msg.Buffers[0][start:end]) msgs[n].N = copied msgs[n].Addr = msg.Addr + if n != i { + oobCopied := copy(msgs[n].OOB[:cap(msgs[n].OOB)], msg.OOB[:msg.NN]) + msgs[n].OOB = msgs[n].OOB[:oobCopied] + msgs[n].NN = oobCopied + } start = end end += gsoSize if end > msg.N { -- 2.54.0 From akawolf0 at gmail.com Tue Jun 9 22:01:51 2026 From: akawolf0 at gmail.com (Artjom Vejsel) Date: Tue, 09 Jun 2026 22:01:51 -0000 Subject: [PATCH wireguard-go 0/3] conn: fix sticky source address on multi-IP hosts Message-ID: <20260609220138.4160252-1-akawolf0@gmail.com> Three independent bugs in conn/bind_std.go cause the sticky source address feature to silently fail on hosts with multiple addresses on the listening interface. With UDP_GRO enabled (Linux 5.12+) the brokenness compounds: even after fixing PKTINFO delivery on the PacketConn, coalesced reads drop the cmsg on every frame past the first. Symptom on a real-world setup: a Linux box with a static 2001:db8::100 and a DHCPv6-assigned 2001:db8::943 on enp1s0, where the default route's preferred source is ::943. A peer initiates a handshake to [::100]:port; the server responds from ::943; the peer rejects the response because the source IP does not match its configured endpoint; the session never establishes. The user has to install a route hack (ip -6 route ... src ::100) as a workaround. After this series, the kernel's PKTINFO ancillary data correctly threads through the PacketConn, recvmmsg/UDP_GRO split path, and back into the outgoing sendmsg cmsg, so the response is emitted from the address the peer reached, regardless of route source preference. Reproduction via netns, verified end-to-end: sudo ip netns add srvns && sudo ip netns add cltns sudo ip link add veth-srv type veth peer name veth-srvNS sudo ip link add veth-clt type veth peer name veth-cltNS sudo ip link set veth-srvNS netns srvns sudo ip link set veth-cltNS netns cltns sudo ip link add br-test type bridge sudo ip link set veth-srv master br-test sudo ip link set veth-clt master br-test sudo ip link set veth-srv up sudo ip link set veth-clt up sudo ip link set br-test up sudo ip netns exec srvns ip link set veth-srvNS up sudo ip netns exec srvns ip -6 addr add fd00:1::100/64 dev veth-srvNS sudo ip netns exec srvns ip -6 addr add fd00:1::943/64 dev veth-srvNS sudo ip netns exec cltns ip link set veth-cltNS up sudo ip netns exec cltns ip -6 addr add fd00:1::1/64 dev veth-cltNS sleep 3 # wait for DAD sudo ip netns exec srvns ip -6 route get fd00:1::1 # confirms: src fd00:1::943 (the preferred source from /64) Run wireguard-go in each netns with the client's Endpoint set to [fd00:1::100]:port. Before the series, tcpdump -i br-test shows the handshake response from ::943 and the client immediately roams its endpoint to ::943. After the series, the response is from ::100 and the client's endpoint stays as configured. Each patch is independent and reviewable on its own: 1/3 enables PKTINFO delivery on the receive path so ep.src actually gets populated; 2/3 fixes a slice-length pool-reuse bug in Send() that truncates the destination address after v4/v6 transitions; 3/3 restores PKTINFO on each frame produced by splitCoalescedMessages so UDP_GRO offload no longer defeats the fix from 1/3. conn/bind_std.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) -- 2.54.0 From akawolf0 at gmail.com Tue Jun 9 22:01:52 2026 From: akawolf0 at gmail.com (Artjom Vejsel) Date: Tue, 09 Jun 2026 22:01:52 -0000 Subject: [PATCH wireguard-go 1/3] conn: enable IP_PKTINFO ancillary data on PacketConn In-Reply-To: <20260609220138.4160252-1-akawolf0@gmail.com> References: <20260609220138.4160252-1-akawolf0@gmail.com> Message-ID: <20260609220138.4160252-2-akawolf0@gmail.com> The IPV6_RECVPKTINFO/IP_PKTINFO setsockopts are set during socket creation via the ListenConfig control function. However, x/net/ipv4 and x/net/ipv6 PacketConn instances maintain their own internal control message option tracking (rawOpt), which is consulted during recvmmsg unpacking. Without calling SetControlMessage(FlagDst|FlagInterface, true), the PacketConn does not advertise these flags to the unpack path, and the resulting Message.NN remains zero on every read. Because Message.NN is zero, getSrcFromControl never finds a PKTINFO cmsg, StdNetEndpoint.src is never populated, and setSrcControl writes empty control bytes on send. The kernel then selects the outgoing source address by route lookup (RFC 6724) rather than honoring the destination address of the incoming packet. The sticky source address behavior is silently broken on hosts with multiple addresses on the listening interface. Reproduced with a userspace bind on a Linux interface holding two global IPv6 addresses, where the route preferred source differs from the address the peer initiated the handshake on: the server responded from the route-preferred address, the peer rejected the response, and the session never established. Signed-off-by: Artjom Vejsel --- conn/bind_std.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conn/bind_std.go b/conn/bind_std.go index f5c8816..c88ed4e 100644 --- a/conn/bind_std.go +++ b/conn/bind_std.go @@ -177,6 +177,7 @@ again: s.ipv4TxOffload, s.ipv4RxOffload = supportsUDPOffload(v4conn) if runtime.GOOS == "linux" || runtime.GOOS == "android" { v4pc = ipv4.NewPacketConn(v4conn) + _ = v4pc.SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true) s.ipv4PC = v4pc } fns = append(fns, s.makeReceiveIPv4(v4pc, v4conn, s.ipv4RxOffload)) @@ -186,6 +187,7 @@ again: s.ipv6TxOffload, s.ipv6RxOffload = supportsUDPOffload(v6conn) if runtime.GOOS == "linux" || runtime.GOOS == "android" { v6pc = ipv6.NewPacketConn(v6conn) + _ = v6pc.SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true) s.ipv6PC = v6pc } fns = append(fns, s.makeReceiveIPv6(v6pc, v6conn, s.ipv6RxOffload)) -- 2.54.0 From akawolf0 at gmail.com Tue Jun 9 22:01:53 2026 From: akawolf0 at gmail.com (Artjom Vejsel) Date: Tue, 09 Jun 2026 22:01:53 -0000 Subject: [PATCH wireguard-go 2/3] conn: reset udpAddrPool slice length before copy In-Reply-To: <20260609220138.4160252-1-akawolf0@gmail.com> References: <20260609220138.4160252-1-akawolf0@gmail.com> Message-ID: <20260609220138.4160252-3-akawolf0@gmail.com> The pooled net.UDPAddr is allocated once with IP = make([]byte, 16) and reused indiscriminately for both IPv4 and IPv6 sends. The current code copies the destination address first and then truncates the slice to the desired length, which works in isolation but corrupts the address on pool reuse: 1. v4 send: copy(ua.IP, as4[:]) writes 4 bytes, then ua.IP = ua.IP[:4] truncates the slice. 2. ua is returned to the pool with len(IP) == 4. 3. v6 send: copy(ua.IP, as16[:]) copies only min(len(dst), len(src)) == 4 bytes (the slice header still says len == 4), then ua.IP = ua.IP[:16] re-extends the slice over the stale tail. The result is an outgoing IPv6 datagram with the upper 4 bytes of the intended destination address followed by 12 stale bytes from prior sends. tcpdump shows truncated destinations like 2001:db8:: instead of 2001:db8::aaa, and either drops the packet or routes it to a wrong peer. Resize the slice before copy so the destination is written into the full address space and any stale bytes are overwritten. Signed-off-by: Artjom Vejsel --- conn/bind_std.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conn/bind_std.go b/conn/bind_std.go index c88ed4e..72f3047 100644 --- a/conn/bind_std.go +++ b/conn/bind_std.go @@ -368,13 +368,13 @@ func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error { ua := s.udpAddrPool.Get().(*net.UDPAddr) defer s.udpAddrPool.Put(ua) if is6 { + ua.IP = ua.IP[:16] as16 := endpoint.DstIP().As16() copy(ua.IP, as16[:]) - ua.IP = ua.IP[:16] } else { + ua.IP = ua.IP[:4] as4 := endpoint.DstIP().As4() copy(ua.IP, as4[:]) - ua.IP = ua.IP[:4] } ua.Port = int(endpoint.(*StdNetEndpoint).Port()) var ( -- 2.54.0 From akawolf0 at gmail.com Tue Jun 9 22:01:55 2026 From: akawolf0 at gmail.com (Artjom Vejsel) Date: Tue, 09 Jun 2026 22:01:55 -0000 Subject: [PATCH wireguard-go 3/3] conn: propagate ancillary data to coalesced split frames In-Reply-To: <20260609220138.4160252-1-akawolf0@gmail.com> References: <20260609220138.4160252-1-akawolf0@gmail.com> Message-ID: <20260609220138.4160252-4-akawolf0@gmail.com> splitCoalescedMessages divides a single coalesced read into individual frames by copying Buffers[0][start:end], setting N, and propagating Addr to each child slot, but it does not propagate the control buffer (OOB) or its length (NN). The parent buffer holds the cmsg returned by recvmmsg for the entire coalesced datagram, including the PKTINFO that identifies the destination address of the original packet. The child slots retain whatever control content was left over from a previous recv, with NN == 0 for unused slots and stale data for reused ones. receiveIP then calls getSrcFromControl(msg.OOB[:msg.NN], ep) on each emitted message. Because NN is zero for every split frame except the first (which still aliases the parent's storage), StdNetEndpoint.src is never populated for the rest, and setSrcControl on the corresponding send writes empty control bytes. The kernel falls back to route-based source selection, defeating the sticky source address mechanism whenever UDP_GRO coalesces packets ? which is the common case on Linux 5.12+. Copy the parent's control buffer and length into each split child. The n == i case is skipped because the destination slot is the parent itself and the slices already alias. Signed-off-by: Artjom Vejsel --- conn/bind_std.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/conn/bind_std.go b/conn/bind_std.go index 72f3047..88c0ce3 100644 --- a/conn/bind_std.go +++ b/conn/bind_std.go @@ -528,6 +528,11 @@ func splitCoalescedMessages(msgs []ipv6.Message, firstMsgAt int, getGSO getGSOFu copied := copy(msgs[n].Buffers[0], msg.Buffers[0][start:end]) msgs[n].N = copied msgs[n].Addr = msg.Addr + if n != i { + oobCopied := copy(msgs[n].OOB[:cap(msgs[n].OOB)], msg.OOB[:msg.NN]) + msgs[n].OOB = msgs[n].OOB[:oobCopied] + msgs[n].NN = oobCopied + } start = end end += gsoSize if end > msg.N { -- 2.54.0 From marqueandreprisal at duck.com Fri Jun 12 18:06:03 2026 From: marqueandreprisal at duck.com (marqueandreprisal at duck.com) Date: Fri, 12 Jun 2026 18:06:03 -0000 Subject: [PATCH] Revert "dns-hatchet: apply resolv.conf's selinux context to new resolv.conf" References: <20260608133610.108416-1-rfrohl@suse.com> <79C0C0A9-DE56-4165-B397-834E4C37F5A6.1@smtp-inbound1.duck.com> Message-ID: In some cases you may have to rebuild the kernel. Distros may or may not include dns-hatchet. Tools like this are meant to be used by hand therefore it is quite possible the developers will exclude it from shipping in release. From anton at sw4me.com Sun Jun 21 07:47:27 2026 From: anton at sw4me.com (Anton Kovalenko) Date: Sun, 21 Jun 2026 07:47:27 -0000 Subject: [PATCH wireguard-android] ui: add import config from clipboard Message-ID: <20260621074710.1106438-1-anton@sw4me.com> Adds a fourth action to the add-tunnel bottom sheet that reads plain text from the system clipboard and routes it through the existing text-import path (validate, name, create) used for QR codes. Signed-off-by: Anton Kovalenko --- The add-tunnel sheet currently offers file/archive, QR scan and create-from-scratch. This adds a clipboard option for the common case of getting a config as text over chat or email, routed through the existing QR-text import path so validation, naming and storage are unchanged. Built and tested on Samsung S24 Ultra, Android 16: a valid config imports and names correctly; empty or non-config clipboard shows the expected error snackbar. First patch I've sent to the list ? happy to adjust style or approach. Disclosure: this patch was written by Claude (Anthropic's AI), which I reviewed, built and tested. .../android/fragment/AddTunnelsSheet.kt | 9 +++++ .../android/fragment/TunnelListFragment.kt | 15 ++++++++ .../wireguard/android/util/ClipboardUtils.kt | 13 +++++++ ui/src/main/res/drawable/ic_action_paste.xml | 14 ++++++++ .../res/layout/add_tunnels_bottom_sheet.xml | 34 ++++++++++++++++--- ui/src/main/res/values/strings.xml | 2 ++ 6 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 ui/src/main/res/drawable/ic_action_paste.xml diff --git a/ui/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt b/ui/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt index 161709c3..d5f01b40 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt @@ -64,6 +64,10 @@ class AddTunnelsSheet : BottomSheetDialogFragment() { dismiss() onRequestImportConfig() } + dialog.findViewById(R.id.create_from_clipboard)?.setOnClickListener { + dismiss() + onRequestImportFromClipboard() + } dialog.findViewById(R.id.create_from_qrcode)?.setOnClickListener { dismiss() onRequestScanQRCode() @@ -89,6 +93,10 @@ class AddTunnelsSheet : BottomSheetDialogFragment() { setFragmentResult(REQUEST_KEY_NEW_TUNNEL, Bundle().apply { putString(REQUEST_METHOD, REQUEST_IMPORT) }) } + private fun onRequestImportFromClipboard() { + setFragmentResult(REQUEST_KEY_NEW_TUNNEL, Bundle().apply { putString(REQUEST_METHOD, REQUEST_CLIPBOARD) }) + } + private fun onRequestScanQRCode() { setFragmentResult(REQUEST_KEY_NEW_TUNNEL, Bundle().apply { putString(REQUEST_METHOD, REQUEST_SCAN) }) } @@ -98,6 +106,7 @@ class AddTunnelsSheet : BottomSheetDialogFragment() { const val REQUEST_METHOD = "request_method" const val REQUEST_CREATE = "request_create" const val REQUEST_IMPORT = "request_import" + const val REQUEST_CLIPBOARD = "request_clipboard" const val REQUEST_SCAN = "request_scan" } } diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt index 119b6afe..8bde6794 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt @@ -34,6 +34,7 @@ import com.wireguard.android.databinding.TunnelListFragmentBinding import com.wireguard.android.databinding.TunnelListItemBinding import com.wireguard.android.model.ObservableTunnel import com.wireguard.android.updater.SnackbarUpdateShower +import com.wireguard.android.util.ClipboardUtils import com.wireguard.android.util.ErrorMessages import com.wireguard.android.util.QrCodeFromFileScanner import com.wireguard.android.util.TunnelImporter @@ -83,6 +84,16 @@ class TunnelListFragment : BaseFragment() { private val snackbarUpdateShower = SnackbarUpdateShower(this) + private fun importFromClipboard() { + val context = context ?: return + val text = ClipboardUtils.readPlainText(context) + if (text.isNullOrBlank()) { + showSnackbar(getString(R.string.clipboard_no_text)) + return + } + TunnelImporter.importTunnel(parentFragmentManager, text.toString()) { showSnackbar(it) } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) if (savedInstanceState != null) { @@ -114,6 +125,10 @@ class TunnelListFragment : BaseFragment() { tunnelFileImportResultLauncher.launch("*/*") } + AddTunnelsSheet.REQUEST_CLIPBOARD -> { + importFromClipboard() + } + AddTunnelsSheet.REQUEST_SCAN -> { qrImportResultLauncher.launch( ScanOptions() diff --git a/ui/src/main/java/com/wireguard/android/util/ClipboardUtils.kt b/ui/src/main/java/com/wireguard/android/util/ClipboardUtils.kt index 8968979f..76eb2b9a 100644 --- a/ui/src/main/java/com/wireguard/android/util/ClipboardUtils.kt +++ b/ui/src/main/java/com/wireguard/android/util/ClipboardUtils.kt @@ -6,6 +6,7 @@ package com.wireguard.android.util import android.content.ClipData import android.content.ClipboardManager +import android.content.Context import android.os.Build import android.view.View import android.widget.TextView @@ -18,6 +19,18 @@ import com.wireguard.android.R * Standalone utilities for interacting with the system clipboard. */ object ClipboardUtils { + /** + * Returns the plain-text contents of the primary clip, or null if the + * clipboard is unavailable, empty, or holds no coercible text. + */ + @JvmStatic + fun readPlainText(context: Context): CharSequence? { + val service = context.getSystemService() ?: return null + val clip = service.primaryClip ?: return null + if (clip.itemCount == 0) return null + return clip.getItemAt(0).coerceToText(context) + } + @JvmStatic fun copyTextView(view: View) { val data = when (view) { diff --git a/ui/src/main/res/drawable/ic_action_paste.xml b/ui/src/main/res/drawable/ic_action_paste.xml new file mode 100644 index 00000000..1841b21e --- /dev/null +++ b/ui/src/main/res/drawable/ic_action_paste.xml @@ -0,0 +1,14 @@ + + + + diff --git a/ui/src/main/res/layout/add_tunnels_bottom_sheet.xml b/ui/src/main/res/layout/add_tunnels_bottom_sheet.xml index 0ad1ef23..310a2d75 100644 --- a/ui/src/main/res/layout/add_tunnels_bottom_sheet.xml +++ b/ui/src/main/res/layout/add_tunnels_bottom_sheet.xml @@ -18,22 +18,22 @@ android:layout_marginLeft="@dimen/normal_margin" android:layout_marginEnd="@dimen/normal_margin" android:layout_marginRight="@dimen/normal_margin" - android:nextFocusDown="@id/create_from_qrcode" - android:nextFocusForward="@id/create_from_qrcode" + android:nextFocusDown="@id/create_from_clipboard" + android:nextFocusForward="@id/create_from_clipboard" android:text="@string/create_from_file" android:textAlignment="viewStart" android:textColor="?attr/colorOnSurface" app:icon="@drawable/ic_action_open" app:iconPadding="@dimen/bottom_sheet_icon_padding" app:iconTint="?attr/colorSecondary" - app:layout_constraintBottom_toTopOf="@+id/create_from_qrcode" + app:layout_constraintBottom_toTopOf="@+id/create_from_clipboard" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="parent" app:rippleColor="?attr/colorSecondary" /> + + QR code not found in image QR code checksum verification failed Cancel + No text on the clipboard Cannot delete configuration file %s Configuration for ?%s? already exists Configuration file ?%s? already exists @@ -97,6 +98,7 @@ Cannot create local binary directory Cannot create file in downloads directory Create from scratch + Import from clipboard Import from file or archive Scan from QR code Cannot create output directory -- 2.34.1 From alhouseenyousef at gmail.com Wed Jun 24 12:30:27 2026 From: alhouseenyousef at gmail.com (Yousef Alhouseen) Date: Wed, 24 Jun 2026 12:30:27 -0000 Subject: [PATCH] selftests: wireguard: avoid extglob in netns CPU count Message-ID: <20260624123009.7008-1-alhouseenyousef@gmail.com> netns.sh enables extglob before using cpu+([0-9]) to count CPUs, but bash -n parses the whole script without executing that shopt command. This makes syntax checks fail even though the script is intended for bash. Use the ordinary cpu[0-9]* glob instead. It matches the same CPU directory names without requiring extglob, and lets bash validate the script syntax. Signed-off-by: Yousef Alhouseen --- tools/testing/selftests/wireguard/netns.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/wireguard/netns.sh b/tools/testing/selftests/wireguard/netns.sh index a8f550aec..98b423494 100755 --- a/tools/testing/selftests/wireguard/netns.sh +++ b/tools/testing/selftests/wireguard/netns.sh @@ -22,12 +22,11 @@ # interfaces in $ns1 and $ns2. See https://www.wireguard.com/netns/ for further # details on how this is accomplished. set -e -shopt -s extglob exec 3>&1 export LANG=C export WG_HIDE_KEYS=never -NPROC=( /sys/devices/system/cpu/cpu+([0-9]) ); NPROC=${#NPROC[@]} +NPROC=( /sys/devices/system/cpu/cpu[0-9]* ); NPROC=${#NPROC[@]} netns0="wg-test-$$-0" netns1="wg-test-$$-1" netns2="wg-test-$$-2" -- 2.54.0 From andrew at lunn.ch Sun Jun 28 21:02:18 2026 From: andrew at lunn.ch (Andrew Lunn) Date: Sun, 28 Jun 2026 21:02:18 -0000 Subject: [PATCH] Wireguard: Fix data-race in rx/tx counter In-Reply-To: <20260628203823.144789-1-rafael@rcpassos.me> References: <20260628203823.144789-1-rafael@rcpassos.me> Message-ID: On Sun, Jun 28, 2026 at 05:38:23PM -0300, Rafael Passos wrote: > fixes data-race in {rx/tx}_bytes counter for wireguard connection. > these values were incremented inside a read_lock_bh block, but write > protections were missing. making them atomic was the simplest way out. > This was found by syzbot with kcsan. > > Reported-by: syzbot+9ca7674fa7521a3f1bc2 at syzkaller.appspotmail.com > Link: https://syzkaller.appspot.com/bug?extid=9ca7674fa7521a3f1bc2 > Signed-off-by: Rafael Passos > --- > > Hi, > > I am posting this patch to better ilustrate the discussion. > If this is a non-issue, its fine. > As I mentioned in the previous email, this issue was reported by syzbot, > but I was not able to reproduce it. > I am also aware atomic calls may introduce extra cost on older arm cpus. Atomics are expensive in general, especially on high CPU count systems. Statistic counters tend to be very asymmetric in usage. They are incremented frequently, maybe per packet, but reported very infrequently, maybe every minute when an SNMP agent reads them. So the solution to statistic counters should reflect this. Increment should be very cheap, reporting them can be expensive. There are a few different solutions. Per CPU counters is one. u64_stats_sync.h may help. Please take a look at other drivers doing statistics. This is a solved problem, you just need to copy bits of code from somewhere else. Andrew From me at msfjarvis.dev Mon Jun 29 04:05:35 2026 From: me at msfjarvis.dev (Harsh Shandilya) Date: Mon, 29 Jun 2026 04:05:35 -0000 Subject: [PATCH wireguard-android] ui: add import config from clipboard In-Reply-To: <20260621074710.1106438-1-anton@sw4me.com> References: <20260621074710.1106438-1-anton@sw4me.com> Message-ID: <41772cd9-8632-454f-b2a5-dae297398109@msfjarvis.dev> On 21-06-2026 13:17, Anton Kovalenko wrote: > Adds a fourth action to the add-tunnel bottom sheet that reads plain > text from the system clipboard and routes it through the existing > text-import path (validate, name, create) used for QR codes. > > Signed-off-by: Anton Kovalenko > --- > The add-tunnel sheet currently offers file/archive, QR scan and > create-from-scratch. This adds a clipboard option for the common case > of getting a config as text over chat or email, routed through the > existing QR-text import path so validation, naming and storage are > unchanged. > > Built and tested on Samsung S24 Ultra, Android 16: a valid config > imports and names correctly; empty or non-config clipboard shows the > expected error snackbar. > > First patch I've sent to the list ? happy to adjust style or approach. > > Disclosure: this patch was written by Claude (Anthropic's AI), which I > reviewed, built and tested. > > .../android/fragment/AddTunnelsSheet.kt | 9 +++++ > .../android/fragment/TunnelListFragment.kt | 15 ++++++++ > .../wireguard/android/util/ClipboardUtils.kt | 13 +++++++ > ui/src/main/res/drawable/ic_action_paste.xml | 14 ++++++++ > .../res/layout/add_tunnels_bottom_sheet.xml | 34 ++++++++++++++++--- > ui/src/main/res/values/strings.xml | 2 ++ > 6 files changed, 82 insertions(+), 5 deletions(-) > create mode 100644 ui/src/main/res/drawable/ic_action_paste.xml > > diff --git a/ui/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt b/ui/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt > index 161709c3..d5f01b40 100644 > --- a/ui/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt > +++ b/ui/src/main/java/com/wireguard/android/fragment/AddTunnelsSheet.kt > @@ -64,6 +64,10 @@ class AddTunnelsSheet : BottomSheetDialogFragment() { > dismiss() > onRequestImportConfig() > } > + dialog.findViewById(R.id.create_from_clipboard)?.setOnClickListener { > + dismiss() > + onRequestImportFromClipboard() > + } > dialog.findViewById(R.id.create_from_qrcode)?.setOnClickListener { > dismiss() > onRequestScanQRCode() > @@ -89,6 +93,10 @@ class AddTunnelsSheet : BottomSheetDialogFragment() { > setFragmentResult(REQUEST_KEY_NEW_TUNNEL, Bundle().apply { putString(REQUEST_METHOD, REQUEST_IMPORT) }) > } > > + private fun onRequestImportFromClipboard() { > + setFragmentResult(REQUEST_KEY_NEW_TUNNEL, Bundle().apply { putString(REQUEST_METHOD, REQUEST_CLIPBOARD) }) > + } > + > private fun onRequestScanQRCode() { > setFragmentResult(REQUEST_KEY_NEW_TUNNEL, Bundle().apply { putString(REQUEST_METHOD, REQUEST_SCAN) }) > } > @@ -98,6 +106,7 @@ class AddTunnelsSheet : BottomSheetDialogFragment() { > const val REQUEST_METHOD = "request_method" > const val REQUEST_CREATE = "request_create" > const val REQUEST_IMPORT = "request_import" > + const val REQUEST_CLIPBOARD = "request_clipboard" > const val REQUEST_SCAN = "request_scan" > } > } > diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt > index 119b6afe..8bde6794 100644 > --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt > +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt > @@ -34,6 +34,7 @@ import com.wireguard.android.databinding.TunnelListFragmentBinding > import com.wireguard.android.databinding.TunnelListItemBinding > import com.wireguard.android.model.ObservableTunnel > import com.wireguard.android.updater.SnackbarUpdateShower > +import com.wireguard.android.util.ClipboardUtils > import com.wireguard.android.util.ErrorMessages > import com.wireguard.android.util.QrCodeFromFileScanner > import com.wireguard.android.util.TunnelImporter > @@ -83,6 +84,16 @@ class TunnelListFragment : BaseFragment() { > > private val snackbarUpdateShower = SnackbarUpdateShower(this) > > + private fun importFromClipboard() { > + val context = context ?: return > + val text = ClipboardUtils.readPlainText(context) > + if (text.isNullOrBlank()) { > + showSnackbar(getString(R.string.clipboard_no_text)) > + return > + } > + TunnelImporter.importTunnel(parentFragmentManager, text.toString()) { showSnackbar(it) } > + } > + > override fun onViewCreated(view: View, savedInstanceState: Bundle?) { > super.onViewCreated(view, savedInstanceState) > if (savedInstanceState != null) { > @@ -114,6 +125,10 @@ class TunnelListFragment : BaseFragment() { > tunnelFileImportResultLauncher.launch("*/*") > } > > + AddTunnelsSheet.REQUEST_CLIPBOARD -> { > + importFromClipboard() > + } > + > AddTunnelsSheet.REQUEST_SCAN -> { > qrImportResultLauncher.launch( > ScanOptions() > diff --git a/ui/src/main/java/com/wireguard/android/util/ClipboardUtils.kt b/ui/src/main/java/com/wireguard/android/util/ClipboardUtils.kt > index 8968979f..76eb2b9a 100644 > --- a/ui/src/main/java/com/wireguard/android/util/ClipboardUtils.kt > +++ b/ui/src/main/java/com/wireguard/android/util/ClipboardUtils.kt > @@ -6,6 +6,7 @@ package com.wireguard.android.util > > import android.content.ClipData > import android.content.ClipboardManager > +import android.content.Context > import android.os.Build > import android.view.View > import android.widget.TextView > @@ -18,6 +19,18 @@ import com.wireguard.android.R > * Standalone utilities for interacting with the system clipboard. > */ > object ClipboardUtils { > + /** > + * Returns the plain-text contents of the primary clip, or null if the > + * clipboard is unavailable, empty, or holds no coercible text. > + */ > + @JvmStatic > + fun readPlainText(context: Context): CharSequence? { > + val service = context.getSystemService() ?: return null > + val clip = service.primaryClip ?: return null > + if (clip.itemCount == 0) return null > + return clip.getItemAt(0).coerceToText(context) > + } > + > @JvmStatic > fun copyTextView(view: View) { > val data = when (view) { > diff --git a/ui/src/main/res/drawable/ic_action_paste.xml b/ui/src/main/res/drawable/ic_action_paste.xml > new file mode 100644 > index 00000000..1841b21e > --- /dev/null > +++ b/ui/src/main/res/drawable/ic_action_paste.xml > @@ -0,0 +1,14 @@ > + > + + android:width="24dp" > + android:height="24dp" > + android:tint="?attr/colorControlNormal" > + android:viewportWidth="24" > + android:viewportHeight="24"> > + + android:fillColor="#FFFFFFFF" > + android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0S9.6,0.84 9.18,2H5C3.9,2 3,2.9 3,4v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z" /> > + > diff --git a/ui/src/main/res/layout/add_tunnels_bottom_sheet.xml b/ui/src/main/res/layout/add_tunnels_bottom_sheet.xml > index 0ad1ef23..310a2d75 100644 > --- a/ui/src/main/res/layout/add_tunnels_bottom_sheet.xml > +++ b/ui/src/main/res/layout/add_tunnels_bottom_sheet.xml > @@ -18,22 +18,22 @@ > android:layout_marginLeft="@dimen/normal_margin" > android:layout_marginEnd="@dimen/normal_margin" > android:layout_marginRight="@dimen/normal_margin" > - android:nextFocusDown="@id/create_from_qrcode" > - android:nextFocusForward="@id/create_from_qrcode" > + android:nextFocusDown="@id/create_from_clipboard" > + android:nextFocusForward="@id/create_from_clipboard" > android:text="@string/create_from_file" > android:textAlignment="viewStart" > android:textColor="?attr/colorOnSurface" > app:icon="@drawable/ic_action_open" > app:iconPadding="@dimen/bottom_sheet_icon_padding" > app:iconTint="?attr/colorSecondary" > - app:layout_constraintBottom_toTopOf="@+id/create_from_qrcode" > + app:layout_constraintBottom_toTopOf="@+id/create_from_clipboard" > app:layout_constraintEnd_toEndOf="parent" > app:layout_constraintStart_toStartOf="parent" > app:layout_constraintTop_toBottomOf="parent" > app:rippleColor="?attr/colorSecondary" /> > > - android:id="@+id/create_from_qrcode" > + android:id="@+id/create_from_clipboard" > style="@style/Widget.Material3.Button.TextButton.Icon" > android:layout_width="match_parent" > android:layout_height="@dimen/bottom_sheet_item_height" > @@ -42,6 +42,30 @@ > android:layout_marginEnd="@dimen/normal_margin" > android:layout_marginRight="@dimen/normal_margin" > android:nextFocusUp="@id/create_from_file" > + android:nextFocusDown="@id/create_from_qrcode" > + android:nextFocusForward="@id/create_from_qrcode" > + android:text="@string/create_from_clipboard" > + android:textAlignment="viewStart" > + android:textColor="?attr/colorOnSurface" > + app:icon="@drawable/ic_action_paste" > + app:iconPadding="@dimen/bottom_sheet_icon_padding" > + app:iconTint="?attr/colorSecondary" > + app:layout_constraintBottom_toTopOf="@+id/create_from_qrcode" > + app:layout_constraintEnd_toEndOf="parent" > + app:layout_constraintStart_toStartOf="parent" > + app:layout_constraintTop_toBottomOf="@+id/create_from_file" > + app:rippleColor="?attr/colorSecondary" /> > + > + + android:id="@+id/create_from_qrcode" > + style="@style/Widget.Material3.Button.TextButton.Icon" > + android:layout_width="match_parent" > + android:layout_height="@dimen/bottom_sheet_item_height" > + android:layout_marginStart="@dimen/normal_margin" > + android:layout_marginLeft="@dimen/normal_margin" > + android:layout_marginEnd="@dimen/normal_margin" > + android:layout_marginRight="@dimen/normal_margin" > + android:nextFocusUp="@id/create_from_clipboard" > android:nextFocusDown="@id/create_empty" > android:nextFocusForward="@id/create_empty" > android:text="@string/create_from_qr_code" > @@ -53,7 +77,7 @@ > app:layout_constraintBottom_toBottomOf="@+id/create_empty" > app:layout_constraintEnd_toEndOf="parent" > app:layout_constraintStart_toStartOf="parent" > - app:layout_constraintTop_toBottomOf="@+id/create_from_file" > + app:layout_constraintTop_toBottomOf="@+id/create_from_clipboard" > app:rippleColor="?attr/colorSecondary" /> > > diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml > index 32e797e6..757c78c9 100644 > --- a/ui/src/main/res/values/strings.xml > +++ b/ui/src/main/res/values/strings.xml > @@ -86,6 +86,7 @@ > QR code not found in image > QR code checksum verification failed > Cancel > + No text on the clipboard > Cannot delete configuration file %s > Configuration for ?%s? already exists > Configuration file ?%s? already exists > @@ -97,6 +98,7 @@ > Cannot create local binary directory > Cannot create file in downloads directory > Create from scratch > + Import from clipboard > Import from file or archive > Scan from QR code > Cannot create output directory The code looks fine at a cursory glance but I'm not sure about the WireGuard project's current policy around LLM-generated code. I'll defer to Jason to make a call on that before I pull the patch down for testing and a proper review. -- Harsh Shandilya From andrew at lunn.ch Mon Jun 29 14:52:06 2026 From: andrew at lunn.ch (Andrew Lunn) Date: Mon, 29 Jun 2026 14:52:06 -0000 Subject: [PATCH v2] Wireguard: Fix data-race in rx/tx counter In-Reply-To: <20260629125913.2354450-1-rafael@rcpassos.me> References: <20260629125913.2354450-1-rafael@rcpassos.me> Message-ID: > When looking around drivers/net, I found a few u64_stats uses without > per-cpu. I will do some digging to understand the reasoning behind it, > compared to the original "bare u64 counters" in wireguard. /* * Protect against 64-bit values tearing on 32-bit architectures. This is * typically used for statistics read/update in different subsystems. The assumption is, updating a 64 bit value on a 64 bit machine is a single operation. You either see the old value, or the new value. For statistics counters, we general don't care about valid but slightly outdated values. On a 32 bit machine, updating a 64 bit value requires up to two reads and two writes. So if a reader and a writer operate at the same time, the reader might see one of the invalid intermediary states, also know as tearing. We do care about a totally wrong value. u64_stats_sync.h gives you a mechanism which is totally free on 64bit systems, but gives the needed protection on 32 bit systems to avoid seeing tears. You only need mutual exclusion on the writer side, from other writers. If you have per-cpu counters, you naturally get mutual exclusion, the CPU cannot be a writer twice to its own per CPU counters. However, without per-CPU counters, you need some form of locking. I don't know the locking used in wireguard, it might already be in place? If so, this would be a cheap solution. Andrew From rafael at rcpassos.me Mon Jun 22 19:34:55 2026 From: rafael at rcpassos.me (Rafael Passos) Date: Mon, 22 Jun 2026 19:34:55 -0000 Subject: [syzbot] [wireguard?] KCSAN: data-race in wg_socket_send_skb_to_peer / wg_socket_send_skb_to_peer (9) In-Reply-To: <6a1d983b.b111c304.35cd64.0028.GAE@google.com> References: <6a1d983b.b111c304.35cd64.0028.GAE@google.com> Message-ID: Hi, I started investigating this KCSAN warning by syzbot, and would like to ask a few questions. On Mon Jun 1, 2026 at 11:33 AM -03, syzbot wrote: > ================================================================== > BUG: KCSAN: data-race in wg_socket_send_skb_to_peer / wg_socket_send_skb_to_peer > > read-write to 0xffff88811af99028 of 8 bytes by task 310 on cpu 1: > wg_socket_send_skb_to_peer+0xe8/0x130 drivers/net/wireguard/socket.c:182 > wg_socket_send_buffer_to_peer+0xf1/0x120 drivers/net/wireguard/socket.c:199 > wg_packet_send_handshake_initiation drivers/net/wireguard/send.c:40 [inline] > wg_packet_handshake_send_worker+0x10d/0x160 drivers/net/wireguard/send.c:51 > process_one_work kernel/workqueue.c:3314 [inline] > process_scheduled_works+0x4f0/0x9c0 kernel/workqueue.c:3397 > worker_thread+0x58a/0x780 kernel/workqueue.c:3478 > kthread+0x22a/0x280 kernel/kthread.c:436 > ret_from_fork+0x146/0x330 arch/x86/kernel/process.c:158 > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > > read-write to 0xffff88811af99028 of 8 bytes by task 15360 on cpu 0: > wg_socket_send_skb_to_peer+0xe8/0x130 drivers/net/wireguard/socket.c:182 > wg_packet_create_data_done drivers/net/wireguard/send.c:251 [inline] > wg_packet_tx_worker+0x12d/0x330 drivers/net/wireguard/send.c:276 > process_one_work kernel/workqueue.c:3314 [inline] > process_scheduled_works+0x4f0/0x9c0 kernel/workqueue.c:3397 > worker_thread+0x58a/0x780 kernel/workqueue.c:3478 > kthread+0x22a/0x280 kernel/kthread.c:436 > ret_from_fork+0x146/0x330 arch/x86/kernel/process.c:158 > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > > value changed: 0x0000000000000a2c -> 0x0000000000000ac0 > > Reported by Kernel Concurrency Sanitizer on: > CPU: 0 UID: 0 PID: 15360 Comm: kworker/0:2 Tainted: G W syzkaller #0 PREEMPT(lazy) > Tainted: [W]=WARN > Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026 > Workqueue: wg-crypt-wg2 wg_packet_tx_worker I tracked the change to this counter increment in `wg_socket_send_skb_to_peer` +++ b/drivers/net/wireguard/socket.c @@ -179,7 +179,8 @@ int wg_socket_send_skb_to_peer(struct wg_peer *peer, struct sk_buff *skb, u8 ds) else dev_kfree_skb(skb); if (likely(!ret)) -> peer->tx_bytes += skb_len; <- protected by a read_lock_bh only read_unlock_bh(&peer->endpoint_lock); It is protected by the read-part of a rwlock. However, if the stack trace makes sense, this `wg_socket_send_skb_to_peer` is being called after a handshake (wg_packet_send_handshake_initiation) and a send worker call (wg_packet_tx_worker). Does this make sense ? Are such calls possible to really hapen outside of fuzzing ? Out of curiosity, I changed `tx_bytes` and `rx_bytes` from u64 to atomic64_t in peer.h, and also the r/w ops in netlink.c, receive.c and socket.c files. I ran the wireguard kselftest suite with and without this patch, and it worked fine. Iperf results seem sine (on amd64). I'm not sure if this should be the solution, or if this is even a real issue in the first place. Any comments ? Eager to learn. Thanks, Rafael Passos From rafael at rcpassos.me Sun Jun 28 20:38:49 2026 From: rafael at rcpassos.me (Rafael Passos) Date: Sun, 28 Jun 2026 20:38:49 -0000 Subject: [PATCH] Wireguard: Fix data-race in rx/tx counter In-Reply-To: References: Message-ID: <20260628203823.144789-1-rafael@rcpassos.me> fixes data-race in {rx/tx}_bytes counter for wireguard connection. these values were incremented inside a read_lock_bh block, but write protections were missing. making them atomic was the simplest way out. This was found by syzbot with kcsan. Reported-by: syzbot+9ca7674fa7521a3f1bc2 at syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=9ca7674fa7521a3f1bc2 Signed-off-by: Rafael Passos --- Hi, I am posting this patch to better ilustrate the discussion. If this is a non-issue, its fine. As I mentioned in the previous email, this issue was reported by syzbot, but I was not able to reproduce it. I am also aware atomic calls may introduce extra cost on older arm cpus. I would like to hear from the community: would this an adequate solution ? Thanks, Rafael Passos drivers/net/wireguard/netlink.c | 4 ++-- drivers/net/wireguard/peer.h | 2 +- drivers/net/wireguard/receive.c | 2 +- drivers/net/wireguard/socket.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireguard/netlink.c b/drivers/net/wireguard/netlink.c index 1da7e98d0d509..ec66f79e46377 100644 --- a/drivers/net/wireguard/netlink.c +++ b/drivers/net/wireguard/netlink.c @@ -109,9 +109,9 @@ get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx) sizeof(last_handshake), &last_handshake) || nla_put_u16(skb, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval) || - nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, peer->tx_bytes, + nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, atomic64_read(&peer->tx_bytes), WGPEER_A_UNSPEC) || - nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, peer->rx_bytes, + nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, atomic64_read(&peer->rx_bytes), WGPEER_A_UNSPEC) || nla_put_u32(skb, WGPEER_A_PROTOCOL_VERSION, 1)) goto err; diff --git a/drivers/net/wireguard/peer.h b/drivers/net/wireguard/peer.h index 718fb42bdac7e..01c4b80086759 100644 --- a/drivers/net/wireguard/peer.h +++ b/drivers/net/wireguard/peer.h @@ -49,7 +49,7 @@ struct wg_peer { struct work_struct transmit_handshake_work, clear_peer_work, transmit_packet_work; struct cookie latest_cookie; struct hlist_node pubkey_hash; - u64 rx_bytes, tx_bytes; + atomic64_t rx_bytes, tx_bytes; struct timer_list timer_retransmit_handshake, timer_send_keepalive; struct timer_list timer_new_handshake, timer_zero_key_material; struct timer_list timer_persistent_keepalive; diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c index eb8851113654f..500d86576c692 100644 --- a/drivers/net/wireguard/receive.c +++ b/drivers/net/wireguard/receive.c @@ -20,7 +20,7 @@ static void update_rx_stats(struct wg_peer *peer, size_t len) { dev_sw_netstats_rx_add(peer->device->dev, len); - peer->rx_bytes += len; + atomic64_add(len, &peer->rx_bytes); } #define SKB_TYPE_LE32(skb) (((struct message_header *)(skb)->data)->type) diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index 0028ef17dc716..9e8a49b9078f2 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -179,7 +179,7 @@ int wg_socket_send_skb_to_peer(struct wg_peer *peer, struct sk_buff *skb, u8 ds) else dev_kfree_skb(skb); if (likely(!ret)) - peer->tx_bytes += skb_len; + atomic64_add(skb_len, &peer->tx_bytes); read_unlock_bh(&peer->endpoint_lock); return ret; -- 2.53.0 From rafael at rcpassos.me Mon Jun 29 03:05:11 2026 From: rafael at rcpassos.me (Rafael Passos) Date: Mon, 29 Jun 2026 03:05:11 -0000 Subject: [PATCH] Wireguard: Fix data-race in rx/tx counter In-Reply-To: References: <20260628203823.144789-1-rafael@rcpassos.me> Message-ID: On Sun Jun 28, 2026 at 11:34 PM -03, Theodore Tso wrote: > One of the reasons why kcsan and syzbot can be quite noisy is that a > human being needs to *think* and consider whether or not this is > actually important. (One of the reasons why I'm not all that worried > about our new AI overlords taking over the world. :-) Consider what > is the worst that might happen if the tx/rx_bytes counter might not be > completely accurate? Is it worth the performance penalty of using > atomics (or the memory overhead of per-CPU counters)? Yeah, I guess not. Still, it was very interesting learning all this. I only knew per-cpu counters by name, and Andrew's response led me to actually understand it. I would like to thank you both! And if I may, I would like to send my v2 patch (as a response in this thread) just because it was very fun making and testing it. And I would love feedback on it, if anything looks wrong. This was an amazing learning opportunity for me. Thanks, Rafael Passos From rafael at rcpassos.me Mon Jun 29 12:59:35 2026 From: rafael at rcpassos.me (Rafael Passos) Date: Mon, 29 Jun 2026 12:59:35 -0000 Subject: [PATCH v2] Wireguard: Fix data-race in rx/tx counter In-Reply-To: References: Message-ID: <20260629125913.2354450-1-rafael@rcpassos.me> Fix data-race in {rx/tx}_bytes counter for wireguard connection. These values were incremented inside a read_lock_bh block, without exclusive write protection. Using per-cpu counters guarantees consistency, and move overhead only to the read part: the least frequent operation. Signed-off-by: Rafael Passos --- As we discussed in the thread, keeping this counters accurate might not be worth this extra memory cost of per-cpu counter. It was great reading and learning about it. I think this would have been a good patch :) When looking around drivers/net, I found a few u64_stats uses without per-cpu. I will do some digging to understand the reasoning behind it, compared to the original "bare u64 counters" in wireguard. Thanks, Rafael Passos drivers/net/wireguard/netlink.c | 22 ++++++++++++++++++++-- drivers/net/wireguard/peer.c | 7 ++++++- drivers/net/wireguard/peer.h | 7 ++++++- drivers/net/wireguard/receive.c | 9 ++++++++- drivers/net/wireguard/socket.c | 9 +++++++-- 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireguard/netlink.c b/drivers/net/wireguard/netlink.c index 1da7e98d0d50..f5978a8c2ca3 100644 --- a/drivers/net/wireguard/netlink.c +++ b/drivers/net/wireguard/netlink.c @@ -105,13 +105,31 @@ get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx) if (fail) goto err; + // read per-cpu counters for peer rx/tx_bytes + u64 total_rx_bytes = 0, total_tx_bytes = 0; + u64 rx_bytes = 0, tx_bytes = 0; + struct wg_peer_stats *pcpu_ptr; + unsigned int cpu, start; + + for_each_possible_cpu(cpu) { + pcpu_ptr = per_cpu_ptr(peer->pcpu_stats, cpu); + do { + start = u64_stats_fetch_begin(&pcpu_ptr->syncp); + rx_bytes = u64_stats_read(&pcpu_ptr->rx_bytes); + tx_bytes = u64_stats_read(&pcpu_ptr->tx_bytes); + } while (u64_stats_fetch_retry(&pcpu_ptr->syncp, start)); + + total_rx_bytes += rx_bytes; + total_tx_bytes += tx_bytes; + } + if (nla_put(skb, WGPEER_A_LAST_HANDSHAKE_TIME, sizeof(last_handshake), &last_handshake) || nla_put_u16(skb, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval) || - nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, peer->tx_bytes, + nla_put_u64_64bit(skb, WGPEER_A_TX_BYTES, total_tx_bytes, WGPEER_A_UNSPEC) || - nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, peer->rx_bytes, + nla_put_u64_64bit(skb, WGPEER_A_RX_BYTES, total_rx_bytes, WGPEER_A_UNSPEC) || nla_put_u32(skb, WGPEER_A_PROTOCOL_VERSION, 1)) goto err; diff --git a/drivers/net/wireguard/peer.c b/drivers/net/wireguard/peer.c index 1cb502a932e0..a37aa31f132a 100644 --- a/drivers/net/wireguard/peer.c +++ b/drivers/net/wireguard/peer.c @@ -36,6 +36,10 @@ struct wg_peer *wg_peer_create(struct wg_device *wg, if (unlikely(dst_cache_init(&peer->endpoint_cache, GFP_KERNEL))) goto err; + peer->pcpu_stats = alloc_percpu(struct wg_peer_stats); + if (unlikely(!peer->pcpu_stats)) + goto err; + peer->device = wg; wg_noise_handshake_init(&peer->handshake, &wg->static_identity, public_key, preshared_key, peer); @@ -189,7 +193,8 @@ static void rcu_release(struct rcu_head *rcu) dst_cache_destroy(&peer->endpoint_cache); WARN_ON(wg_prev_queue_peek(&peer->tx_queue) || wg_prev_queue_peek(&peer->rx_queue)); - + + free_percpu(peer->pcpu_stats); /* The final zeroing takes care of clearing any remaining handshake key * material and other potentially sensitive information. */ diff --git a/drivers/net/wireguard/peer.h b/drivers/net/wireguard/peer.h index 718fb42bdac7..e01781724aa1 100644 --- a/drivers/net/wireguard/peer.h +++ b/drivers/net/wireguard/peer.h @@ -34,6 +34,11 @@ struct endpoint { }; }; +struct wg_peer_stats { + u64_stats_t rx_bytes, tx_bytes; + struct u64_stats_sync syncp; +}; + struct wg_peer { struct wg_device *device; struct prev_queue tx_queue, rx_queue; @@ -49,7 +54,7 @@ struct wg_peer { struct work_struct transmit_handshake_work, clear_peer_work, transmit_packet_work; struct cookie latest_cookie; struct hlist_node pubkey_hash; - u64 rx_bytes, tx_bytes; + struct wg_peer_stats __percpu *pcpu_stats; struct timer_list timer_retransmit_handshake, timer_send_keepalive; struct timer_list timer_new_handshake, timer_zero_key_material; struct timer_list timer_persistent_keepalive; diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c index eb8851113654..d799370122b5 100644 --- a/drivers/net/wireguard/receive.c +++ b/drivers/net/wireguard/receive.c @@ -19,8 +19,15 @@ /* Must be called with bh disabled. */ static void update_rx_stats(struct wg_peer *peer, size_t len) { + struct wg_peer_stats *pcpu_ptr; + dev_sw_netstats_rx_add(peer->device->dev, len); - peer->rx_bytes += len; + + pcpu_ptr = this_cpu_ptr(peer->pcpu_stats); + + u64_stats_update_begin(&pcpu_ptr->syncp); + u64_stats_add(&pcpu_ptr->rx_bytes, len); + u64_stats_update_end(&pcpu_ptr->syncp); } #define SKB_TYPE_LE32(skb) (((struct message_header *)(skb)->data)->type) diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index 0028ef17dc71..685ae6a0fb2c 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -166,6 +166,7 @@ static int send6(struct wg_device *wg, struct sk_buff *skb, int wg_socket_send_skb_to_peer(struct wg_peer *peer, struct sk_buff *skb, u8 ds) { + struct wg_peer_stats *pcpu_ptr; size_t skb_len = skb->len; int ret = -EAFNOSUPPORT; @@ -178,8 +179,12 @@ int wg_socket_send_skb_to_peer(struct wg_peer *peer, struct sk_buff *skb, u8 ds) &peer->endpoint_cache); else dev_kfree_skb(skb); - if (likely(!ret)) - peer->tx_bytes += skb_len; + if (likely(!ret)) { + pcpu_ptr = this_cpu_ptr(peer->pcpu_stats); + u64_stats_update_begin(&pcpu_ptr->syncp); + u64_stats_add(&pcpu_ptr->tx_bytes, skb_len); + u64_stats_update_end(&pcpu_ptr->syncp); + } read_unlock_bh(&peer->endpoint_lock); return ret; -- 2.53.0