mirror of
https://github.com/amnezia-vpn/amneziawg-go.git
synced 2026-10-12 22:24:18 +03:00
Compare commits
85
Commits
v0.1.3
...
feature/awg4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4659f37e23 | ||
|
|
f6542209f4 | ||
|
|
1abd24b5b9 | ||
|
|
3f19f1c657 | ||
|
|
c207898480 | ||
|
|
fe75b639fa | ||
|
|
169ed49a46 | ||
|
|
eeb8aae13e | ||
|
|
99f2e6d66f | ||
|
|
d5359f52f0 | ||
|
|
6768090667 | ||
|
|
2cad62c40b | ||
|
|
8051f17147 | ||
|
|
ace3e11ef2 | ||
|
|
8a2b2bf4f4 | ||
|
|
75d6c67a67 | ||
|
|
ac8a885a03 | ||
|
|
6a7c878409 | ||
|
|
704d57c27a | ||
|
|
c0b6e6a200 | ||
|
|
c803ce1e5b | ||
|
|
deedce495a | ||
|
|
27e661d68e | ||
|
|
71be0eb3a6 | ||
|
|
e3f1273f8a | ||
|
|
c97b5b7615 | ||
|
|
668ddfd455 | ||
|
|
b8da08c106 | ||
|
|
2e3f7d122c | ||
|
|
2e7780471a | ||
|
|
87d8c00f86 | ||
|
|
c00bda9200 | ||
|
|
d2b0fc9789 | ||
|
|
77d39ff3b9 | ||
|
|
e433d13df6 | ||
|
|
3ddf952973 | ||
|
|
3f0a3bcfa0 | ||
|
|
4dddf62e57 | ||
|
|
827ec6e14b | ||
|
|
92e28a0d14 | ||
|
|
52fed4d362 | ||
|
|
9c6b3ff332 | ||
|
|
7de7a9a754 | ||
|
|
0c347529b8 | ||
|
|
6705978fc8 | ||
|
|
032e33f577 | ||
|
|
59101fd202 | ||
|
|
8bcfbac230 | ||
|
|
f0dfb5eacc | ||
|
|
9195025d8f | ||
|
|
cbd414dfec | ||
|
|
7155d20913 | ||
|
|
bfeb3954f6 | ||
|
|
e3c9ec8012 | ||
|
|
ce9d3866a3 | ||
|
|
e5f355e843 | ||
|
|
c05b2ee2a3 | ||
|
|
180c9284f3 | ||
|
|
015e11875d | ||
|
|
12269c2761 | ||
|
|
542e565baa | ||
|
|
7c20311b3d | ||
|
|
4ffa9c2032 | ||
|
|
d0bc03c707 | ||
|
|
1cf89f5339 | ||
|
|
b43118018e | ||
|
|
7af55a3e6f | ||
|
|
c493b95f66 | ||
|
|
2e0774f246 | ||
|
|
b3df23dcd4 | ||
|
|
f502ec3fad | ||
|
|
5d37bd24e1 | ||
|
|
24ea13351e | ||
|
|
177caa7e44 | ||
|
|
b81ca925db | ||
|
|
42ec952ead | ||
|
|
ec8f6f82c2 | ||
|
|
1ec454f253 | ||
|
|
8a015f7c76 | ||
|
|
895d6c23cd | ||
|
|
4201e08f1d | ||
|
|
6a84778f2c | ||
|
|
b34974c476 | ||
|
|
f30419e0d1 | ||
|
|
8f1a6a10b2 |
No files matched your search
@@ -0,0 +1,41 @@
|
||||
name: build-if-tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
env:
|
||||
APP: amneziawg-go
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: build
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref_name }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Setup metadata
|
||||
uses: docker/metadata-action@v5
|
||||
id: metadata
|
||||
with:
|
||||
images: amneziavpn/${{ env.APP }}
|
||||
tags: type=semver,pattern={{version}}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
+1
-1
@@ -1 +1 @@
|
||||
wireguard-go
|
||||
amneziawg-go
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
FROM golang:1.24.4 as awg
|
||||
COPY . /awg
|
||||
WORKDIR /awg
|
||||
RUN go mod download && \
|
||||
go mod verify && \
|
||||
go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin
|
||||
|
||||
FROM alpine:3.19
|
||||
ARG AWGTOOLS_RELEASE="1.0.20250901"
|
||||
|
||||
RUN apk --no-cache add iproute2 iptables bash && \
|
||||
cd /usr/bin/ && \
|
||||
wget https://github.com/amnezia-vpn/amneziawg-tools/releases/download/v${AWGTOOLS_RELEASE}/alpine-3.19-amneziawg-tools.zip && \
|
||||
unzip -j alpine-3.19-amneziawg-tools.zip && \
|
||||
chmod +x /usr/bin/awg /usr/bin/awg-quick && \
|
||||
ln -s /usr/bin/awg /usr/bin/wg && \
|
||||
ln -s /usr/bin/awg-quick /usr/bin/wg-quick
|
||||
COPY --from=awg /usr/bin/amneziawg-go /usr/bin/amneziawg-go
|
||||
@@ -9,23 +9,23 @@ MAKEFLAGS += --no-print-directory
|
||||
|
||||
generate-version-and-build:
|
||||
@export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \
|
||||
tag="$$(git describe --dirty 2>/dev/null)" && \
|
||||
tag="$$(git describe --tags --dirty 2>/dev/null)" && \
|
||||
ver="$$(printf 'package main\n\nconst Version = "%s"\n' "$$tag")" && \
|
||||
[ "$$(cat version.go 2>/dev/null)" != "$$ver" ] && \
|
||||
echo "$$ver" > version.go && \
|
||||
git update-index --assume-unchanged version.go || true
|
||||
@$(MAKE) wireguard-go
|
||||
@$(MAKE) amneziawg-go
|
||||
|
||||
wireguard-go: $(wildcard *.go) $(wildcard */*.go)
|
||||
amneziawg-go: $(wildcard *.go) $(wildcard */*.go)
|
||||
go build -v -o "$@"
|
||||
|
||||
install: wireguard-go
|
||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/wireguard-go"
|
||||
install: amneziawg-go
|
||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/amneziawg-go"
|
||||
|
||||
test:
|
||||
go test ./...
|
||||
|
||||
clean:
|
||||
rm -f wireguard-go
|
||||
rm -f amneziawg-go
|
||||
|
||||
.PHONY: all clean test install generate-version-and-build
|
||||
@@ -1,24 +1,27 @@
|
||||
# Go Implementation of [WireGuard](https://www.wireguard.com/)
|
||||
# Go Implementation of AmneziaWG
|
||||
|
||||
This is an implementation of WireGuard in Go.
|
||||
AmneziaWG is a contemporary version of the WireGuard protocol. It's a fork of WireGuard-Go and offers protection against detection by Deep Packet Inspection (DPI) systems. At the same time, it retains the simplified architecture and high performance of the original.
|
||||
|
||||
The precursor, WireGuard, is known for its efficiency but had issues with detection due to its distinctive packet signatures.
|
||||
AmneziaWG addresses this problem by employing advanced obfuscation methods, allowing its traffic to blend seamlessly with regular internet traffic.
|
||||
As a result, AmneziaWG maintains high performance while adding an extra layer of stealth, making it a superb choice for those seeking a fast and discreet VPN connection.
|
||||
|
||||
## Usage
|
||||
|
||||
Most Linux kernel WireGuard users are used to adding an interface with `ip link add wg0 type wireguard`. With wireguard-go, instead simply run:
|
||||
Simply run:
|
||||
|
||||
```
|
||||
$ wireguard-go wg0
|
||||
$ amneziawg-go wg0
|
||||
```
|
||||
|
||||
This will create an interface and fork into the background. To remove the interface, use the usual `ip link del wg0`, or if your system does not support removing interfaces directly, you may instead remove the control socket via `rm -f /var/run/wireguard/wg0.sock`, which will result in wireguard-go shutting down.
|
||||
This will create an interface and fork into the background. To remove the interface, use the usual `ip link del wg0`, or if your system does not support removing interfaces directly, you may instead remove the control socket via `rm -f /var/run/amneziawg/wg0.sock`, which will result in amneziawg-go shutting down.
|
||||
|
||||
To run wireguard-go without forking to the background, pass `-f` or `--foreground`:
|
||||
To run amneziawg-go without forking to the background, pass `-f` or `--foreground`:
|
||||
|
||||
```
|
||||
$ wireguard-go -f wg0
|
||||
$ amneziawg-go -f wg0
|
||||
```
|
||||
|
||||
When an interface is running, you may use [`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) to configure it, as well as the usual `ip(8)` and `ifconfig(8)` commands.
|
||||
When an interface is running, you may use [`amneziawg-tools `](https://github.com/amnezia-vpn/amneziawg-tools) to configure it, as well as the usual `ip(8)` and `ifconfig(8)` commands.
|
||||
|
||||
To run with more logging you may set the environment variable `LOG_LEVEL=debug`.
|
||||
|
||||
@@ -26,52 +29,25 @@ To run with more logging you may set the environment variable `LOG_LEVEL=debug`.
|
||||
|
||||
### Linux
|
||||
|
||||
This will run on Linux; however you should instead use the kernel module, which is faster and better integrated into the OS. See the [installation page](https://www.wireguard.com/install/) for instructions.
|
||||
This will run on Linux; you should run amnezia-wg instead of using default linux kernel module.
|
||||
|
||||
### macOS
|
||||
|
||||
This runs on macOS using the utun driver. It does not yet support sticky sockets, and won't support fwmarks because of Darwin limitations. Since the utun driver cannot have arbitrary interface names, you must either use `utun[0-9]+` for an explicit interface name or `utun` to have the kernel select one for you. If you choose `utun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
|
||||
This runs on MacOS, you should use it from [amneziawg-apple](https://github.com/amnezia-vpn/amneziawg-apple)
|
||||
|
||||
### Windows
|
||||
|
||||
This runs on Windows, but you should instead use it from the more [fully featured Windows app](https://git.zx2c4.com/wireguard-windows/about/), which uses this as a module.
|
||||
This runs on Windows, you should use it from [amneziawg-windows](https://github.com/amnezia-vpn/amneziawg-windows), which uses this as a module.
|
||||
|
||||
### FreeBSD
|
||||
|
||||
This will run on FreeBSD. It does not yet support sticky sockets. Fwmark is mapped to `SO_USER_COOKIE`.
|
||||
|
||||
### OpenBSD
|
||||
|
||||
This will run on OpenBSD. It does not yet support sticky sockets. Fwmark is mapped to `SO_RTABLE`. Since the tun driver cannot have arbitrary interface names, you must either use `tun[0-9]+` for an explicit interface name or `tun` to have the program select one for you. If you choose `tun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
|
||||
|
||||
## Building
|
||||
|
||||
This requires an installation of the latest version of [Go](https://go.dev/).
|
||||
|
||||
```
|
||||
$ git clone https://git.zx2c4.com/wireguard-go
|
||||
$ cd wireguard-go
|
||||
$ git clone https://github.com/amnezia-vpn/amneziawg-go
|
||||
$ cd amneziawg-go
|
||||
$ make
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
+272
-136
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -8,6 +8,7 @@ package conn
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
@@ -29,16 +30,19 @@ var (
|
||||
// methods for sending and receiving multiple datagrams per-syscall. See the
|
||||
// proposal in https://github.com/golang/go/issues/45886#issuecomment-1218301564.
|
||||
type StdNetBind struct {
|
||||
mu sync.Mutex // protects all fields except as specified
|
||||
ipv4 *net.UDPConn
|
||||
ipv6 *net.UDPConn
|
||||
ipv4PC *ipv4.PacketConn // will be nil on non-Linux
|
||||
ipv6PC *ipv6.PacketConn // will be nil on non-Linux
|
||||
mu sync.Mutex // protects all fields except as specified
|
||||
ipv4 *net.UDPConn
|
||||
ipv6 *net.UDPConn
|
||||
ipv4PC *ipv4.PacketConn // will be nil on non-Linux
|
||||
ipv6PC *ipv6.PacketConn // will be nil on non-Linux
|
||||
ipv4TxOffload bool
|
||||
ipv4RxOffload bool
|
||||
ipv6TxOffload bool
|
||||
ipv6RxOffload bool
|
||||
|
||||
// these three fields are not guarded by mu
|
||||
udpAddrPool sync.Pool
|
||||
ipv4MsgsPool sync.Pool
|
||||
ipv6MsgsPool sync.Pool
|
||||
// these two fields are not guarded by mu
|
||||
udpAddrPool sync.Pool
|
||||
msgsPool sync.Pool
|
||||
|
||||
blackhole4 bool
|
||||
blackhole6 bool
|
||||
@@ -54,23 +58,14 @@ func NewStdNetBind() Bind {
|
||||
},
|
||||
},
|
||||
|
||||
ipv4MsgsPool: sync.Pool{
|
||||
New: func() any {
|
||||
msgs := make([]ipv4.Message, IdealBatchSize)
|
||||
for i := range msgs {
|
||||
msgs[i].Buffers = make(net.Buffers, 1)
|
||||
msgs[i].OOB = make([]byte, srcControlSize)
|
||||
}
|
||||
return &msgs
|
||||
},
|
||||
},
|
||||
|
||||
ipv6MsgsPool: sync.Pool{
|
||||
msgsPool: sync.Pool{
|
||||
New: func() any {
|
||||
// ipv6.Message and ipv4.Message are interchangeable as they are
|
||||
// both aliases for x/net/internal/socket.Message.
|
||||
msgs := make([]ipv6.Message, IdealBatchSize)
|
||||
for i := range msgs {
|
||||
msgs[i].Buffers = make(net.Buffers, 1)
|
||||
msgs[i].OOB = make([]byte, srcControlSize)
|
||||
msgs[i].OOB = make([]byte, 0, stickyControlSize+gsoControlSize)
|
||||
}
|
||||
return &msgs
|
||||
},
|
||||
@@ -113,7 +108,7 @@ func (e *StdNetEndpoint) DstIP() netip.Addr {
|
||||
return e.AddrPort.Addr()
|
||||
}
|
||||
|
||||
// See sticky_default,linux, etc for implementations of SrcIP and SrcIfidx.
|
||||
// See control_default,linux, etc for implementations of SrcIP and SrcIfidx.
|
||||
|
||||
func (e *StdNetEndpoint) DstToBytes() []byte {
|
||||
b, _ := e.AddrPort.MarshalBinary()
|
||||
@@ -179,19 +174,21 @@ again:
|
||||
}
|
||||
var fns []ReceiveFunc
|
||||
if v4conn != nil {
|
||||
if runtime.GOOS == "linux" {
|
||||
s.ipv4TxOffload, s.ipv4RxOffload = supportsUDPOffload(v4conn)
|
||||
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||
v4pc = ipv4.NewPacketConn(v4conn)
|
||||
s.ipv4PC = v4pc
|
||||
}
|
||||
fns = append(fns, s.makeReceiveIPv4(v4pc, v4conn))
|
||||
fns = append(fns, s.makeReceiveIPv4(v4pc, v4conn, s.ipv4RxOffload))
|
||||
s.ipv4 = v4conn
|
||||
}
|
||||
if v6conn != nil {
|
||||
if runtime.GOOS == "linux" {
|
||||
s.ipv6TxOffload, s.ipv6RxOffload = supportsUDPOffload(v6conn)
|
||||
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||
v6pc = ipv6.NewPacketConn(v6conn)
|
||||
s.ipv6PC = v6pc
|
||||
}
|
||||
fns = append(fns, s.makeReceiveIPv6(v6pc, v6conn))
|
||||
fns = append(fns, s.makeReceiveIPv6(v6pc, v6conn, s.ipv6RxOffload))
|
||||
s.ipv6 = v6conn
|
||||
}
|
||||
if len(fns) == 0 {
|
||||
@@ -201,76 +198,101 @@ again:
|
||||
return fns, uint16(port), nil
|
||||
}
|
||||
|
||||
func (s *StdNetBind) makeReceiveIPv4(pc *ipv4.PacketConn, conn *net.UDPConn) ReceiveFunc {
|
||||
return func(bufs [][]byte, sizes []int, eps []Endpoint) (n int, err error) {
|
||||
msgs := s.ipv4MsgsPool.Get().(*[]ipv4.Message)
|
||||
defer s.ipv4MsgsPool.Put(msgs)
|
||||
for i := range bufs {
|
||||
(*msgs)[i].Buffers[0] = bufs[i]
|
||||
}
|
||||
var numMsgs int
|
||||
if runtime.GOOS == "linux" {
|
||||
numMsgs, err = pc.ReadBatch(*msgs, 0)
|
||||
func (s *StdNetBind) putMessages(msgs *[]ipv6.Message) {
|
||||
for i := range *msgs {
|
||||
(*msgs)[i].OOB = (*msgs)[i].OOB[:0]
|
||||
(*msgs)[i] = ipv6.Message{Buffers: (*msgs)[i].Buffers, OOB: (*msgs)[i].OOB}
|
||||
}
|
||||
s.msgsPool.Put(msgs)
|
||||
}
|
||||
|
||||
func (s *StdNetBind) getMessages() *[]ipv6.Message {
|
||||
return s.msgsPool.Get().(*[]ipv6.Message)
|
||||
}
|
||||
|
||||
var (
|
||||
// If compilation fails here these are no longer the same underlying type.
|
||||
_ ipv6.Message = ipv4.Message{}
|
||||
)
|
||||
|
||||
type batchReader interface {
|
||||
ReadBatch([]ipv6.Message, int) (int, error)
|
||||
}
|
||||
|
||||
type batchWriter interface {
|
||||
WriteBatch([]ipv6.Message, int) (int, error)
|
||||
}
|
||||
|
||||
func (s *StdNetBind) receiveIP(
|
||||
br batchReader,
|
||||
conn *net.UDPConn,
|
||||
rxOffload bool,
|
||||
bufs [][]byte,
|
||||
sizes []int,
|
||||
eps []Endpoint,
|
||||
) (n int, err error) {
|
||||
msgs := s.getMessages()
|
||||
for i := range bufs {
|
||||
(*msgs)[i].Buffers[0] = bufs[i]
|
||||
(*msgs)[i].OOB = (*msgs)[i].OOB[:cap((*msgs)[i].OOB)]
|
||||
}
|
||||
defer s.putMessages(msgs)
|
||||
var numMsgs int
|
||||
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||
if rxOffload {
|
||||
readAt := len(*msgs) - (IdealBatchSize / udpSegmentMaxDatagrams)
|
||||
numMsgs, err = br.ReadBatch((*msgs)[readAt:], 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
numMsgs, err = splitCoalescedMessages(*msgs, readAt, getGSOSize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
msg := &(*msgs)[0]
|
||||
msg.N, msg.NN, _, msg.Addr, err = conn.ReadMsgUDP(msg.Buffers[0], msg.OOB)
|
||||
numMsgs, err = br.ReadBatch(*msgs, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
numMsgs = 1
|
||||
}
|
||||
for i := 0; i < numMsgs; i++ {
|
||||
msg := &(*msgs)[i]
|
||||
sizes[i] = msg.N
|
||||
addrPort := msg.Addr.(*net.UDPAddr).AddrPort()
|
||||
ep := &StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation
|
||||
getSrcFromControl(msg.OOB[:msg.NN], ep)
|
||||
eps[i] = ep
|
||||
} else {
|
||||
msg := &(*msgs)[0]
|
||||
msg.N, msg.NN, _, msg.Addr, err = conn.ReadMsgUDP(msg.Buffers[0], msg.OOB)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return numMsgs, nil
|
||||
numMsgs = 1
|
||||
}
|
||||
for i := 0; i < numMsgs; i++ {
|
||||
msg := &(*msgs)[i]
|
||||
sizes[i] = msg.N
|
||||
if sizes[i] == 0 {
|
||||
continue
|
||||
}
|
||||
addrPort := msg.Addr.(*net.UDPAddr).AddrPort()
|
||||
ep := &StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation
|
||||
getSrcFromControl(msg.OOB[:msg.NN], ep)
|
||||
eps[i] = ep
|
||||
}
|
||||
return numMsgs, nil
|
||||
}
|
||||
|
||||
func (s *StdNetBind) makeReceiveIPv4(pc *ipv4.PacketConn, conn *net.UDPConn, rxOffload bool) ReceiveFunc {
|
||||
return func(bufs [][]byte, sizes []int, eps []Endpoint) (n int, err error) {
|
||||
return s.receiveIP(pc, conn, rxOffload, bufs, sizes, eps)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StdNetBind) makeReceiveIPv6(pc *ipv6.PacketConn, conn *net.UDPConn) ReceiveFunc {
|
||||
func (s *StdNetBind) makeReceiveIPv6(pc *ipv6.PacketConn, conn *net.UDPConn, rxOffload bool) ReceiveFunc {
|
||||
return func(bufs [][]byte, sizes []int, eps []Endpoint) (n int, err error) {
|
||||
msgs := s.ipv6MsgsPool.Get().(*[]ipv6.Message)
|
||||
defer s.ipv6MsgsPool.Put(msgs)
|
||||
for i := range bufs {
|
||||
(*msgs)[i].Buffers[0] = bufs[i]
|
||||
}
|
||||
var numMsgs int
|
||||
if runtime.GOOS == "linux" {
|
||||
numMsgs, err = pc.ReadBatch(*msgs, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
msg := &(*msgs)[0]
|
||||
msg.N, msg.NN, _, msg.Addr, err = conn.ReadMsgUDP(msg.Buffers[0], msg.OOB)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
numMsgs = 1
|
||||
}
|
||||
for i := 0; i < numMsgs; i++ {
|
||||
msg := &(*msgs)[i]
|
||||
sizes[i] = msg.N
|
||||
addrPort := msg.Addr.(*net.UDPAddr).AddrPort()
|
||||
ep := &StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation
|
||||
getSrcFromControl(msg.OOB[:msg.NN], ep)
|
||||
eps[i] = ep
|
||||
}
|
||||
return numMsgs, nil
|
||||
return s.receiveIP(pc, conn, rxOffload, bufs, sizes, eps)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: When all Binds handle IdealBatchSize, remove this dynamic function and
|
||||
// rename the IdealBatchSize constant to BatchSize.
|
||||
func (s *StdNetBind) BatchSize() int {
|
||||
if runtime.GOOS == "linux" {
|
||||
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||
return IdealBatchSize
|
||||
}
|
||||
return 1
|
||||
@@ -293,28 +315,42 @@ func (s *StdNetBind) Close() error {
|
||||
}
|
||||
s.blackhole4 = false
|
||||
s.blackhole6 = false
|
||||
s.ipv4TxOffload = false
|
||||
s.ipv4RxOffload = false
|
||||
s.ipv6TxOffload = false
|
||||
s.ipv6RxOffload = false
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
type ErrUDPGSODisabled struct {
|
||||
onLaddr string
|
||||
RetryErr error
|
||||
}
|
||||
|
||||
func (e ErrUDPGSODisabled) Error() string {
|
||||
return fmt.Sprintf("disabled UDP GSO on %s, NIC(s) may not support checksum offload or peer MTU with protocol headers is greater than path MTU", e.onLaddr)
|
||||
}
|
||||
|
||||
func (e ErrUDPGSODisabled) Unwrap() error {
|
||||
return e.RetryErr
|
||||
}
|
||||
|
||||
func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error {
|
||||
s.mu.Lock()
|
||||
blackhole := s.blackhole4
|
||||
conn := s.ipv4
|
||||
var (
|
||||
pc4 *ipv4.PacketConn
|
||||
pc6 *ipv6.PacketConn
|
||||
)
|
||||
offload := s.ipv4TxOffload
|
||||
br := batchWriter(s.ipv4PC)
|
||||
is6 := false
|
||||
if endpoint.DstIP().Is6() {
|
||||
blackhole = s.blackhole6
|
||||
conn = s.ipv6
|
||||
pc6 = s.ipv6PC
|
||||
br = s.ipv6PC
|
||||
is6 = true
|
||||
} else {
|
||||
pc4 = s.ipv4PC
|
||||
offload = s.ipv6TxOffload
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
@@ -324,85 +360,185 @@ func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error {
|
||||
if conn == nil {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
|
||||
msgs := s.getMessages()
|
||||
defer s.putMessages(msgs)
|
||||
ua := s.udpAddrPool.Get().(*net.UDPAddr)
|
||||
defer s.udpAddrPool.Put(ua)
|
||||
if is6 {
|
||||
return s.send6(conn, pc6, endpoint, bufs)
|
||||
as16 := endpoint.DstIP().As16()
|
||||
copy(ua.IP, as16[:])
|
||||
ua.IP = ua.IP[:16]
|
||||
} else {
|
||||
return s.send4(conn, pc4, endpoint, bufs)
|
||||
as4 := endpoint.DstIP().As4()
|
||||
copy(ua.IP, as4[:])
|
||||
ua.IP = ua.IP[:4]
|
||||
}
|
||||
ua.Port = int(endpoint.(*StdNetEndpoint).Port())
|
||||
var (
|
||||
retried bool
|
||||
err error
|
||||
)
|
||||
retry:
|
||||
if offload {
|
||||
n := coalesceMessages(ua, endpoint.(*StdNetEndpoint), bufs, *msgs, setGSOSize)
|
||||
err = s.send(conn, br, (*msgs)[:n])
|
||||
if err != nil && offload && errShouldDisableUDPGSO(err) {
|
||||
offload = false
|
||||
s.mu.Lock()
|
||||
if is6 {
|
||||
s.ipv6TxOffload = false
|
||||
} else {
|
||||
s.ipv4TxOffload = false
|
||||
}
|
||||
s.mu.Unlock()
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
} else {
|
||||
for i := range bufs {
|
||||
(*msgs)[i].Addr = ua
|
||||
(*msgs)[i].Buffers[0] = bufs[i]
|
||||
setSrcControl(&(*msgs)[i].OOB, endpoint.(*StdNetEndpoint))
|
||||
}
|
||||
err = s.send(conn, br, (*msgs)[:len(bufs)])
|
||||
}
|
||||
if retried {
|
||||
return ErrUDPGSODisabled{onLaddr: conn.LocalAddr().String(), RetryErr: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StdNetBind) send4(conn *net.UDPConn, pc *ipv4.PacketConn, ep Endpoint, bufs [][]byte) error {
|
||||
ua := s.udpAddrPool.Get().(*net.UDPAddr)
|
||||
as4 := ep.DstIP().As4()
|
||||
copy(ua.IP, as4[:])
|
||||
ua.IP = ua.IP[:4]
|
||||
ua.Port = int(ep.(*StdNetEndpoint).Port())
|
||||
msgs := s.ipv4MsgsPool.Get().(*[]ipv4.Message)
|
||||
for i, buf := range bufs {
|
||||
(*msgs)[i].Buffers[0] = buf
|
||||
(*msgs)[i].Addr = ua
|
||||
setSrcControl(&(*msgs)[i].OOB, ep.(*StdNetEndpoint))
|
||||
}
|
||||
func (s *StdNetBind) send(conn *net.UDPConn, pc batchWriter, msgs []ipv6.Message) error {
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
start int
|
||||
)
|
||||
if runtime.GOOS == "linux" {
|
||||
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||
for {
|
||||
n, err = pc.WriteBatch((*msgs)[start:len(bufs)], 0)
|
||||
if err != nil || n == len((*msgs)[start:len(bufs)]) {
|
||||
n, err = pc.WriteBatch(msgs[start:], 0)
|
||||
if err != nil || n == len(msgs[start:]) {
|
||||
break
|
||||
}
|
||||
start += n
|
||||
}
|
||||
} else {
|
||||
for i, buf := range bufs {
|
||||
_, _, err = conn.WriteMsgUDP(buf, (*msgs)[i].OOB, ua)
|
||||
for _, msg := range msgs {
|
||||
_, _, err = conn.WriteMsgUDP(msg.Buffers[0], msg.OOB, msg.Addr.(*net.UDPAddr))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
s.udpAddrPool.Put(ua)
|
||||
s.ipv4MsgsPool.Put(msgs)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *StdNetBind) send6(conn *net.UDPConn, pc *ipv6.PacketConn, ep Endpoint, bufs [][]byte) error {
|
||||
ua := s.udpAddrPool.Get().(*net.UDPAddr)
|
||||
as16 := ep.DstIP().As16()
|
||||
copy(ua.IP, as16[:])
|
||||
ua.IP = ua.IP[:16]
|
||||
ua.Port = int(ep.(*StdNetEndpoint).Port())
|
||||
msgs := s.ipv6MsgsPool.Get().(*[]ipv6.Message)
|
||||
for i, buf := range bufs {
|
||||
(*msgs)[i].Buffers[0] = buf
|
||||
(*msgs)[i].Addr = ua
|
||||
setSrcControl(&(*msgs)[i].OOB, ep.(*StdNetEndpoint))
|
||||
}
|
||||
const (
|
||||
// Exceeding these values results in EMSGSIZE. They account for layer3 and
|
||||
// layer4 headers. IPv6 does not need to account for itself as the payload
|
||||
// length field is self excluding.
|
||||
maxIPv4PayloadLen = 1<<16 - 1 - 20 - 8
|
||||
maxIPv6PayloadLen = 1<<16 - 1 - 8
|
||||
|
||||
// This is a hard limit imposed by the kernel.
|
||||
udpSegmentMaxDatagrams = 64
|
||||
)
|
||||
|
||||
type setGSOFunc func(control *[]byte, gsoSize uint16)
|
||||
|
||||
func coalesceMessages(addr *net.UDPAddr, ep *StdNetEndpoint, bufs [][]byte, msgs []ipv6.Message, setGSO setGSOFunc) int {
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
start int
|
||||
base = -1 // index of msg we are currently coalescing into
|
||||
gsoSize int // segmentation size of msgs[base]
|
||||
dgramCnt int // number of dgrams coalesced into msgs[base]
|
||||
endBatch bool // tracking flag to start a new batch on next iteration of bufs
|
||||
)
|
||||
if runtime.GOOS == "linux" {
|
||||
for {
|
||||
n, err = pc.WriteBatch((*msgs)[start:len(bufs)], 0)
|
||||
if err != nil || n == len((*msgs)[start:len(bufs)]) {
|
||||
break
|
||||
maxPayloadLen := maxIPv4PayloadLen
|
||||
if ep.DstIP().Is6() {
|
||||
maxPayloadLen = maxIPv6PayloadLen
|
||||
}
|
||||
for i, buf := range bufs {
|
||||
if i > 0 {
|
||||
msgLen := len(buf)
|
||||
baseLenBefore := len(msgs[base].Buffers[0])
|
||||
freeBaseCap := cap(msgs[base].Buffers[0]) - baseLenBefore
|
||||
if msgLen+baseLenBefore <= maxPayloadLen &&
|
||||
msgLen <= gsoSize &&
|
||||
msgLen <= freeBaseCap &&
|
||||
dgramCnt < udpSegmentMaxDatagrams &&
|
||||
!endBatch {
|
||||
msgs[base].Buffers[0] = append(msgs[base].Buffers[0], buf...)
|
||||
if i == len(bufs)-1 {
|
||||
setGSO(&msgs[base].OOB, uint16(gsoSize))
|
||||
}
|
||||
dgramCnt++
|
||||
if msgLen < gsoSize {
|
||||
// A smaller than gsoSize packet on the tail is legal, but
|
||||
// it must end the batch.
|
||||
endBatch = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
start += n
|
||||
}
|
||||
} else {
|
||||
for i, buf := range bufs {
|
||||
_, _, err = conn.WriteMsgUDP(buf, (*msgs)[i].OOB, ua)
|
||||
if err != nil {
|
||||
break
|
||||
if dgramCnt > 1 {
|
||||
setGSO(&msgs[base].OOB, uint16(gsoSize))
|
||||
}
|
||||
// Reset prior to incrementing base since we are preparing to start a
|
||||
// new potential batch.
|
||||
endBatch = false
|
||||
base++
|
||||
gsoSize = len(buf)
|
||||
setSrcControl(&msgs[base].OOB, ep)
|
||||
msgs[base].Buffers[0] = buf
|
||||
msgs[base].Addr = addr
|
||||
dgramCnt = 1
|
||||
}
|
||||
return base + 1
|
||||
}
|
||||
|
||||
type getGSOFunc func(control []byte) (int, error)
|
||||
|
||||
func splitCoalescedMessages(msgs []ipv6.Message, firstMsgAt int, getGSO getGSOFunc) (n int, err error) {
|
||||
for i := firstMsgAt; i < len(msgs); i++ {
|
||||
msg := &msgs[i]
|
||||
if msg.N == 0 {
|
||||
return n, err
|
||||
}
|
||||
var (
|
||||
gsoSize int
|
||||
start int
|
||||
end = msg.N
|
||||
numToSplit = 1
|
||||
)
|
||||
gsoSize, err = getGSO(msg.OOB[:msg.NN])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if gsoSize > 0 {
|
||||
numToSplit = (msg.N + gsoSize - 1) / gsoSize
|
||||
end = gsoSize
|
||||
}
|
||||
for j := 0; j < numToSplit; j++ {
|
||||
if n > i {
|
||||
return n, errors.New("splitting coalesced packet resulted in overflow")
|
||||
}
|
||||
copied := copy(msgs[n].Buffers[0], msg.Buffers[0][start:end])
|
||||
msgs[n].N = copied
|
||||
msgs[n].Addr = msg.Addr
|
||||
start = end
|
||||
end += gsoSize
|
||||
if end > msg.N {
|
||||
end = msg.N
|
||||
}
|
||||
n++
|
||||
}
|
||||
if i != n-1 {
|
||||
// It is legal for bytes to move within msg.Buffers[0] as a result
|
||||
// of splitting, so we only zero the source msg len when it is not
|
||||
// the destination of the last split operation above.
|
||||
msg.N = 0
|
||||
}
|
||||
}
|
||||
s.udpAddrPool.Put(ua)
|
||||
s.ipv6MsgsPool.Put(msgs)
|
||||
return err
|
||||
return n, nil
|
||||
}
|
||||
+229
-1
@@ -1,6 +1,12 @@
|
||||
package conn
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
func TestStdNetBindReceiveFuncAfterClose(t *testing.T) {
|
||||
bind := NewStdNetBind().(*StdNetBind)
|
||||
@@ -20,3 +26,225 @@ func TestStdNetBindReceiveFuncAfterClose(t *testing.T) {
|
||||
fn(bufs, sizes, eps)
|
||||
}
|
||||
}
|
||||
|
||||
func mockSetGSOSize(control *[]byte, gsoSize uint16) {
|
||||
*control = (*control)[:cap(*control)]
|
||||
binary.LittleEndian.PutUint16(*control, gsoSize)
|
||||
}
|
||||
|
||||
func Test_coalesceMessages(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
buffs [][]byte
|
||||
wantLens []int
|
||||
wantGSO []int
|
||||
}{
|
||||
{
|
||||
name: "one message no coalesce",
|
||||
buffs: [][]byte{
|
||||
make([]byte, 1, 1),
|
||||
},
|
||||
wantLens: []int{1},
|
||||
wantGSO: []int{0},
|
||||
},
|
||||
{
|
||||
name: "two messages equal len coalesce",
|
||||
buffs: [][]byte{
|
||||
make([]byte, 1, 2),
|
||||
make([]byte, 1, 1),
|
||||
},
|
||||
wantLens: []int{2},
|
||||
wantGSO: []int{1},
|
||||
},
|
||||
{
|
||||
name: "two messages unequal len coalesce",
|
||||
buffs: [][]byte{
|
||||
make([]byte, 2, 3),
|
||||
make([]byte, 1, 1),
|
||||
},
|
||||
wantLens: []int{3},
|
||||
wantGSO: []int{2},
|
||||
},
|
||||
{
|
||||
name: "three messages second unequal len coalesce",
|
||||
buffs: [][]byte{
|
||||
make([]byte, 2, 3),
|
||||
make([]byte, 1, 1),
|
||||
make([]byte, 2, 2),
|
||||
},
|
||||
wantLens: []int{3, 2},
|
||||
wantGSO: []int{2, 0},
|
||||
},
|
||||
{
|
||||
name: "three messages limited cap coalesce",
|
||||
buffs: [][]byte{
|
||||
make([]byte, 2, 4),
|
||||
make([]byte, 2, 2),
|
||||
make([]byte, 2, 2),
|
||||
},
|
||||
wantLens: []int{4, 2},
|
||||
wantGSO: []int{2, 0},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
addr := &net.UDPAddr{
|
||||
IP: net.ParseIP("127.0.0.1").To4(),
|
||||
Port: 1,
|
||||
}
|
||||
msgs := make([]ipv6.Message, len(tt.buffs))
|
||||
for i := range msgs {
|
||||
msgs[i].Buffers = make([][]byte, 1)
|
||||
msgs[i].OOB = make([]byte, 0, 2)
|
||||
}
|
||||
got := coalesceMessages(addr, &StdNetEndpoint{AddrPort: addr.AddrPort()}, tt.buffs, msgs, mockSetGSOSize)
|
||||
if got != len(tt.wantLens) {
|
||||
t.Fatalf("got len %d want: %d", got, len(tt.wantLens))
|
||||
}
|
||||
for i := 0; i < got; i++ {
|
||||
if msgs[i].Addr != addr {
|
||||
t.Errorf("msgs[%d].Addr != passed addr", i)
|
||||
}
|
||||
gotLen := len(msgs[i].Buffers[0])
|
||||
if gotLen != tt.wantLens[i] {
|
||||
t.Errorf("len(msgs[%d].Buffers[0]) %d != %d", i, gotLen, tt.wantLens[i])
|
||||
}
|
||||
gotGSO, err := mockGetGSOSize(msgs[i].OOB)
|
||||
if err != nil {
|
||||
t.Fatalf("msgs[%d] getGSOSize err: %v", i, err)
|
||||
}
|
||||
if gotGSO != tt.wantGSO[i] {
|
||||
t.Errorf("msgs[%d] gsoSize %d != %d", i, gotGSO, tt.wantGSO[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mockGetGSOSize(control []byte) (int, error) {
|
||||
if len(control) < 2 {
|
||||
return 0, nil
|
||||
}
|
||||
return int(binary.LittleEndian.Uint16(control)), nil
|
||||
}
|
||||
|
||||
func Test_splitCoalescedMessages(t *testing.T) {
|
||||
newMsg := func(n, gso int) ipv6.Message {
|
||||
msg := ipv6.Message{
|
||||
Buffers: [][]byte{make([]byte, 1<<16-1)},
|
||||
N: n,
|
||||
OOB: make([]byte, 2),
|
||||
}
|
||||
binary.LittleEndian.PutUint16(msg.OOB, uint16(gso))
|
||||
if gso > 0 {
|
||||
msg.NN = 2
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
msgs []ipv6.Message
|
||||
firstMsgAt int
|
||||
wantNumEval int
|
||||
wantMsgLens []int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "second last split last empty",
|
||||
msgs: []ipv6.Message{
|
||||
newMsg(0, 0),
|
||||
newMsg(0, 0),
|
||||
newMsg(3, 1),
|
||||
newMsg(0, 0),
|
||||
},
|
||||
firstMsgAt: 2,
|
||||
wantNumEval: 3,
|
||||
wantMsgLens: []int{1, 1, 1, 0},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "second last no split last empty",
|
||||
msgs: []ipv6.Message{
|
||||
newMsg(0, 0),
|
||||
newMsg(0, 0),
|
||||
newMsg(1, 0),
|
||||
newMsg(0, 0),
|
||||
},
|
||||
firstMsgAt: 2,
|
||||
wantNumEval: 1,
|
||||
wantMsgLens: []int{1, 0, 0, 0},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "second last no split last no split",
|
||||
msgs: []ipv6.Message{
|
||||
newMsg(0, 0),
|
||||
newMsg(0, 0),
|
||||
newMsg(1, 0),
|
||||
newMsg(1, 0),
|
||||
},
|
||||
firstMsgAt: 2,
|
||||
wantNumEval: 2,
|
||||
wantMsgLens: []int{1, 1, 0, 0},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "second last no split last split",
|
||||
msgs: []ipv6.Message{
|
||||
newMsg(0, 0),
|
||||
newMsg(0, 0),
|
||||
newMsg(1, 0),
|
||||
newMsg(3, 1),
|
||||
},
|
||||
firstMsgAt: 2,
|
||||
wantNumEval: 4,
|
||||
wantMsgLens: []int{1, 1, 1, 1},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "second last split last split",
|
||||
msgs: []ipv6.Message{
|
||||
newMsg(0, 0),
|
||||
newMsg(0, 0),
|
||||
newMsg(2, 1),
|
||||
newMsg(2, 1),
|
||||
},
|
||||
firstMsgAt: 2,
|
||||
wantNumEval: 4,
|
||||
wantMsgLens: []int{1, 1, 1, 1},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "second last no split last split overflow",
|
||||
msgs: []ipv6.Message{
|
||||
newMsg(0, 0),
|
||||
newMsg(0, 0),
|
||||
newMsg(1, 0),
|
||||
newMsg(4, 1),
|
||||
},
|
||||
firstMsgAt: 2,
|
||||
wantNumEval: 4,
|
||||
wantMsgLens: []int{1, 1, 1, 1},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := splitCoalescedMessages(tt.msgs, 2, mockGetGSOSize)
|
||||
if err != nil && !tt.wantErr {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if got != tt.wantNumEval {
|
||||
t.Fatalf("got to eval: %d want: %d", got, tt.wantNumEval)
|
||||
}
|
||||
for i, msg := range tt.msgs {
|
||||
if msg.N != tt.wantMsgLens[i] {
|
||||
t.Fatalf("msg[%d].N: %d want: %d", i, msg.N, tt.wantMsgLens[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn/winrio"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn/winrio"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package bindtest
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
)
|
||||
|
||||
type ChannelBind struct {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
// Package conn implements WireGuard's network connections.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -13,6 +13,35 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Taken from go/src/internal/syscall/unix/kernel_version_linux.go
|
||||
func kernelVersion() (major, minor int) {
|
||||
var uname unix.Utsname
|
||||
if err := unix.Uname(&uname); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
values [2]int
|
||||
value, vi int
|
||||
)
|
||||
for _, c := range uname.Release {
|
||||
if '0' <= c && c <= '9' {
|
||||
value = (value * 10) + int(c-'0')
|
||||
} else {
|
||||
// Note that we're assuming N.N.N here.
|
||||
// If we see anything else, we are likely to mis-parse it.
|
||||
values[vi] = value
|
||||
vi++
|
||||
if vi >= len(values) {
|
||||
break
|
||||
}
|
||||
value = 0
|
||||
}
|
||||
}
|
||||
|
||||
return values[0], values[1]
|
||||
}
|
||||
|
||||
func init() {
|
||||
controlFns = append(controlFns,
|
||||
|
||||
@@ -57,5 +86,24 @@ func init() {
|
||||
}
|
||||
return err
|
||||
},
|
||||
|
||||
// Attempt to enable UDP_GRO
|
||||
func(network, address string, c syscall.RawConn) error {
|
||||
// Kernels below 5.12 are missing 98184612aca0 ("net:
|
||||
// udp: Add support for getsockopt(..., ..., UDP_GRO,
|
||||
// ..., ...);"), which means we can't read this back
|
||||
// later. We could pipe the return value through to
|
||||
// the rest of the code, but UDP_GRO is kind of buggy
|
||||
// anyway, so just gate this here.
|
||||
major, minor := kernelVersion()
|
||||
if major < 5 || (major == 5 && minor < 12) {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.Control(func(fd uintptr) {
|
||||
_ = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
|
||||
})
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build !linux
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
func errShouldDisableUDPGSO(_ error) bool {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func errShouldDisableUDPGSO(err error) bool {
|
||||
var serr *os.SyscallError
|
||||
if errors.As(err, &serr) {
|
||||
// EIO is returned by udp_send_skb() if the device driver does not have
|
||||
// tx checksumming enabled, which is a hard requirement of UDP_SEGMENT.
|
||||
// See:
|
||||
// https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man7/udp.7?id=806eabd74910447f21005160e90957bde4db0183#n228
|
||||
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/udp.c?h=v6.2&id=c9c3395d5e3dcc6daee66c6908354d47bf98cb0c#n942
|
||||
// If gso_size + udp + ip headers > fragment size EINVAL is returned.
|
||||
// It occurs when the peer mtu + wg headers is greater than path mtu.
|
||||
return serr.Err == unix.EIO || serr.Err == unix.EINVAL
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
import "net"
|
||||
|
||||
func supportsUDPOffload(_ *net.UDPConn) (txOffload, rxOffload bool) {
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func supportsUDPOffload(conn *net.UDPConn) (txOffload, rxOffload bool) {
|
||||
rc, err := conn.SyscallConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = rc.Control(func(fd uintptr) {
|
||||
_, errSyscall := unix.GetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_SEGMENT)
|
||||
txOffload = errSyscall == nil
|
||||
// getsockopt(IPPROTO_UDP, UDP_GRO) is not supported in android
|
||||
// use setsockopt workaround
|
||||
errSyscall = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
|
||||
rxOffload = errSyscall == nil
|
||||
})
|
||||
if err != nil {
|
||||
return false, false
|
||||
}
|
||||
return txOffload, rxOffload
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//go:build !linux
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
// getGSOSize parses control for UDP_GRO and if found returns its GSO size data.
|
||||
func getGSOSize(control []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// setGSOSize sets a UDP_SEGMENT in control based on gsoSize.
|
||||
func setGSOSize(control *[]byte, gsoSize uint16) {
|
||||
}
|
||||
|
||||
// gsoControlSize returns the recommended buffer size for pooling sticky and UDP
|
||||
// offloading control data.
|
||||
const gsoControlSize = 0
|
||||
@@ -0,0 +1,65 @@
|
||||
//go:build linux
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
sizeOfGSOData = 2
|
||||
)
|
||||
|
||||
// getGSOSize parses control for UDP_GRO and if found returns its GSO size data.
|
||||
func getGSOSize(control []byte) (int, error) {
|
||||
var (
|
||||
hdr unix.Cmsghdr
|
||||
data []byte
|
||||
rem = control
|
||||
err error
|
||||
)
|
||||
|
||||
for len(rem) > unix.SizeofCmsghdr {
|
||||
hdr, data, rem, err = unix.ParseOneSocketControlMessage(rem)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error parsing socket control message: %w", err)
|
||||
}
|
||||
if hdr.Level == unix.SOL_UDP && hdr.Type == unix.UDP_GRO && len(data) >= sizeOfGSOData {
|
||||
var gso uint16
|
||||
copy(unsafe.Slice((*byte)(unsafe.Pointer(&gso)), sizeOfGSOData), data[:sizeOfGSOData])
|
||||
return int(gso), nil
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// setGSOSize sets a UDP_SEGMENT in control based on gsoSize. It leaves existing
|
||||
// data in control untouched.
|
||||
func setGSOSize(control *[]byte, gsoSize uint16) {
|
||||
existingLen := len(*control)
|
||||
avail := cap(*control) - existingLen
|
||||
space := unix.CmsgSpace(sizeOfGSOData)
|
||||
if avail < space {
|
||||
return
|
||||
}
|
||||
*control = (*control)[:cap(*control)]
|
||||
gsoControl := (*control)[existingLen:]
|
||||
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&(gsoControl)[0]))
|
||||
hdr.Level = unix.SOL_UDP
|
||||
hdr.Type = unix.UDP_SEGMENT
|
||||
hdr.SetLen(unix.CmsgLen(sizeOfGSOData))
|
||||
copy((gsoControl)[unix.CmsgLen(0):], unsafe.Slice((*byte)(unsafe.Pointer(&gsoSize)), sizeOfGSOData))
|
||||
*control = (*control)[:existingLen+space]
|
||||
}
|
||||
|
||||
// gsoControlSize returns the recommended buffer size for pooling UDP
|
||||
// offloading control data.
|
||||
var gsoControlSize = unix.CmsgSpace(sizeOfGSOData)
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -21,8 +21,9 @@ func (e *StdNetEndpoint) SrcToString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// TODO: macOS, FreeBSD and other BSDs likely do support this feature set, but
|
||||
// use alternatively named flags and need ports and require testing.
|
||||
// TODO: macOS, FreeBSD and other BSDs likely do support the sticky sockets
|
||||
// {get,set}srcControl feature set, but use alternatively named flags and need
|
||||
// ports and require testing.
|
||||
|
||||
// getSrcFromControl parses the control for PKTINFO and if found updates ep with
|
||||
// the source information found.
|
||||
@@ -34,8 +35,8 @@ func getSrcFromControl(control []byte, ep *StdNetEndpoint) {
|
||||
func setSrcControl(control *[]byte, ep *StdNetEndpoint) {
|
||||
}
|
||||
|
||||
// srcControlSize returns the recommended buffer size for pooling sticky control
|
||||
// data.
|
||||
const srcControlSize = 0
|
||||
// stickyControlSize returns the recommended buffer size for pooling sticky
|
||||
// offloading control data.
|
||||
const stickyControlSize = 0
|
||||
|
||||
const StdNetSupportsStickySockets = false
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -105,6 +105,8 @@ func setSrcControl(control *[]byte, ep *StdNetEndpoint) {
|
||||
*control = append(*control, ep.src...)
|
||||
}
|
||||
|
||||
var srcControlSize = unix.CmsgSpace(unix.SizeofInet6Pktinfo)
|
||||
// stickyControlSize returns the recommended buffer size for pooling sticky
|
||||
// offloading control data.
|
||||
var stickyControlSize = unix.CmsgSpace(unix.SizeofInet6Pktinfo)
|
||||
|
||||
const StdNetSupportsStickySockets = true
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -60,7 +60,7 @@ func Test_setSrcControl(t *testing.T) {
|
||||
}
|
||||
setSrc(ep, netip.MustParseAddr("127.0.0.1"), 5)
|
||||
|
||||
control := make([]byte, srcControlSize)
|
||||
control := make([]byte, stickyControlSize)
|
||||
|
||||
setSrcControl(&control, ep)
|
||||
|
||||
@@ -89,7 +89,7 @@ func Test_setSrcControl(t *testing.T) {
|
||||
}
|
||||
setSrc(ep, netip.MustParseAddr("::1"), 5)
|
||||
|
||||
control := make([]byte, srcControlSize)
|
||||
control := make([]byte, stickyControlSize)
|
||||
|
||||
setSrcControl(&control, ep)
|
||||
|
||||
@@ -113,7 +113,7 @@ func Test_setSrcControl(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ClearOnNoSrc", func(t *testing.T) {
|
||||
control := make([]byte, unix.CmsgLen(0))
|
||||
control := make([]byte, stickyControlSize)
|
||||
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
|
||||
hdr.Level = 1
|
||||
hdr.Type = 2
|
||||
@@ -129,7 +129,7 @@ func Test_setSrcControl(t *testing.T) {
|
||||
|
||||
func Test_getSrcFromControl(t *testing.T) {
|
||||
t.Run("IPv4", func(t *testing.T) {
|
||||
control := make([]byte, unix.CmsgSpace(unix.SizeofInet4Pktinfo))
|
||||
control := make([]byte, stickyControlSize)
|
||||
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
|
||||
hdr.Level = unix.IPPROTO_IP
|
||||
hdr.Type = unix.IP_PKTINFO
|
||||
@@ -149,7 +149,7 @@ func Test_getSrcFromControl(t *testing.T) {
|
||||
}
|
||||
})
|
||||
t.Run("IPv6", func(t *testing.T) {
|
||||
control := make([]byte, unix.CmsgSpace(unix.SizeofInet6Pktinfo))
|
||||
control := make([]byte, stickyControlSize)
|
||||
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
|
||||
hdr.Level = unix.IPPROTO_IPV6
|
||||
hdr.Type = unix.IPV6_PKTINFO
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winrio
|
||||
|
||||
+56
-33
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -223,6 +223,60 @@ func (table *AllowedIPs) EntriesForPeer(peer *Peer, cb func(prefix netip.Prefix)
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) remove() {
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = nil
|
||||
if node.child[0] != nil && node.child[1] != nil {
|
||||
return
|
||||
}
|
||||
bit := 0
|
||||
if node.child[0] == nil {
|
||||
bit = 1
|
||||
}
|
||||
child := node.child[bit]
|
||||
if child != nil {
|
||||
child.parent = node.parent
|
||||
}
|
||||
*node.parent.parentBit = child
|
||||
if node.child[0] != nil || node.child[1] != nil || node.parent.parentBitType > 1 {
|
||||
node.zeroizePointers()
|
||||
return
|
||||
}
|
||||
parent := (*trieEntry)(unsafe.Pointer(uintptr(unsafe.Pointer(node.parent.parentBit)) - unsafe.Offsetof(node.child) - unsafe.Sizeof(node.child[0])*uintptr(node.parent.parentBitType)))
|
||||
if parent.peer != nil {
|
||||
node.zeroizePointers()
|
||||
return
|
||||
}
|
||||
child = parent.child[node.parent.parentBitType^1]
|
||||
if child != nil {
|
||||
child.parent = parent.parent
|
||||
}
|
||||
*parent.parent.parentBit = child
|
||||
node.zeroizePointers()
|
||||
parent.zeroizePointers()
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) Remove(prefix netip.Prefix, peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
var node *trieEntry
|
||||
var exact bool
|
||||
|
||||
if prefix.Addr().Is6() {
|
||||
ip := prefix.Addr().As16()
|
||||
node, exact = table.IPv6.nodePlacement(ip[:], uint8(prefix.Bits()))
|
||||
} else if prefix.Addr().Is4() {
|
||||
ip := prefix.Addr().As4()
|
||||
node, exact = table.IPv4.nodePlacement(ip[:], uint8(prefix.Bits()))
|
||||
} else {
|
||||
panic(errors.New("removing unknown address type"))
|
||||
}
|
||||
if !exact || node == nil || peer != node.peer {
|
||||
return
|
||||
}
|
||||
node.remove()
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
@@ -230,38 +284,7 @@ func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
var next *list.Element
|
||||
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
|
||||
next = elem.Next()
|
||||
node := elem.Value.(*trieEntry)
|
||||
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = nil
|
||||
if node.child[0] != nil && node.child[1] != nil {
|
||||
continue
|
||||
}
|
||||
bit := 0
|
||||
if node.child[0] == nil {
|
||||
bit = 1
|
||||
}
|
||||
child := node.child[bit]
|
||||
if child != nil {
|
||||
child.parent = node.parent
|
||||
}
|
||||
*node.parent.parentBit = child
|
||||
if node.child[0] != nil || node.child[1] != nil || node.parent.parentBitType > 1 {
|
||||
node.zeroizePointers()
|
||||
continue
|
||||
}
|
||||
parent := (*trieEntry)(unsafe.Pointer(uintptr(unsafe.Pointer(node.parent.parentBit)) - unsafe.Offsetof(node.child) - unsafe.Sizeof(node.child[0])*uintptr(node.parent.parentBitType)))
|
||||
if parent.peer != nil {
|
||||
node.zeroizePointers()
|
||||
continue
|
||||
}
|
||||
child = parent.child[node.parent.parentBitType^1]
|
||||
if child != nil {
|
||||
child.parent = parent.parent
|
||||
}
|
||||
*parent.parent.parentBit = child
|
||||
node.zeroizePointers()
|
||||
parent.zeroizePointers()
|
||||
elem.Value.(*trieEntry).remove()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -83,7 +83,7 @@ func TestTrieRandom(t *testing.T) {
|
||||
var peers []*Peer
|
||||
var allowedIPs AllowedIPs
|
||||
|
||||
rand.Seed(1)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
|
||||
for n := 0; n < NumberOfPeers; n++ {
|
||||
peers = append(peers, &Peer{})
|
||||
@@ -91,14 +91,14 @@ func TestTrieRandom(t *testing.T) {
|
||||
|
||||
for n := 0; n < NumberOfAddresses; n++ {
|
||||
var addr4 [4]byte
|
||||
rand.Read(addr4[:])
|
||||
rng.Read(addr4[:])
|
||||
cidr := uint8(rand.Intn(32) + 1)
|
||||
index := rand.Intn(NumberOfPeers)
|
||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4(addr4), int(cidr)), peers[index])
|
||||
slow4 = slow4.Insert(addr4[:], cidr, peers[index])
|
||||
|
||||
var addr6 [16]byte
|
||||
rand.Read(addr6[:])
|
||||
rng.Read(addr6[:])
|
||||
cidr = uint8(rand.Intn(128) + 1)
|
||||
index = rand.Intn(NumberOfPeers)
|
||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(addr6), int(cidr)), peers[index])
|
||||
@@ -109,7 +109,7 @@ func TestTrieRandom(t *testing.T) {
|
||||
for p = 0; ; p++ {
|
||||
for n := 0; n < NumberOfTests; n++ {
|
||||
var addr4 [4]byte
|
||||
rand.Read(addr4[:])
|
||||
rng.Read(addr4[:])
|
||||
peer1 := slow4.Lookup(addr4[:])
|
||||
peer2 := allowedIPs.Lookup(addr4[:])
|
||||
if peer1 != peer2 {
|
||||
@@ -117,7 +117,7 @@ func TestTrieRandom(t *testing.T) {
|
||||
}
|
||||
|
||||
var addr6 [16]byte
|
||||
rand.Read(addr6[:])
|
||||
rng.Read(addr6[:])
|
||||
peer1 = slow6.Lookup(addr6[:])
|
||||
peer2 = allowedIPs.Lookup(addr6[:])
|
||||
if peer1 != peer2 {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -39,12 +39,12 @@ func TestCommonBits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkTrie(peerNumber, addressNumber, addressLength int, b *testing.B) {
|
||||
func benchmarkTrie(peerNumber, addressNumber, _ int, b *testing.B) {
|
||||
var trie *trieEntry
|
||||
var peers []*Peer
|
||||
root := parentIndirection{&trie, 2}
|
||||
|
||||
rand.Seed(1)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
|
||||
const AddressLength = 4
|
||||
|
||||
@@ -54,15 +54,15 @@ func benchmarkTrie(peerNumber, addressNumber, addressLength int, b *testing.B) {
|
||||
|
||||
for n := 0; n < addressNumber; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
cidr := uint8(rand.Uint32() % (AddressLength * 8))
|
||||
index := rand.Int() % peerNumber
|
||||
rng.Read(addr[:])
|
||||
cidr := uint8(rng.Uint32() % (AddressLength * 8))
|
||||
index := rng.Int() % peerNumber
|
||||
root.insert(addr[:], cidr, peers[index])
|
||||
}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
rng.Read(addr[:])
|
||||
trie.lookup(addr[:])
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,10 @@ func TestTrieIPv4(t *testing.T) {
|
||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4([4]byte{a, b, c, d}), int(cidr)), peer)
|
||||
}
|
||||
|
||||
remove := func(peer *Peer, a, b, c, d byte, cidr uint8) {
|
||||
allowedIPs.Remove(netip.PrefixFrom(netip.AddrFrom4([4]byte{a, b, c, d}), int(cidr)), peer)
|
||||
}
|
||||
|
||||
assertEQ := func(peer *Peer, a, b, c, d byte) {
|
||||
p := allowedIPs.Lookup([]byte{a, b, c, d})
|
||||
if p != peer {
|
||||
@@ -176,6 +180,21 @@ func TestTrieIPv4(t *testing.T) {
|
||||
allowedIPs.RemoveByPeer(a)
|
||||
|
||||
assertNEQ(a, 192, 168, 0, 1)
|
||||
|
||||
insert(a, 1, 0, 0, 0, 32)
|
||||
insert(a, 192, 0, 0, 0, 24)
|
||||
assertEQ(a, 1, 0, 0, 0)
|
||||
assertEQ(a, 192, 0, 0, 1)
|
||||
remove(a, 192, 0, 0, 0, 32)
|
||||
assertEQ(a, 192, 0, 0, 1)
|
||||
remove(nil, 192, 0, 0, 0, 24)
|
||||
assertEQ(a, 192, 0, 0, 1)
|
||||
remove(b, 192, 0, 0, 0, 24)
|
||||
assertEQ(a, 192, 0, 0, 1)
|
||||
remove(a, 192, 0, 0, 0, 24)
|
||||
assertNEQ(a, 192, 0, 0, 1)
|
||||
remove(a, 1, 0, 0, 0, 32)
|
||||
assertNEQ(a, 1, 0, 0, 0)
|
||||
}
|
||||
|
||||
/* Test ported from kernel implementation:
|
||||
@@ -211,6 +230,15 @@ func TestTrieIPv6(t *testing.T) {
|
||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(*(*[16]byte)(addr)), int(cidr)), peer)
|
||||
}
|
||||
|
||||
remove := func(peer *Peer, a, b, c, d uint32, cidr uint8) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
addr = append(addr, expand(b)...)
|
||||
addr = append(addr, expand(c)...)
|
||||
addr = append(addr, expand(d)...)
|
||||
allowedIPs.Remove(netip.PrefixFrom(netip.AddrFrom16(*(*[16]byte)(addr)), int(cidr)), peer)
|
||||
}
|
||||
|
||||
assertEQ := func(peer *Peer, a, b, c, d uint32) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
@@ -223,6 +251,18 @@ func TestTrieIPv6(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
assertNEQ := func(peer *Peer, a, b, c, d uint32) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
addr = append(addr, expand(b)...)
|
||||
addr = append(addr, expand(c)...)
|
||||
addr = append(addr, expand(d)...)
|
||||
p := allowedIPs.Lookup(addr)
|
||||
if p == peer {
|
||||
t.Error("Assert NEQ failed")
|
||||
}
|
||||
}
|
||||
|
||||
insert(d, 0x26075300, 0x60006b00, 0, 0xc05f0543, 128)
|
||||
insert(c, 0x26075300, 0x60006b00, 0, 0, 64)
|
||||
insert(e, 0, 0, 0, 0, 0)
|
||||
@@ -244,4 +284,21 @@ func TestTrieIPv6(t *testing.T) {
|
||||
assertEQ(h, 0x24046800, 0x40040800, 0, 0)
|
||||
assertEQ(h, 0x24046800, 0x40040800, 0x10101010, 0x10101010)
|
||||
assertEQ(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef)
|
||||
|
||||
insert(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
insert(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
|
||||
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
assertEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
|
||||
remove(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 96)
|
||||
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
remove(nil, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
remove(b, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
remove(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
assertNEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
remove(b, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
|
||||
assertEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
|
||||
remove(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
|
||||
assertNEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -8,7 +8,7 @@ package device
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
)
|
||||
|
||||
type DummyDatagram struct {
|
||||
|
||||
+17
-17
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -19,13 +19,13 @@ import (
|
||||
// call wg.Done to remove the initial reference.
|
||||
// When the refcount hits 0, the queue's channel is closed.
|
||||
type outboundQueue struct {
|
||||
c chan *QueueOutboundElement
|
||||
c chan *QueueOutboundElementsContainer
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func newOutboundQueue() *outboundQueue {
|
||||
q := &outboundQueue{
|
||||
c: make(chan *QueueOutboundElement, QueueOutboundSize),
|
||||
c: make(chan *QueueOutboundElementsContainer, QueueOutboundSize),
|
||||
}
|
||||
q.wg.Add(1)
|
||||
go func() {
|
||||
@@ -37,13 +37,13 @@ func newOutboundQueue() *outboundQueue {
|
||||
|
||||
// A inboundQueue is similar to an outboundQueue; see those docs.
|
||||
type inboundQueue struct {
|
||||
c chan *QueueInboundElement
|
||||
c chan *QueueInboundElementsContainer
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func newInboundQueue() *inboundQueue {
|
||||
q := &inboundQueue{
|
||||
c: make(chan *QueueInboundElement, QueueInboundSize),
|
||||
c: make(chan *QueueInboundElementsContainer, QueueInboundSize),
|
||||
}
|
||||
q.wg.Add(1)
|
||||
go func() {
|
||||
@@ -72,7 +72,7 @@ func newHandshakeQueue() *handshakeQueue {
|
||||
}
|
||||
|
||||
type autodrainingInboundQueue struct {
|
||||
c chan *[]*QueueInboundElement
|
||||
c chan *QueueInboundElementsContainer
|
||||
}
|
||||
|
||||
// newAutodrainingInboundQueue returns a channel that will be drained when it gets GC'd.
|
||||
@@ -81,7 +81,7 @@ type autodrainingInboundQueue struct {
|
||||
// some other means, such as sending a sentinel nil values.
|
||||
func newAutodrainingInboundQueue(device *Device) *autodrainingInboundQueue {
|
||||
q := &autodrainingInboundQueue{
|
||||
c: make(chan *[]*QueueInboundElement, QueueInboundSize),
|
||||
c: make(chan *QueueInboundElementsContainer, QueueInboundSize),
|
||||
}
|
||||
runtime.SetFinalizer(q, device.flushInboundQueue)
|
||||
return q
|
||||
@@ -90,13 +90,13 @@ func newAutodrainingInboundQueue(device *Device) *autodrainingInboundQueue {
|
||||
func (device *Device) flushInboundQueue(q *autodrainingInboundQueue) {
|
||||
for {
|
||||
select {
|
||||
case elems := <-q.c:
|
||||
for _, elem := range *elems {
|
||||
elem.Lock()
|
||||
case elemsContainer := <-q.c:
|
||||
elemsContainer.Lock()
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutInboundElement(elem)
|
||||
}
|
||||
device.PutInboundElementsSlice(elems)
|
||||
device.PutInboundElementsContainer(elemsContainer)
|
||||
default:
|
||||
return
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func (device *Device) flushInboundQueue(q *autodrainingInboundQueue) {
|
||||
}
|
||||
|
||||
type autodrainingOutboundQueue struct {
|
||||
c chan *[]*QueueOutboundElement
|
||||
c chan *QueueOutboundElementsContainer
|
||||
}
|
||||
|
||||
// newAutodrainingOutboundQueue returns a channel that will be drained when it gets GC'd.
|
||||
@@ -114,7 +114,7 @@ type autodrainingOutboundQueue struct {
|
||||
// All sends to the channel must be best-effort, because there may be no receivers.
|
||||
func newAutodrainingOutboundQueue(device *Device) *autodrainingOutboundQueue {
|
||||
q := &autodrainingOutboundQueue{
|
||||
c: make(chan *[]*QueueOutboundElement, QueueOutboundSize),
|
||||
c: make(chan *QueueOutboundElementsContainer, QueueOutboundSize),
|
||||
}
|
||||
runtime.SetFinalizer(q, device.flushOutboundQueue)
|
||||
return q
|
||||
@@ -123,13 +123,13 @@ func newAutodrainingOutboundQueue(device *Device) *autodrainingOutboundQueue {
|
||||
func (device *Device) flushOutboundQueue(q *autodrainingOutboundQueue) {
|
||||
for {
|
||||
select {
|
||||
case elems := <-q.c:
|
||||
for _, elem := range *elems {
|
||||
elem.Lock()
|
||||
case elemsContainer := <-q.c:
|
||||
elemsContainer.Lock()
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
device.PutOutboundElementsSlice(elems)
|
||||
device.PutOutboundElementsContainer(elemsContainer)
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -118,6 +118,7 @@ func (st *CookieChecker) CreateReply(
|
||||
msg []byte,
|
||||
recv uint32,
|
||||
src []byte,
|
||||
msgType uint32,
|
||||
) (*MessageCookieReply, error) {
|
||||
st.RLock()
|
||||
|
||||
@@ -153,7 +154,7 @@ func (st *CookieChecker) CreateReply(
|
||||
smac1 := smac2 - blake2s.Size128
|
||||
|
||||
reply := new(MessageCookieReply)
|
||||
reply.Type = MessageCookieReplyType
|
||||
reply.Type = msgType
|
||||
reply.Receiver = recv
|
||||
|
||||
_, err := rand.Read(reply.Nonce[:])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -99,7 +99,7 @@ func TestCookieMAC1(t *testing.T) {
|
||||
0x8c, 0xe1, 0xe8, 0xfa, 0x67, 0x20, 0x80, 0x6d,
|
||||
}
|
||||
generator.AddMacs(msg)
|
||||
reply, err := checker.CreateReply(msg, 1377, src)
|
||||
reply, err := checker.CreateReply(msg, 1377, src, MessageCookieReplyType)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create cookie reply:", err)
|
||||
}
|
||||
|
||||
+50
-23
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/ratelimiter"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/ratelimiter"
|
||||
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
@@ -68,11 +68,11 @@ type Device struct {
|
||||
cookieChecker CookieChecker
|
||||
|
||||
pool struct {
|
||||
outboundElementsSlice *WaitPool
|
||||
inboundElementsSlice *WaitPool
|
||||
messageBuffers *WaitPool
|
||||
inboundElements *WaitPool
|
||||
outboundElements *WaitPool
|
||||
inboundElementsContainer *WaitPool
|
||||
outboundElementsContainer *WaitPool
|
||||
messageBuffers *WaitPool
|
||||
inboundElements *WaitPool
|
||||
outboundElements *WaitPool
|
||||
}
|
||||
|
||||
queue struct {
|
||||
@@ -89,6 +89,35 @@ type Device struct {
|
||||
ipcMutex sync.RWMutex
|
||||
closed chan struct{}
|
||||
log *Logger
|
||||
|
||||
junk struct {
|
||||
min int
|
||||
max int
|
||||
count int
|
||||
}
|
||||
|
||||
headers struct {
|
||||
init *magicHeader
|
||||
cookie *magicHeader
|
||||
response *magicHeader
|
||||
transport *magicHeader
|
||||
}
|
||||
|
||||
paddings struct {
|
||||
init int
|
||||
response int
|
||||
cookie int
|
||||
transport int
|
||||
}
|
||||
|
||||
ipackets [5]*obfChain
|
||||
|
||||
dpackets struct {
|
||||
init *obfChain
|
||||
cookie *obfChain
|
||||
response *obfChain
|
||||
transport *obfChain
|
||||
}
|
||||
}
|
||||
|
||||
// deviceState represents the state of a Device.
|
||||
@@ -162,7 +191,8 @@ func (device *Device) changeState(want deviceState) (err error) {
|
||||
err = errDown
|
||||
}
|
||||
}
|
||||
device.log.Verbosef("Interface state was %s, requested %s, now %s", old, want, device.deviceState())
|
||||
device.log.Verbosef(
|
||||
"Interface state was %s, requested %s, now %s", old, want, device.deviceState())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -298,6 +328,11 @@ func NewDevice(tunDevice tun.Device, bind conn.Bind, logger *Logger) *Device {
|
||||
device.rate.limiter.Init()
|
||||
device.indexTable.Init()
|
||||
|
||||
device.headers.init = &magicHeader{start: MessageInitiationType, end: MessageInitiationType}
|
||||
device.headers.response = &magicHeader{start: MessageResponseType, end: MessageResponseType}
|
||||
device.headers.cookie = &magicHeader{start: MessageCookieReplyType, end: MessageCookieReplyType}
|
||||
device.headers.transport = &magicHeader{start: MessageTransportType, end: MessageTransportType}
|
||||
|
||||
device.PopulatePools()
|
||||
|
||||
// create queues
|
||||
@@ -368,10 +403,10 @@ func (device *Device) RemoveAllPeers() {
|
||||
}
|
||||
|
||||
func (device *Device) Close() {
|
||||
device.ipcMutex.Lock()
|
||||
defer device.ipcMutex.Unlock()
|
||||
device.state.Lock()
|
||||
defer device.state.Unlock()
|
||||
device.ipcMutex.Lock()
|
||||
defer device.ipcMutex.Unlock()
|
||||
if device.isClosed() {
|
||||
return
|
||||
}
|
||||
@@ -461,11 +496,7 @@ func (device *Device) BindSetMark(mark uint32) error {
|
||||
// clear cached source addresses
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
peer.markEndpointSrcForClearing()
|
||||
}
|
||||
device.peers.RUnlock()
|
||||
|
||||
@@ -515,11 +546,7 @@ func (device *Device) BindUpdate() error {
|
||||
// clear cached source addresses
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
peer.markEndpointSrcForClearing()
|
||||
}
|
||||
device.peers.RUnlock()
|
||||
|
||||
|
||||
+151
-25
@@ -1,29 +1,32 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/conn/bindtest"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"golang.zx2c4.com/wireguard/tun/tuntest"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn/bindtest"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/tuntest"
|
||||
)
|
||||
|
||||
// uapiCfg returns a string that contains cfg formatted use with IpcSet.
|
||||
@@ -50,7 +53,7 @@ func uapiCfg(cfg ...string) string {
|
||||
|
||||
// genConfigs generates a pair of configs that connect to each other.
|
||||
// The configs use distinct, probably-usable ports.
|
||||
func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
func genConfigs(tb testing.TB, cfg ...string) (cfgs, endpointCfgs [2]string) {
|
||||
var key1, key2 NoisePrivateKey
|
||||
_, err := rand.Read(key1[:])
|
||||
if err != nil {
|
||||
@@ -62,7 +65,8 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
}
|
||||
pub1, pub2 := key1.publicKey(), key2.publicKey()
|
||||
|
||||
cfgs[0] = uapiCfg(
|
||||
args0 := append([]string(nil), cfg...)
|
||||
args0 = append(args0, []string{
|
||||
"private_key", hex.EncodeToString(key1[:]),
|
||||
"listen_port", "0",
|
||||
"replace_peers", "true",
|
||||
@@ -70,12 +74,16 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
"protocol_version", "1",
|
||||
"replace_allowed_ips", "true",
|
||||
"allowed_ip", "1.0.0.2/32",
|
||||
)
|
||||
}...)
|
||||
cfgs[0] = uapiCfg(args0...)
|
||||
|
||||
endpointCfgs[0] = uapiCfg(
|
||||
"public_key", hex.EncodeToString(pub2[:]),
|
||||
"endpoint", "127.0.0.1:%d",
|
||||
)
|
||||
cfgs[1] = uapiCfg(
|
||||
|
||||
args1 := append([]string(nil), cfg...)
|
||||
args1 = append(args1, []string{
|
||||
"private_key", hex.EncodeToString(key2[:]),
|
||||
"listen_port", "0",
|
||||
"replace_peers", "true",
|
||||
@@ -83,7 +91,9 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
"protocol_version", "1",
|
||||
"replace_allowed_ips", "true",
|
||||
"allowed_ip", "1.0.0.1/32",
|
||||
)
|
||||
}...)
|
||||
|
||||
cfgs[1] = uapiCfg(args1...)
|
||||
endpointCfgs[1] = uapiCfg(
|
||||
"public_key", hex.EncodeToString(pub1[:]),
|
||||
"endpoint", "127.0.0.1:%d",
|
||||
@@ -115,16 +125,21 @@ func (d SendDirection) String() string {
|
||||
return "pong"
|
||||
}
|
||||
|
||||
func (pair *testPair) Send(tb testing.TB, ping SendDirection, done chan struct{}) {
|
||||
func (pair *testPair) Send(
|
||||
tb testing.TB,
|
||||
ping SendDirection,
|
||||
done chan struct{},
|
||||
) {
|
||||
tb.Helper()
|
||||
p0, p1 := pair[0], pair[1]
|
||||
if !ping {
|
||||
// pong is the new ping
|
||||
p0, p1 = p1, p0
|
||||
}
|
||||
|
||||
msg := tuntest.Ping(p0.ip, p1.ip)
|
||||
p1.tun.Outbound <- msg
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
timer := time.NewTimer(6 * time.Second)
|
||||
defer timer.Stop()
|
||||
var err error
|
||||
select {
|
||||
@@ -149,8 +164,14 @@ func (pair *testPair) Send(tb testing.TB, ping SendDirection, done chan struct{}
|
||||
}
|
||||
|
||||
// genTestPair creates a testPair.
|
||||
func genTestPair(tb testing.TB, realSocket bool) (pair testPair) {
|
||||
cfg, endpointCfg := genConfigs(tb)
|
||||
func genTestPair(
|
||||
tb testing.TB,
|
||||
realSocket bool,
|
||||
extraCfg ...string,
|
||||
) (pair testPair) {
|
||||
var cfg, endpointCfg [2]string
|
||||
cfg, endpointCfg = genConfigs(tb, extraCfg...)
|
||||
|
||||
var binds [2]conn.Bind
|
||||
if realSocket {
|
||||
binds[0], binds[1] = conn.NewDefaultBind(), conn.NewDefaultBind()
|
||||
@@ -203,6 +224,97 @@ func TestTwoDevicePing(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// Run test with -race=false to avoid the race for setting the default msgTypes 2 times
|
||||
func TestAWGDevicePing(t *testing.T) {
|
||||
goroutineLeakCheck(t)
|
||||
pair := genTestPair(t, true,
|
||||
"jc", "5",
|
||||
"jmin", "500",
|
||||
"jmax", "1000",
|
||||
"s1", "15",
|
||||
"s2", "18",
|
||||
"s3", "20",
|
||||
"s4", "25",
|
||||
"h1", "123456-123500",
|
||||
"h2", "67543-67550",
|
||||
"h3", "123123-123200",
|
||||
"h4", "32345-32350",
|
||||
)
|
||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||
pair.Send(t, Ping, nil)
|
||||
})
|
||||
t.Run("ping 1.0.0.2", func(t *testing.T) {
|
||||
pair.Send(t, Pong, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// Needs to be stopped with Ctrl-C
|
||||
func TestAWGHandshakeDevicePing(t *testing.T) {
|
||||
t.Skip("This test is intended to be run manually, not as part of the test suite.")
|
||||
|
||||
signalContext, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer cancel()
|
||||
isRunning := atomic.NewBool(true)
|
||||
go func() {
|
||||
<-signalContext.Done()
|
||||
fmt.Println("Waiting to finish")
|
||||
isRunning.Store(false)
|
||||
}()
|
||||
|
||||
goroutineLeakCheck(t)
|
||||
pair := genTestPair(t, true,
|
||||
"i1", "<b 0xf6ab3267fa><c><b 0xf6ab><t><r 10>",
|
||||
"i2", "<b 0xf6ab3267fa><c><b 0xf6ab><t><rc 10>",
|
||||
"i3", "<b 0xf6ab3267fa><c><b 0xf6ab><t><rd 10>",
|
||||
"i4", "<b 0xf6ab3267fa><r 100>",
|
||||
// "jc", "1",
|
||||
// "jmin", "500",
|
||||
// "jmax", "1000",
|
||||
// "s1", "30",
|
||||
// "s2", "40",
|
||||
// "h1", "123456",
|
||||
// "h2", "67543",
|
||||
// "h4", "32345",
|
||||
// "h3", "123123",
|
||||
)
|
||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||
for isRunning.Load() {
|
||||
pair.Send(t, Ping, nil)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
})
|
||||
t.Run("ping 1.0.0.2", func(t *testing.T) {
|
||||
for isRunning.Load() {
|
||||
pair.Send(t, Pong, nil)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWGObfuscatedDevicePing(t *testing.T) {
|
||||
goroutineLeakCheck(t)
|
||||
pair := genTestPair(t, true,
|
||||
"s1", "10",
|
||||
"s2", "10",
|
||||
"s3", "10",
|
||||
"s4", "10",
|
||||
"h1", "12-34",
|
||||
"h2", "56-78",
|
||||
"h3", "9998-12345",
|
||||
"h4", "123123-123456",
|
||||
"di", "<b 0x123401000001000000000001056d757369630679616e64657802727500001c0001000001000112345678><dz 2><d>",
|
||||
"dr", "<b 0x1234><d><t><r 10><rc 10><rd 10><dz 8>",
|
||||
"dc", "<b 0x1234><d>",
|
||||
"dt", "<b 0x2345><ds>",
|
||||
)
|
||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||
pair.Send(t, Ping, nil)
|
||||
})
|
||||
t.Run("ping 1.0.0.2", func(t *testing.T) {
|
||||
pair.Send(t, Pong, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpDown(t *testing.T) {
|
||||
goroutineLeakCheck(t)
|
||||
const itrials = 50
|
||||
@@ -423,29 +535,43 @@ type fakeBindSized struct {
|
||||
size int
|
||||
}
|
||||
|
||||
func (b *fakeBindSized) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
|
||||
func (b *fakeBindSized) Open(
|
||||
port uint16,
|
||||
) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (b *fakeBindSized) Close() error { return nil }
|
||||
func (b *fakeBindSized) SetMark(mark uint32) error { return nil }
|
||||
func (b *fakeBindSized) Send(bufs [][]byte, ep conn.Endpoint) error { return nil }
|
||||
|
||||
func (b *fakeBindSized) Close() error { return nil }
|
||||
|
||||
func (b *fakeBindSized) SetMark(mark uint32) error { return nil }
|
||||
|
||||
func (b *fakeBindSized) Send(bufs [][]byte, ep conn.Endpoint) error { return nil }
|
||||
|
||||
func (b *fakeBindSized) ParseEndpoint(s string) (conn.Endpoint, error) { return nil, nil }
|
||||
func (b *fakeBindSized) BatchSize() int { return b.size }
|
||||
|
||||
func (b *fakeBindSized) BatchSize() int { return b.size }
|
||||
|
||||
type fakeTUNDeviceSized struct {
|
||||
size int
|
||||
}
|
||||
|
||||
func (t *fakeTUNDeviceSized) File() *os.File { return nil }
|
||||
|
||||
func (t *fakeTUNDeviceSized) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (t *fakeTUNDeviceSized) Write(bufs [][]byte, offset int) (int, error) { return 0, nil }
|
||||
func (t *fakeTUNDeviceSized) MTU() (int, error) { return 0, nil }
|
||||
func (t *fakeTUNDeviceSized) Name() (string, error) { return "", nil }
|
||||
func (t *fakeTUNDeviceSized) Events() <-chan tun.Event { return nil }
|
||||
func (t *fakeTUNDeviceSized) Close() error { return nil }
|
||||
func (t *fakeTUNDeviceSized) BatchSize() int { return t.size }
|
||||
|
||||
func (t *fakeTUNDeviceSized) MTU() (int, error) { return 0, nil }
|
||||
|
||||
func (t *fakeTUNDeviceSized) Name() (string, error) { return "", nil }
|
||||
|
||||
func (t *fakeTUNDeviceSized) Events() <-chan tun.Event { return nil }
|
||||
|
||||
func (t *fakeTUNDeviceSized) Close() error { return nil }
|
||||
|
||||
func (t *fakeTUNDeviceSized) BatchSize() int { return t.size }
|
||||
|
||||
func TestBatchSize(t *testing.T) {
|
||||
d := Device{}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/wireguard/replay"
|
||||
"github.com/amnezia-vpn/amneziawg-go/replay"
|
||||
)
|
||||
|
||||
/* Due to limitations in Go and /x/crypto there is currently
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type magicHeader struct {
|
||||
start uint32
|
||||
end uint32
|
||||
}
|
||||
|
||||
func newMagicHeader(spec string) (*magicHeader, error) {
|
||||
parts := strings.Split(spec, "-")
|
||||
if len(parts) < 1 || len(parts) > 2 {
|
||||
return nil, errors.New("bad format")
|
||||
}
|
||||
|
||||
start, err := strconv.ParseUint(parts[0], 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse %s: %w", parts[0], err)
|
||||
}
|
||||
|
||||
var end uint64
|
||||
if len(parts) > 1 {
|
||||
end, err = strconv.ParseUint(parts[1], 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse %s: %w", parts[1], err)
|
||||
}
|
||||
} else {
|
||||
end = start
|
||||
}
|
||||
|
||||
if end < start {
|
||||
return nil, errors.New("wrong range specified")
|
||||
}
|
||||
|
||||
return &magicHeader{
|
||||
start: uint32(start),
|
||||
end: uint32(end),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *magicHeader) GenSpec() string {
|
||||
if h.start == h.end {
|
||||
return fmt.Sprintf("%d", h.start)
|
||||
}
|
||||
return fmt.Sprintf("%d-%d", h.start, h.end)
|
||||
}
|
||||
|
||||
func (h *magicHeader) Validate(val uint32) bool {
|
||||
return h.start <= val && val <= h.end
|
||||
}
|
||||
|
||||
func (h *magicHeader) Generate() uint32 {
|
||||
high := int64(h.end - h.start + 1)
|
||||
r, _ := rand.Int(rand.Reader, big.NewInt(high))
|
||||
return h.start + uint32(r.Int64())
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -11,9 +11,9 @@ func (device *Device) DisableSomeRoamingForBrokenMobileSemantics() {
|
||||
device.net.brokenRoaming = true
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Lock()
|
||||
peer.disableRoaming = peer.endpoint != nil
|
||||
peer.Unlock()
|
||||
peer.endpoint.Lock()
|
||||
peer.endpoint.disableRoaming = peer.endpoint.val != nil
|
||||
peer.endpoint.Unlock()
|
||||
}
|
||||
device.peers.RUnlock()
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/poly1305"
|
||||
|
||||
"golang.zx2c4.com/wireguard/tai64n"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tai64n"
|
||||
)
|
||||
|
||||
type handshakeState int
|
||||
@@ -53,10 +53,11 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
MessageInitiationType = 1
|
||||
MessageResponseType = 2
|
||||
MessageCookieReplyType = 3
|
||||
MessageTransportType = 4
|
||||
MessageUnknownType uint32 = 0
|
||||
MessageInitiationType uint32 = 1
|
||||
MessageResponseType uint32 = 2
|
||||
MessageCookieReplyType uint32 = 3
|
||||
MessageTransportType uint32 = 4
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -193,8 +194,10 @@ func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, e
|
||||
|
||||
handshake.mixHash(handshake.remoteStatic[:])
|
||||
|
||||
msgType := device.headers.init.Generate()
|
||||
|
||||
msg := MessageInitiation{
|
||||
Type: MessageInitiationType,
|
||||
Type: msgType,
|
||||
Ephemeral: handshake.localEphemeral.publicKey(),
|
||||
}
|
||||
|
||||
@@ -367,7 +370,7 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error
|
||||
}
|
||||
|
||||
var msg MessageResponse
|
||||
msg.Type = MessageResponseType
|
||||
msg.Type = device.headers.response.Generate()
|
||||
msg.Sender = handshake.localIndex
|
||||
msg.Receiver = handshake.remoteIndex
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/tun/tuntest"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/tuntest"
|
||||
)
|
||||
|
||||
func TestCurveWrappers(t *testing.T) {
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type obfBuilder func(val string) (obf, error)
|
||||
|
||||
var obfBuilders = map[string]obfBuilder{
|
||||
"b": newBytesObf,
|
||||
"t": newTimestampObf,
|
||||
"r": newRandObf,
|
||||
"rc": newRandCharObf,
|
||||
"rd": newRandDigitsObf,
|
||||
"d": newDataObf,
|
||||
"ds": newDataStringObf,
|
||||
"dz": newDataSizeObf,
|
||||
}
|
||||
|
||||
type obf interface {
|
||||
Obfuscate(dst, src []byte)
|
||||
Deobfuscate(dst, src []byte) bool
|
||||
ObfuscatedLen(srcLen int) int
|
||||
DeobfuscatedLen(srcLen int) int
|
||||
}
|
||||
|
||||
type obfChain struct {
|
||||
Spec string
|
||||
obfs []obf
|
||||
}
|
||||
|
||||
func newObfChain(spec string) (*obfChain, error) {
|
||||
var (
|
||||
obfs []obf
|
||||
errs []error
|
||||
)
|
||||
|
||||
remaining := spec[:]
|
||||
for {
|
||||
start := strings.IndexByte(remaining, '<')
|
||||
if start == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
end := strings.IndexByte(remaining[start:], '>')
|
||||
if end == -1 {
|
||||
return nil, errors.New("missing enclosing >")
|
||||
}
|
||||
end += start
|
||||
|
||||
tag := remaining[start+1 : end]
|
||||
parts := strings.Fields(tag)
|
||||
if len(parts) == 0 {
|
||||
errs = append(errs, errors.New("empty tag"))
|
||||
remaining = remaining[end+1:]
|
||||
continue
|
||||
}
|
||||
|
||||
key := parts[0]
|
||||
builder, ok := obfBuilders[key]
|
||||
if !ok {
|
||||
errs = append(errs, fmt.Errorf("unknown tag <%s>", key))
|
||||
remaining = remaining[end+1:]
|
||||
continue
|
||||
}
|
||||
|
||||
val := ""
|
||||
if len(parts) > 1 {
|
||||
val = parts[1]
|
||||
}
|
||||
|
||||
o, err := builder(val)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to build <%s>: %w", key, err))
|
||||
remaining = remaining[end+1:]
|
||||
continue
|
||||
}
|
||||
|
||||
obfs = append(obfs, o)
|
||||
remaining = remaining[end+1:]
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
return &obfChain{
|
||||
Spec: spec,
|
||||
obfs: obfs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *obfChain) Obfuscate(dst, src []byte) {
|
||||
written := 0
|
||||
for _, o := range c.obfs {
|
||||
obfLen := o.ObfuscatedLen(len(src))
|
||||
o.Obfuscate(dst[written:written+obfLen], src)
|
||||
written += obfLen
|
||||
}
|
||||
}
|
||||
|
||||
func (c *obfChain) Deobfuscate(dst, src []byte) bool {
|
||||
dynamicLen := len(src) - c.ObfuscatedLen(0)
|
||||
|
||||
written, read := 0, 0
|
||||
|
||||
for _, o := range c.obfs {
|
||||
deobfLen := o.DeobfuscatedLen(dynamicLen)
|
||||
obfLen := o.ObfuscatedLen(deobfLen)
|
||||
|
||||
if !o.Deobfuscate(dst[written:written+deobfLen], src[read:read+obfLen]) {
|
||||
return false
|
||||
}
|
||||
|
||||
written += deobfLen
|
||||
read += obfLen
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *obfChain) ObfuscatedLen(n int) int {
|
||||
total := 0
|
||||
for _, o := range c.obfs {
|
||||
total += o.ObfuscatedLen(n)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func (c *obfChain) DeobfuscatedLen(n int) int {
|
||||
dynamicLen := n - c.ObfuscatedLen(0)
|
||||
|
||||
total := 0
|
||||
for _, o := range c.obfs {
|
||||
total += o.DeobfuscatedLen(dynamicLen)
|
||||
}
|
||||
return total
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func newBytesObf(val string) (obf, error) {
|
||||
val = strings.TrimPrefix(val, "0x")
|
||||
|
||||
if len(val) == 0 {
|
||||
return nil, errors.New("empty argument")
|
||||
}
|
||||
|
||||
if len(val)%2 != 0 {
|
||||
return nil, errors.New("odd amount of symbols")
|
||||
}
|
||||
|
||||
bytes, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &bytesObf{data: bytes}, nil
|
||||
}
|
||||
|
||||
type bytesObf struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (o *bytesObf) Obfuscate(dst, src []byte) {
|
||||
copy(dst, o.data)
|
||||
}
|
||||
|
||||
func (o *bytesObf) Deobfuscate(dst, src []byte) bool {
|
||||
return bytes.Equal(o.data, src[:o.ObfuscatedLen(0)])
|
||||
}
|
||||
|
||||
func (o *bytesObf) ObfuscatedLen(srcLen int) int {
|
||||
return len(o.data)
|
||||
}
|
||||
|
||||
func (o *bytesObf) DeobfuscatedLen(srcLen int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package device
|
||||
|
||||
func newDataObf(val string) (obf, error) {
|
||||
return &dataObf{}, nil
|
||||
}
|
||||
|
||||
type dataObf struct {
|
||||
}
|
||||
|
||||
func (obf *dataObf) Obfuscate(dst, src []byte) {
|
||||
copy(dst, src)
|
||||
}
|
||||
|
||||
func (obf *dataObf) Deobfuscate(dst, src []byte) bool {
|
||||
copy(dst, src)
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *dataObf) ObfuscatedLen(n int) int {
|
||||
return n
|
||||
}
|
||||
|
||||
func (o *dataObf) DeobfuscatedLen(n int) int {
|
||||
return n
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package device
|
||||
|
||||
import "strconv"
|
||||
|
||||
func newDataSizeObf(val string) (obf, error) {
|
||||
length, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dataSizeObf{
|
||||
length: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type dataSizeObf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
func (o *dataSizeObf) Obfuscate(dst, src []byte) {
|
||||
srcLen := len(src)
|
||||
for i := o.length - 1; i >= 0; i-- {
|
||||
dst[i] = byte(srcLen & 0xFF)
|
||||
srcLen >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
func (o *dataSizeObf) Deobfuscate(dst, src []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *dataSizeObf) ObfuscatedLen(srcLen int) int {
|
||||
return o.length
|
||||
}
|
||||
|
||||
func (o *dataSizeObf) DeobfuscatedLen(srcLen int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
func newDataStringObf(val string) (obf, error) {
|
||||
return &dataStringObf{}, nil
|
||||
}
|
||||
|
||||
type dataStringObf struct {
|
||||
}
|
||||
|
||||
func (o *dataStringObf) Obfuscate(dst, src []byte) {
|
||||
base64.RawStdEncoding.Encode(dst, src)
|
||||
}
|
||||
|
||||
func (o *dataStringObf) Deobfuscate(dst, src []byte) bool {
|
||||
base64.RawStdEncoding.Decode(dst, src)
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *dataStringObf) ObfuscatedLen(n int) int {
|
||||
return base64.RawStdEncoding.EncodedLen(n)
|
||||
}
|
||||
|
||||
func (o *dataStringObf) DeobfuscatedLen(n int) int {
|
||||
return base64.RawStdEncoding.DecodedLen(n)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func newRandObf(val string) (obf, error) {
|
||||
length, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &randObf{
|
||||
length: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type randObf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
func (o *randObf) Obfuscate(dst, src []byte) {
|
||||
rand.Read(dst[:o.length])
|
||||
}
|
||||
|
||||
func (o *randObf) Deobfuscate(dst, src []byte) bool {
|
||||
// there is no way to validate randomness :)
|
||||
// assume that it is always true
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *randObf) ObfuscatedLen(n int) int {
|
||||
return o.length
|
||||
}
|
||||
|
||||
func (o *randObf) DeobfuscatedLen(n int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strconv"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const chars52 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
func newRandCharObf(val string) (obf, error) {
|
||||
length, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &randCharObf{
|
||||
length: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type randCharObf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
func (o *randCharObf) Obfuscate(dst, src []byte) {
|
||||
rand.Read(dst[:o.length])
|
||||
for i := range dst[:o.length] {
|
||||
dst[i] = chars52[dst[i]%52]
|
||||
}
|
||||
}
|
||||
|
||||
func (o *randCharObf) Deobfuscate(dst, src []byte) bool {
|
||||
for _, b := range src[:o.length] {
|
||||
if !unicode.IsLetter(rune(b)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *randCharObf) ObfuscatedLen(n int) int {
|
||||
return o.length
|
||||
}
|
||||
|
||||
func (o *randCharObf) DeobfuscatedLen(n int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strconv"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const digits10 = "0123456789"
|
||||
|
||||
func newRandDigitsObf(val string) (obf, error) {
|
||||
length, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &randDigitObf{
|
||||
length: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type randDigitObf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
func (o *randDigitObf) Obfuscate(dst, src []byte) {
|
||||
rand.Read(dst[:o.length])
|
||||
for i := range dst[:o.length] {
|
||||
dst[i] = digits10[dst[i]%10]
|
||||
}
|
||||
}
|
||||
|
||||
func (o *randDigitObf) Deobfuscate(dst, src []byte) bool {
|
||||
for _, b := range src[:o.length] {
|
||||
if !unicode.IsDigit(rune(b)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *randDigitObf) ObfuscatedLen(n int) int {
|
||||
return o.length
|
||||
}
|
||||
|
||||
func (o *randDigitObf) DeobfuscatedLen(n int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newTimestampObf(_ string) (obf, error) {
|
||||
return ×tampObf{}, nil
|
||||
}
|
||||
|
||||
type timestampObf struct{}
|
||||
|
||||
func (o *timestampObf) Obfuscate(dst, src []byte) {
|
||||
t := uint32(time.Now().Unix())
|
||||
binary.BigEndian.PutUint32(dst, t)
|
||||
}
|
||||
|
||||
func (o *timestampObf) Deobfuscate(dst, src []byte) bool {
|
||||
// replay attack check?
|
||||
// requires time to be always synchronized
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *timestampObf) ObfuscatedLen(n int) int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func (o *timestampObf) DeobfuscatedLen(n int) int {
|
||||
return 0
|
||||
}
|
||||
+41
-21
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -12,22 +12,25 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
)
|
||||
|
||||
type Peer struct {
|
||||
isRunning atomic.Bool
|
||||
sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
|
||||
keypairs Keypairs
|
||||
handshake Handshake
|
||||
device *Device
|
||||
endpoint conn.Endpoint
|
||||
stopping sync.WaitGroup // routines pending stop
|
||||
txBytes atomic.Uint64 // bytes send to peer (endpoint)
|
||||
rxBytes atomic.Uint64 // bytes received from peer
|
||||
lastHandshakeNano atomic.Int64 // nano seconds since epoch
|
||||
|
||||
disableRoaming bool
|
||||
endpoint struct {
|
||||
sync.Mutex
|
||||
val conn.Endpoint
|
||||
clearSrcOnTx bool // signal to val.ClearSrc() prior to next packet transmission
|
||||
disableRoaming bool
|
||||
}
|
||||
|
||||
timers struct {
|
||||
retransmitHandshake *Timer
|
||||
@@ -45,9 +48,9 @@ type Peer struct {
|
||||
}
|
||||
|
||||
queue struct {
|
||||
staged chan *[]*QueueOutboundElement // staged packets before a handshake is available
|
||||
outbound *autodrainingOutboundQueue // sequential ordering of udp transmission
|
||||
inbound *autodrainingInboundQueue // sequential ordering of tun writing
|
||||
staged chan *QueueOutboundElementsContainer // staged packets before a handshake is available
|
||||
outbound *autodrainingOutboundQueue // sequential ordering of udp transmission
|
||||
inbound *autodrainingInboundQueue // sequential ordering of tun writing
|
||||
}
|
||||
|
||||
cookieGenerator CookieGenerator
|
||||
@@ -74,14 +77,12 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
|
||||
// create peer
|
||||
peer := new(Peer)
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
|
||||
peer.cookieGenerator.Init(pk)
|
||||
peer.device = device
|
||||
peer.queue.outbound = newAutodrainingOutboundQueue(device)
|
||||
peer.queue.inbound = newAutodrainingInboundQueue(device)
|
||||
peer.queue.staged = make(chan *[]*QueueOutboundElement, QueueStagedSize)
|
||||
peer.queue.staged = make(chan *QueueOutboundElementsContainer, QueueStagedSize)
|
||||
|
||||
// map public key
|
||||
_, ok := device.peers.keyMap[pk]
|
||||
@@ -97,7 +98,11 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
handshake.mutex.Unlock()
|
||||
|
||||
// reset endpoint
|
||||
peer.endpoint = nil
|
||||
peer.endpoint.Lock()
|
||||
peer.endpoint.val = nil
|
||||
peer.endpoint.disableRoaming = false
|
||||
peer.endpoint.clearSrcOnTx = false
|
||||
peer.endpoint.Unlock()
|
||||
|
||||
// init timers
|
||||
peer.timersInit()
|
||||
@@ -116,14 +121,19 @@ func (peer *Peer) SendBuffers(buffers [][]byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
peer.RLock()
|
||||
defer peer.RUnlock()
|
||||
|
||||
if peer.endpoint == nil {
|
||||
peer.endpoint.Lock()
|
||||
endpoint := peer.endpoint.val
|
||||
if endpoint == nil {
|
||||
peer.endpoint.Unlock()
|
||||
return errors.New("no known endpoint for peer")
|
||||
}
|
||||
if peer.endpoint.clearSrcOnTx {
|
||||
endpoint.ClearSrc()
|
||||
peer.endpoint.clearSrcOnTx = false
|
||||
}
|
||||
peer.endpoint.Unlock()
|
||||
|
||||
err := peer.device.net.bind.Send(buffers, peer.endpoint)
|
||||
err := peer.device.net.bind.Send(buffers, endpoint)
|
||||
if err == nil {
|
||||
var totalLen uint64
|
||||
for _, b := range buffers {
|
||||
@@ -267,10 +277,20 @@ func (peer *Peer) Stop() {
|
||||
}
|
||||
|
||||
func (peer *Peer) SetEndpointFromPacket(endpoint conn.Endpoint) {
|
||||
if peer.disableRoaming {
|
||||
peer.endpoint.Lock()
|
||||
defer peer.endpoint.Unlock()
|
||||
if peer.endpoint.disableRoaming {
|
||||
return
|
||||
}
|
||||
peer.Lock()
|
||||
peer.endpoint = endpoint
|
||||
peer.Unlock()
|
||||
peer.endpoint.clearSrcOnTx = false
|
||||
peer.endpoint.val = endpoint
|
||||
}
|
||||
|
||||
func (peer *Peer) markEndpointSrcForClearing() {
|
||||
peer.endpoint.Lock()
|
||||
defer peer.endpoint.Unlock()
|
||||
if peer.endpoint.val == nil {
|
||||
return
|
||||
}
|
||||
peer.endpoint.clearSrcOnTx = true
|
||||
}
|
||||
+31
-26
@@ -1,20 +1,19 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type WaitPool struct {
|
||||
pool sync.Pool
|
||||
cond sync.Cond
|
||||
lock sync.Mutex
|
||||
count atomic.Uint32
|
||||
count uint32 // Get calls not yet Put back
|
||||
max uint32
|
||||
}
|
||||
|
||||
@@ -27,10 +26,10 @@ func NewWaitPool(max uint32, new func() any) *WaitPool {
|
||||
func (p *WaitPool) Get() any {
|
||||
if p.max != 0 {
|
||||
p.lock.Lock()
|
||||
for p.count.Load() >= p.max {
|
||||
for p.count >= p.max {
|
||||
p.cond.Wait()
|
||||
}
|
||||
p.count.Add(1)
|
||||
p.count++
|
||||
p.lock.Unlock()
|
||||
}
|
||||
return p.pool.Get()
|
||||
@@ -41,18 +40,20 @@ func (p *WaitPool) Put(x any) {
|
||||
if p.max == 0 {
|
||||
return
|
||||
}
|
||||
p.count.Add(^uint32(0))
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
p.count--
|
||||
p.cond.Signal()
|
||||
}
|
||||
|
||||
func (device *Device) PopulatePools() {
|
||||
device.pool.outboundElementsSlice = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
||||
s := make([]*QueueOutboundElement, 0, device.BatchSize())
|
||||
return &s
|
||||
})
|
||||
device.pool.inboundElementsSlice = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
||||
device.pool.inboundElementsContainer = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
||||
s := make([]*QueueInboundElement, 0, device.BatchSize())
|
||||
return &s
|
||||
return &QueueInboundElementsContainer{elems: s}
|
||||
})
|
||||
device.pool.outboundElementsContainer = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
||||
s := make([]*QueueOutboundElement, 0, device.BatchSize())
|
||||
return &QueueOutboundElementsContainer{elems: s}
|
||||
})
|
||||
device.pool.messageBuffers = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
||||
return new([MaxMessageSize]byte)
|
||||
@@ -65,28 +66,32 @@ func (device *Device) PopulatePools() {
|
||||
})
|
||||
}
|
||||
|
||||
func (device *Device) GetOutboundElementsSlice() *[]*QueueOutboundElement {
|
||||
return device.pool.outboundElementsSlice.Get().(*[]*QueueOutboundElement)
|
||||
func (device *Device) GetInboundElementsContainer() *QueueInboundElementsContainer {
|
||||
c := device.pool.inboundElementsContainer.Get().(*QueueInboundElementsContainer)
|
||||
c.Mutex = sync.Mutex{}
|
||||
return c
|
||||
}
|
||||
|
||||
func (device *Device) PutOutboundElementsSlice(s *[]*QueueOutboundElement) {
|
||||
for i := range *s {
|
||||
(*s)[i] = nil
|
||||
func (device *Device) PutInboundElementsContainer(c *QueueInboundElementsContainer) {
|
||||
for i := range c.elems {
|
||||
c.elems[i] = nil
|
||||
}
|
||||
*s = (*s)[:0]
|
||||
device.pool.outboundElementsSlice.Put(s)
|
||||
c.elems = c.elems[:0]
|
||||
device.pool.inboundElementsContainer.Put(c)
|
||||
}
|
||||
|
||||
func (device *Device) GetInboundElementsSlice() *[]*QueueInboundElement {
|
||||
return device.pool.inboundElementsSlice.Get().(*[]*QueueInboundElement)
|
||||
func (device *Device) GetOutboundElementsContainer() *QueueOutboundElementsContainer {
|
||||
c := device.pool.outboundElementsContainer.Get().(*QueueOutboundElementsContainer)
|
||||
c.Mutex = sync.Mutex{}
|
||||
return c
|
||||
}
|
||||
|
||||
func (device *Device) PutInboundElementsSlice(s *[]*QueueInboundElement) {
|
||||
for i := range *s {
|
||||
(*s)[i] = nil
|
||||
func (device *Device) PutOutboundElementsContainer(c *QueueOutboundElementsContainer) {
|
||||
for i := range c.elems {
|
||||
c.elems[i] = nil
|
||||
}
|
||||
*s = (*s)[:0]
|
||||
device.pool.inboundElementsSlice.Put(s)
|
||||
c.elems = c.elems[:0]
|
||||
device.pool.outboundElementsContainer.Put(c)
|
||||
}
|
||||
|
||||
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -32,7 +32,9 @@ func TestWaitPool(t *testing.T) {
|
||||
wg.Add(workers)
|
||||
var max atomic.Uint32
|
||||
updateMax := func() {
|
||||
count := p.count.Load()
|
||||
p.lock.Lock()
|
||||
count := p.count
|
||||
p.lock.Unlock()
|
||||
if count > p.max {
|
||||
t.Errorf("count (%d) > max (%d)", count, p.max)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import "golang.zx2c4.com/wireguard/conn"
|
||||
import "github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
|
||||
/* Reduce memory consumption for Android */
|
||||
|
||||
@@ -14,6 +14,6 @@ const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = 2200
|
||||
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
|
||||
PreallocatedBuffersPerPool = 4096
|
||||
)
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import "golang.zx2c4.com/wireguard/conn"
|
||||
import "github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
|
||||
const (
|
||||
QueueStagedSize = conn.IdealBatchSize
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+179
-58
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
)
|
||||
|
||||
type QueueHandshakeElement struct {
|
||||
@@ -27,7 +27,6 @@ type QueueHandshakeElement struct {
|
||||
}
|
||||
|
||||
type QueueInboundElement struct {
|
||||
sync.Mutex
|
||||
buffer *[MaxMessageSize]byte
|
||||
packet []byte
|
||||
counter uint64
|
||||
@@ -35,6 +34,11 @@ type QueueInboundElement struct {
|
||||
endpoint conn.Endpoint
|
||||
}
|
||||
|
||||
type QueueInboundElementsContainer struct {
|
||||
sync.Mutex
|
||||
elems []*QueueInboundElement
|
||||
}
|
||||
|
||||
// clearPointers clears elem fields that contain pointers.
|
||||
// This makes the garbage collector's life easier and
|
||||
// avoids accidentally keeping other objects around unnecessarily.
|
||||
@@ -66,7 +70,10 @@ func (peer *Peer) keepKeyFreshReceiving() {
|
||||
* Every time the bind is updated a new routine is started for
|
||||
* IPv4 and IPv6 (separately)
|
||||
*/
|
||||
func (device *Device) RoutineReceiveIncoming(maxBatchSize int, recv conn.ReceiveFunc) {
|
||||
func (device *Device) RoutineReceiveIncoming(
|
||||
maxBatchSize int,
|
||||
recv conn.ReceiveFunc,
|
||||
) {
|
||||
recvName := recv.PrettyName()
|
||||
defer func() {
|
||||
device.log.Verbosef("Routine: receive incoming %s - stopped", recvName)
|
||||
@@ -87,16 +94,16 @@ func (device *Device) RoutineReceiveIncoming(maxBatchSize int, recv conn.Receive
|
||||
count int
|
||||
endpoints = make([]conn.Endpoint, maxBatchSize)
|
||||
deathSpiral int
|
||||
elemsByPeer = make(map[*Peer]*[]*QueueInboundElement, maxBatchSize)
|
||||
elemsByPeer = make(map[*Peer]*QueueInboundElementsContainer, maxBatchSize)
|
||||
)
|
||||
|
||||
for i := range bufsArrs {
|
||||
for i := range maxBatchSize {
|
||||
bufsArrs[i] = device.GetMessageBuffer()
|
||||
bufs[i] = bufsArrs[i][:]
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for i := 0; i < maxBatchSize; i++ {
|
||||
for i := range maxBatchSize {
|
||||
if bufsArrs[i] != nil {
|
||||
device.PutMessageBuffer(bufsArrs[i])
|
||||
}
|
||||
@@ -129,9 +136,25 @@ func (device *Device) RoutineReceiveIncoming(maxBatchSize int, recv conn.Receive
|
||||
}
|
||||
|
||||
// check size of packet
|
||||
|
||||
packet := bufsArrs[i][:size]
|
||||
msgType := binary.LittleEndian.Uint32(packet[:4])
|
||||
|
||||
// decode D1-D4
|
||||
deobfBuf := device.GetMessageBuffer()
|
||||
n, _ := device.DeobfuscatePacket(deobfBuf[:], packet)
|
||||
if n > 0 {
|
||||
device.PutMessageBuffer(bufsArrs[i])
|
||||
bufsArrs[i] = deobfBuf
|
||||
packet = deobfBuf[:n]
|
||||
} else {
|
||||
device.PutMessageBuffer(deobfBuf)
|
||||
}
|
||||
|
||||
// get message padding and type based on information from S1-S4 and H1-H4
|
||||
msgType, padding := device.DeterminePacketTypeAndPadding(packet, MessageUnknownType)
|
||||
if padding > 0 {
|
||||
copy(packet, packet[padding:])
|
||||
packet = packet[:len(packet)-padding]
|
||||
}
|
||||
|
||||
switch msgType {
|
||||
|
||||
@@ -170,15 +193,14 @@ func (device *Device) RoutineReceiveIncoming(maxBatchSize int, recv conn.Receive
|
||||
elem.keypair = keypair
|
||||
elem.endpoint = endpoints[i]
|
||||
elem.counter = 0
|
||||
elem.Mutex = sync.Mutex{}
|
||||
elem.Lock()
|
||||
|
||||
elemsForPeer, ok := elemsByPeer[peer]
|
||||
if !ok {
|
||||
elemsForPeer = device.GetInboundElementsSlice()
|
||||
elemsForPeer = device.GetInboundElementsContainer()
|
||||
elemsForPeer.Lock()
|
||||
elemsByPeer[peer] = elemsForPeer
|
||||
}
|
||||
*elemsForPeer = append(*elemsForPeer, elem)
|
||||
elemsForPeer.elems = append(elemsForPeer.elems, elem)
|
||||
bufsArrs[i] = device.GetMessageBuffer()
|
||||
bufs[i] = bufsArrs[i][:]
|
||||
continue
|
||||
@@ -217,18 +239,16 @@ func (device *Device) RoutineReceiveIncoming(maxBatchSize int, recv conn.Receive
|
||||
default:
|
||||
}
|
||||
}
|
||||
for peer, elems := range elemsByPeer {
|
||||
for peer, elemsContainer := range elemsByPeer {
|
||||
if peer.isRunning.Load() {
|
||||
peer.queue.inbound.c <- elems
|
||||
for _, elem := range *elems {
|
||||
device.queue.decryption.c <- elem
|
||||
}
|
||||
peer.queue.inbound.c <- elemsContainer
|
||||
device.queue.decryption.c <- elemsContainer
|
||||
} else {
|
||||
for _, elem := range *elems {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutInboundElement(elem)
|
||||
}
|
||||
device.PutInboundElementsSlice(elems)
|
||||
device.PutInboundElementsContainer(elemsContainer)
|
||||
}
|
||||
delete(elemsByPeer, peer)
|
||||
}
|
||||
@@ -241,26 +261,28 @@ func (device *Device) RoutineDecryption(id int) {
|
||||
defer device.log.Verbosef("Routine: decryption worker %d - stopped", id)
|
||||
device.log.Verbosef("Routine: decryption worker %d - started", id)
|
||||
|
||||
for elem := range device.queue.decryption.c {
|
||||
// split message into fields
|
||||
counter := elem.packet[MessageTransportOffsetCounter:MessageTransportOffsetContent]
|
||||
content := elem.packet[MessageTransportOffsetContent:]
|
||||
for elemsContainer := range device.queue.decryption.c {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
// split message into fields
|
||||
counter := elem.packet[MessageTransportOffsetCounter:MessageTransportOffsetContent]
|
||||
content := elem.packet[MessageTransportOffsetContent:]
|
||||
|
||||
// decrypt and release to consumer
|
||||
var err error
|
||||
elem.counter = binary.LittleEndian.Uint64(counter)
|
||||
// copy counter to nonce
|
||||
binary.LittleEndian.PutUint64(nonce[0x4:0xc], elem.counter)
|
||||
elem.packet, err = elem.keypair.receive.Open(
|
||||
content[:0],
|
||||
nonce[:],
|
||||
content,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
elem.packet = nil
|
||||
// decrypt and release to consumer
|
||||
var err error
|
||||
elem.counter = binary.LittleEndian.Uint64(counter)
|
||||
// copy counter to nonce
|
||||
binary.LittleEndian.PutUint64(nonce[0x4:0xc], elem.counter)
|
||||
elem.packet, err = elem.keypair.receive.Open(
|
||||
content[:0],
|
||||
nonce[:],
|
||||
content,
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
elem.packet = nil
|
||||
}
|
||||
}
|
||||
elem.Unlock()
|
||||
elemsContainer.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +296,6 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
device.log.Verbosef("Routine: handshake worker %d - started", id)
|
||||
|
||||
for elem := range device.queue.handshake.c {
|
||||
|
||||
// handle cookie fields and ratelimiting
|
||||
|
||||
switch elem.msgType {
|
||||
@@ -302,9 +323,14 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
// consume reply
|
||||
|
||||
if peer := entry.peer; peer.isRunning.Load() {
|
||||
device.log.Verbosef("Receiving cookie response from %s", elem.endpoint.DstToString())
|
||||
device.log.Verbosef(
|
||||
"Receiving cookie response from %s",
|
||||
elem.endpoint.DstToString(),
|
||||
)
|
||||
if !peer.cookieGenerator.ConsumeReply(&reply) {
|
||||
device.log.Verbosef("Could not decrypt invalid cookie response")
|
||||
device.log.Verbosef(
|
||||
"Could not decrypt invalid cookie response",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,9 +372,7 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
|
||||
switch elem.msgType {
|
||||
case MessageInitiationType:
|
||||
|
||||
// unmarshal
|
||||
|
||||
var msg MessageInitiation
|
||||
reader := bytes.NewReader(elem.packet)
|
||||
err := binary.Read(reader, binary.LittleEndian, &msg)
|
||||
@@ -357,8 +381,10 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
goto skip
|
||||
}
|
||||
|
||||
// consume initiation
|
||||
// have to reassign msgType for ranged msgType to work
|
||||
msg.Type = elem.msgType
|
||||
|
||||
// consume initiation
|
||||
peer := device.ConsumeMessageInitiation(&msg)
|
||||
if peer == nil {
|
||||
device.log.Verbosef("Received invalid initiation message from %s", elem.endpoint.DstToString())
|
||||
@@ -390,6 +416,9 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
goto skip
|
||||
}
|
||||
|
||||
// have to reassign msgType for ranged msgType to work
|
||||
msg.Type = elem.msgType
|
||||
|
||||
// consume response
|
||||
|
||||
peer := device.ConsumeMessageResponse(&msg)
|
||||
@@ -437,12 +466,15 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||
|
||||
bufs := make([][]byte, 0, maxBatchSize)
|
||||
|
||||
for elems := range peer.queue.inbound.c {
|
||||
if elems == nil {
|
||||
for elemsContainer := range peer.queue.inbound.c {
|
||||
if elemsContainer == nil {
|
||||
return
|
||||
}
|
||||
for _, elem := range *elems {
|
||||
elem.Lock()
|
||||
elemsContainer.Lock()
|
||||
validTailPacket := -1
|
||||
dataPacketReceived := false
|
||||
rxBytesLen := uint64(0)
|
||||
for i, elem := range elemsContainer.elems {
|
||||
if elem.packet == nil {
|
||||
// decryption failed
|
||||
continue
|
||||
@@ -452,21 +484,19 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||
continue
|
||||
}
|
||||
|
||||
peer.SetEndpointFromPacket(elem.endpoint)
|
||||
validTailPacket = i
|
||||
if peer.ReceivedWithKeypair(elem.keypair) {
|
||||
peer.SetEndpointFromPacket(elem.endpoint)
|
||||
peer.timersHandshakeComplete()
|
||||
peer.SendStagedPackets()
|
||||
}
|
||||
peer.keepKeyFreshReceiving()
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketReceived()
|
||||
peer.rxBytes.Add(uint64(len(elem.packet) + MinMessageSize))
|
||||
rxBytesLen += uint64(len(elem.packet) + MinMessageSize)
|
||||
|
||||
if len(elem.packet) == 0 {
|
||||
device.log.Verbosef("%v - Receiving keepalive packet", peer)
|
||||
continue
|
||||
}
|
||||
peer.timersDataReceived()
|
||||
dataPacketReceived = true
|
||||
|
||||
switch elem.packet[0] >> 4 {
|
||||
case 4:
|
||||
@@ -503,11 +533,28 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||
}
|
||||
|
||||
default:
|
||||
device.log.Verbosef("Packet with invalid IP version from %v", peer)
|
||||
device.log.Verbosef(
|
||||
"Packet with invalid IP version from %v",
|
||||
peer,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
bufs = append(bufs, elem.buffer[:MessageTransportOffsetContent+len(elem.packet)])
|
||||
bufs = append(
|
||||
bufs,
|
||||
elem.buffer[:MessageTransportOffsetContent+len(elem.packet)],
|
||||
)
|
||||
}
|
||||
|
||||
peer.rxBytes.Add(rxBytesLen)
|
||||
if validTailPacket >= 0 {
|
||||
peer.SetEndpointFromPacket(elemsContainer.elems[validTailPacket].endpoint)
|
||||
peer.keepKeyFreshReceiving()
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketReceived()
|
||||
}
|
||||
if dataPacketReceived {
|
||||
peer.timersDataReceived()
|
||||
}
|
||||
if len(bufs) > 0 {
|
||||
_, err := device.tun.device.Write(bufs, MessageTransportOffsetContent)
|
||||
@@ -515,11 +562,85 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||
device.log.Errorf("Failed to write packets to TUN device: %v", err)
|
||||
}
|
||||
}
|
||||
for _, elem := range *elems {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutInboundElement(elem)
|
||||
}
|
||||
bufs = bufs[:0]
|
||||
device.PutInboundElementsSlice(elems)
|
||||
device.PutInboundElementsContainer(elemsContainer)
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) DeobfuscatePacket(dst, src []byte) (n int, expectedType uint32) {
|
||||
if chain := device.dpackets.init; chain != nil && chain.Deobfuscate(dst, src) {
|
||||
return chain.DeobfuscatedLen(len(src)), MessageInitiationType
|
||||
}
|
||||
|
||||
if chain := device.dpackets.response; chain != nil && chain.Deobfuscate(dst, src) {
|
||||
return chain.DeobfuscatedLen(len(src)), MessageResponseType
|
||||
}
|
||||
|
||||
if chain := device.dpackets.cookie; chain != nil && chain.Deobfuscate(dst, src) {
|
||||
return chain.DeobfuscatedLen(len(src)), MessageCookieReplyType
|
||||
}
|
||||
|
||||
if chain := device.dpackets.transport; chain != nil && chain.Deobfuscate(dst, src) {
|
||||
return chain.DeobfuscatedLen(len(src)), MessageTransportType
|
||||
}
|
||||
|
||||
return 0, MessageUnknownType
|
||||
}
|
||||
|
||||
func (device *Device) DeterminePacketTypeAndPadding(packet []byte, expectedType uint32) (uint32, int) {
|
||||
size := len(packet)
|
||||
|
||||
if expectedType == MessageUnknownType || expectedType == MessageInitiationType {
|
||||
padding := device.paddings.init
|
||||
header := device.headers.init
|
||||
|
||||
if size == padding+MessageInitiationSize {
|
||||
data := packet[padding:]
|
||||
if header.Validate(binary.LittleEndian.Uint32(data)) {
|
||||
return MessageInitiationType, padding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if expectedType == MessageUnknownType || expectedType == MessageResponseType {
|
||||
padding := device.paddings.response
|
||||
header := device.headers.response
|
||||
|
||||
if size == padding+MessageResponseSize {
|
||||
data := packet[padding:]
|
||||
if header.Validate(binary.LittleEndian.Uint32(data)) {
|
||||
return MessageResponseType, padding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if expectedType == MessageUnknownType || expectedType == MessageCookieReplyType {
|
||||
padding := device.paddings.cookie
|
||||
header := device.headers.cookie
|
||||
|
||||
if size == padding+MessageCookieReplySize {
|
||||
data := packet[padding:]
|
||||
if header.Validate(binary.LittleEndian.Uint32(data)) {
|
||||
return MessageCookieReplyType, padding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if expectedType == MessageUnknownType || expectedType == MessageTransportType {
|
||||
padding := device.paddings.transport
|
||||
header := device.headers.transport
|
||||
|
||||
if size >= padding+MessageTransportHeaderSize {
|
||||
data := packet[padding:]
|
||||
if header.Validate(binary.LittleEndian.Uint32(data)) {
|
||||
return MessageTransportType, padding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MessageUnknownType, 0
|
||||
}
|
||||
+180
-71
@@ -1,23 +1,26 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
/* Outbound flow
|
||||
@@ -45,7 +48,6 @@ import (
|
||||
*/
|
||||
|
||||
type QueueOutboundElement struct {
|
||||
sync.Mutex
|
||||
buffer *[MaxMessageSize]byte // slice holding the packet data
|
||||
packet []byte // slice of "buffer" (always!)
|
||||
nonce uint64 // nonce for encryption
|
||||
@@ -53,10 +55,14 @@ type QueueOutboundElement struct {
|
||||
peer *Peer // related peer
|
||||
}
|
||||
|
||||
type QueueOutboundElementsContainer struct {
|
||||
sync.Mutex
|
||||
elems []*QueueOutboundElement
|
||||
}
|
||||
|
||||
func (device *Device) NewOutboundElement() *QueueOutboundElement {
|
||||
elem := device.GetOutboundElement()
|
||||
elem.buffer = device.GetMessageBuffer()
|
||||
elem.Mutex = sync.Mutex{}
|
||||
elem.nonce = 0
|
||||
// keypair and peer were cleared (if necessary) by clearPointers.
|
||||
return elem
|
||||
@@ -78,15 +84,15 @@ func (elem *QueueOutboundElement) clearPointers() {
|
||||
func (peer *Peer) SendKeepalive() {
|
||||
if len(peer.queue.staged) == 0 && peer.isRunning.Load() {
|
||||
elem := peer.device.NewOutboundElement()
|
||||
elems := peer.device.GetOutboundElementsSlice()
|
||||
*elems = append(*elems, elem)
|
||||
elemsContainer := peer.device.GetOutboundElementsContainer()
|
||||
elemsContainer.elems = append(elemsContainer.elems, elem)
|
||||
select {
|
||||
case peer.queue.staged <- elems:
|
||||
case peer.queue.staged <- elemsContainer:
|
||||
peer.device.log.Verbosef("%v - Sending keepalive packet", peer)
|
||||
default:
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
peer.device.PutOutboundElementsSlice(elems)
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
}
|
||||
}
|
||||
peer.SendStagedPackets()
|
||||
@@ -120,6 +126,29 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var sendBuffer [][]byte
|
||||
|
||||
for _, ipacket := range peer.device.ipackets {
|
||||
if ipacket != nil {
|
||||
buf := make([]byte, ipacket.ObfuscatedLen(0))
|
||||
ipacket.Obfuscate(buf, nil)
|
||||
sendBuffer = append(sendBuffer, buf)
|
||||
}
|
||||
}
|
||||
|
||||
jc := peer.device.junk.count
|
||||
jmin := peer.device.junk.min
|
||||
jmax := peer.device.junk.max
|
||||
|
||||
for i := 0; i < jc; i++ {
|
||||
nBig, _ := rand.Int(rand.Reader, big.NewInt(int64(jmax-jmin+1)))
|
||||
n := int(nBig.Int64()) + jmin
|
||||
|
||||
buf := make([]byte, n)
|
||||
rand.Read(buf)
|
||||
sendBuffer = append(sendBuffer, buf)
|
||||
}
|
||||
|
||||
var buf [MessageInitiationSize]byte
|
||||
writer := bytes.NewBuffer(buf[:0])
|
||||
binary.Write(writer, binary.LittleEndian, msg)
|
||||
@@ -129,7 +158,22 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketSent()
|
||||
|
||||
err = peer.SendBuffers([][]byte{packet})
|
||||
if padding := peer.device.paddings.init; padding > 0 {
|
||||
buf := make([]byte, padding+len(packet))
|
||||
rand.Read(buf[:padding])
|
||||
copy(buf[padding:], packet)
|
||||
packet = buf
|
||||
}
|
||||
|
||||
if chain := peer.device.dpackets.init; chain != nil {
|
||||
buf := make([]byte, chain.ObfuscatedLen(len(packet)))
|
||||
chain.Obfuscate(buf, packet)
|
||||
packet = buf
|
||||
}
|
||||
|
||||
sendBuffer = append(sendBuffer, packet)
|
||||
|
||||
err = peer.SendBuffers(sendBuffer)
|
||||
if err != nil {
|
||||
peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err)
|
||||
}
|
||||
@@ -153,6 +197,7 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
|
||||
var buf [MessageResponseSize]byte
|
||||
writer := bytes.NewBuffer(buf[:0])
|
||||
|
||||
binary.Write(writer, binary.LittleEndian, response)
|
||||
packet := writer.Bytes()
|
||||
peer.cookieGenerator.AddMacs(packet)
|
||||
@@ -167,6 +212,19 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketSent()
|
||||
|
||||
if padding := peer.device.paddings.response; padding > 0 {
|
||||
buf := make([]byte, padding+len(packet))
|
||||
rand.Read(buf[:padding])
|
||||
copy(buf[padding:], packet)
|
||||
packet = buf
|
||||
}
|
||||
|
||||
if chain := peer.device.dpackets.response; chain != nil {
|
||||
buf := make([]byte, chain.ObfuscatedLen(len(packet)))
|
||||
chain.Obfuscate(buf, packet)
|
||||
packet = buf
|
||||
}
|
||||
|
||||
// TODO: allocation could be avoided
|
||||
err = peer.SendBuffers([][]byte{packet})
|
||||
if err != nil {
|
||||
@@ -179,7 +237,14 @@ func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement)
|
||||
device.log.Verbosef("Sending cookie response for denied handshake message for %v", initiatingElem.endpoint.DstToString())
|
||||
|
||||
sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8])
|
||||
reply, err := device.cookieChecker.CreateReply(initiatingElem.packet, sender, initiatingElem.endpoint.DstToBytes())
|
||||
msgType := device.headers.cookie.Generate()
|
||||
|
||||
reply, err := device.cookieChecker.CreateReply(
|
||||
initiatingElem.packet,
|
||||
sender,
|
||||
initiatingElem.endpoint.DstToBytes(),
|
||||
msgType,
|
||||
)
|
||||
if err != nil {
|
||||
device.log.Errorf("Failed to create cookie reply: %v", err)
|
||||
return err
|
||||
@@ -188,8 +253,22 @@ func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement)
|
||||
var buf [MessageCookieReplySize]byte
|
||||
writer := bytes.NewBuffer(buf[:0])
|
||||
binary.Write(writer, binary.LittleEndian, reply)
|
||||
packet := writer.Bytes()
|
||||
|
||||
if padding := device.paddings.cookie; padding > 0 {
|
||||
buf := make([]byte, padding+len(packet))
|
||||
rand.Read(buf[:padding])
|
||||
copy(buf[padding:], packet)
|
||||
packet = buf
|
||||
}
|
||||
|
||||
if chain := device.dpackets.cookie; chain != nil {
|
||||
buf := make([]byte, chain.ObfuscatedLen(len(packet)))
|
||||
chain.Obfuscate(buf, packet)
|
||||
packet = buf
|
||||
}
|
||||
// TODO: allocation could be avoided
|
||||
device.net.bind.Send([][]byte{writer.Bytes()}, initiatingElem.endpoint)
|
||||
device.net.bind.Send([][]byte{packet}, initiatingElem.endpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -218,7 +297,7 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
readErr error
|
||||
elems = make([]*QueueOutboundElement, batchSize)
|
||||
bufs = make([][]byte, batchSize)
|
||||
elemsByPeer = make(map[*Peer]*[]*QueueOutboundElement, batchSize)
|
||||
elemsByPeer = make(map[*Peer]*QueueOutboundElementsContainer, batchSize)
|
||||
count = 0
|
||||
sizes = make([]int, batchSize)
|
||||
offset = MessageTransportHeaderSize
|
||||
@@ -275,10 +354,10 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
}
|
||||
elemsForPeer, ok := elemsByPeer[peer]
|
||||
if !ok {
|
||||
elemsForPeer = device.GetOutboundElementsSlice()
|
||||
elemsForPeer = device.GetOutboundElementsContainer()
|
||||
elemsByPeer[peer] = elemsForPeer
|
||||
}
|
||||
*elemsForPeer = append(*elemsForPeer, elem)
|
||||
elemsForPeer.elems = append(elemsForPeer.elems, elem)
|
||||
elems[i] = device.NewOutboundElement()
|
||||
bufs[i] = elems[i].buffer[:]
|
||||
}
|
||||
@@ -288,11 +367,11 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
peer.StagePackets(elemsForPeer)
|
||||
peer.SendStagedPackets()
|
||||
} else {
|
||||
for _, elem := range *elemsForPeer {
|
||||
for _, elem := range elemsForPeer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
device.PutOutboundElementsSlice(elemsForPeer)
|
||||
device.PutOutboundElementsContainer(elemsForPeer)
|
||||
}
|
||||
delete(elemsByPeer, peer)
|
||||
}
|
||||
@@ -316,7 +395,7 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
}
|
||||
}
|
||||
|
||||
func (peer *Peer) StagePackets(elems *[]*QueueOutboundElement) {
|
||||
func (peer *Peer) StagePackets(elems *QueueOutboundElementsContainer) {
|
||||
for {
|
||||
select {
|
||||
case peer.queue.staged <- elems:
|
||||
@@ -325,11 +404,11 @@ func (peer *Peer) StagePackets(elems *[]*QueueOutboundElement) {
|
||||
}
|
||||
select {
|
||||
case tooOld := <-peer.queue.staged:
|
||||
for _, elem := range *tooOld {
|
||||
for _, elem := range tooOld.elems {
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
}
|
||||
peer.device.PutOutboundElementsSlice(tooOld)
|
||||
peer.device.PutOutboundElementsContainer(tooOld)
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -348,54 +427,52 @@ top:
|
||||
}
|
||||
|
||||
for {
|
||||
var elemsOOO *[]*QueueOutboundElement
|
||||
var elemsContainerOOO *QueueOutboundElementsContainer
|
||||
select {
|
||||
case elems := <-peer.queue.staged:
|
||||
case elemsContainer := <-peer.queue.staged:
|
||||
i := 0
|
||||
for _, elem := range *elems {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
elem.peer = peer
|
||||
elem.nonce = keypair.sendNonce.Add(1) - 1
|
||||
if elem.nonce >= RejectAfterMessages {
|
||||
keypair.sendNonce.Store(RejectAfterMessages)
|
||||
if elemsOOO == nil {
|
||||
elemsOOO = peer.device.GetOutboundElementsSlice()
|
||||
if elemsContainerOOO == nil {
|
||||
elemsContainerOOO = peer.device.GetOutboundElementsContainer()
|
||||
}
|
||||
*elemsOOO = append(*elemsOOO, elem)
|
||||
elemsContainerOOO.elems = append(elemsContainerOOO.elems, elem)
|
||||
continue
|
||||
} else {
|
||||
(*elems)[i] = elem
|
||||
elemsContainer.elems[i] = elem
|
||||
i++
|
||||
}
|
||||
|
||||
elem.keypair = keypair
|
||||
elem.Lock()
|
||||
}
|
||||
*elems = (*elems)[:i]
|
||||
elemsContainer.Lock()
|
||||
elemsContainer.elems = elemsContainer.elems[:i]
|
||||
|
||||
if elemsOOO != nil {
|
||||
peer.StagePackets(elemsOOO) // XXX: Out of order, but we can't front-load go chans
|
||||
if elemsContainerOOO != nil {
|
||||
peer.StagePackets(elemsContainerOOO) // XXX: Out of order, but we can't front-load go chans
|
||||
}
|
||||
|
||||
if len(*elems) == 0 {
|
||||
peer.device.PutOutboundElementsSlice(elems)
|
||||
if len(elemsContainer.elems) == 0 {
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
goto top
|
||||
}
|
||||
|
||||
// add to parallel and sequential queue
|
||||
if peer.isRunning.Load() {
|
||||
peer.queue.outbound.c <- elems
|
||||
for _, elem := range *elems {
|
||||
peer.device.queue.encryption.c <- elem
|
||||
}
|
||||
peer.queue.outbound.c <- elemsContainer
|
||||
peer.device.queue.encryption.c <- elemsContainer
|
||||
} else {
|
||||
for _, elem := range *elems {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
}
|
||||
peer.device.PutOutboundElementsSlice(elems)
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
}
|
||||
|
||||
if elemsOOO != nil {
|
||||
if elemsContainerOOO != nil {
|
||||
goto top
|
||||
}
|
||||
default:
|
||||
@@ -407,12 +484,12 @@ top:
|
||||
func (peer *Peer) FlushStagedPackets() {
|
||||
for {
|
||||
select {
|
||||
case elems := <-peer.queue.staged:
|
||||
for _, elem := range *elems {
|
||||
case elemsContainer := <-peer.queue.staged:
|
||||
for _, elem := range elemsContainer.elems {
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
}
|
||||
peer.device.PutOutboundElementsSlice(elems)
|
||||
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||
default:
|
||||
return
|
||||
}
|
||||
@@ -446,32 +523,36 @@ func (device *Device) RoutineEncryption(id int) {
|
||||
defer device.log.Verbosef("Routine: encryption worker %d - stopped", id)
|
||||
device.log.Verbosef("Routine: encryption worker %d - started", id)
|
||||
|
||||
for elem := range device.queue.encryption.c {
|
||||
// populate header fields
|
||||
header := elem.buffer[:MessageTransportHeaderSize]
|
||||
for elemsContainer := range device.queue.encryption.c {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
// populate header fields
|
||||
header := elem.buffer[:MessageTransportHeaderSize]
|
||||
|
||||
fieldType := header[0:4]
|
||||
fieldReceiver := header[4:8]
|
||||
fieldNonce := header[8:16]
|
||||
fieldType := header[0:4]
|
||||
fieldReceiver := header[4:8]
|
||||
fieldNonce := header[8:16]
|
||||
|
||||
binary.LittleEndian.PutUint32(fieldType, MessageTransportType)
|
||||
binary.LittleEndian.PutUint32(fieldReceiver, elem.keypair.remoteIndex)
|
||||
binary.LittleEndian.PutUint64(fieldNonce, elem.nonce)
|
||||
msgType := device.headers.transport.Generate()
|
||||
|
||||
// pad content to multiple of 16
|
||||
paddingSize := calculatePaddingSize(len(elem.packet), int(device.tun.mtu.Load()))
|
||||
elem.packet = append(elem.packet, paddingZeros[:paddingSize]...)
|
||||
binary.LittleEndian.PutUint32(fieldType, msgType)
|
||||
binary.LittleEndian.PutUint32(fieldReceiver, elem.keypair.remoteIndex)
|
||||
binary.LittleEndian.PutUint64(fieldNonce, elem.nonce)
|
||||
|
||||
// encrypt content and release to consumer
|
||||
// pad content to multiple of 16
|
||||
paddingSize := calculatePaddingSize(len(elem.packet), int(device.tun.mtu.Load()))
|
||||
elem.packet = append(elem.packet, paddingZeros[:paddingSize]...)
|
||||
|
||||
binary.LittleEndian.PutUint64(nonce[4:], elem.nonce)
|
||||
elem.packet = elem.keypair.send.Seal(
|
||||
header,
|
||||
nonce[:],
|
||||
elem.packet,
|
||||
nil,
|
||||
)
|
||||
elem.Unlock()
|
||||
// encrypt content and release to consumer
|
||||
|
||||
binary.LittleEndian.PutUint64(nonce[4:], elem.nonce)
|
||||
elem.packet = elem.keypair.send.Seal(
|
||||
header,
|
||||
nonce[:],
|
||||
elem.packet,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
elemsContainer.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,9 +566,9 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||
|
||||
bufs := make([][]byte, 0, maxBatchSize)
|
||||
|
||||
for elems := range peer.queue.outbound.c {
|
||||
for elemsContainer := range peer.queue.outbound.c {
|
||||
bufs = bufs[:0]
|
||||
if elems == nil {
|
||||
if elemsContainer == nil {
|
||||
return
|
||||
}
|
||||
if !peer.isRunning.Load() {
|
||||
@@ -497,18 +578,38 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||
// The timers and SendBuffers code are resilient to a few stragglers.
|
||||
// TODO: rework peer shutdown order to ensure
|
||||
// that we never accidentally keep timers alive longer than necessary.
|
||||
for _, elem := range *elems {
|
||||
elem.Lock()
|
||||
elemsContainer.Lock()
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
device.PutOutboundElementsContainer(elemsContainer)
|
||||
continue
|
||||
}
|
||||
dataSent := false
|
||||
for _, elem := range *elems {
|
||||
elem.Lock()
|
||||
elemsContainer.Lock()
|
||||
for _, elem := range elemsContainer.elems {
|
||||
if len(elem.packet) != MessageKeepaliveSize {
|
||||
dataSent = true
|
||||
|
||||
if padding := device.paddings.transport; padding > 0 {
|
||||
// elem.packet is stored at the start of elem.buffer
|
||||
// with zero padding
|
||||
for i := len(elem.packet) - 1; i >= 0; i-- {
|
||||
elem.buffer[i+padding] = elem.buffer[i]
|
||||
}
|
||||
rand.Read(elem.buffer[:padding])
|
||||
elem.packet = elem.buffer[:padding+len(elem.packet)]
|
||||
}
|
||||
|
||||
if chain := peer.device.dpackets.transport; chain != nil {
|
||||
buf := device.GetMessageBuffer()
|
||||
len := chain.ObfuscatedLen(len(elem.packet))
|
||||
chain.Obfuscate(buf[:], elem.packet)
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
elem.packet = buf[:len]
|
||||
elem.buffer = buf
|
||||
}
|
||||
}
|
||||
bufs = append(bufs, elem.packet)
|
||||
}
|
||||
@@ -520,11 +621,19 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||
if dataSent {
|
||||
peer.timersDataSent()
|
||||
}
|
||||
for _, elem := range *elems {
|
||||
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
device.PutOutboundElementsSlice(elems)
|
||||
device.PutOutboundElementsContainer(elemsContainer)
|
||||
if err != nil {
|
||||
var errGSO conn.ErrUDPGSODisabled
|
||||
if errors.As(err, &errGSO) {
|
||||
device.log.Verbosef(err.Error())
|
||||
err = errGSO.RetryErr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
device.log.Errorf("%v - Failed to send data packets: %v", peer, err)
|
||||
continue
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||
)
|
||||
|
||||
func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) {
|
||||
func (device *Device) startRouteListener(_ conn.Bind) (*rwcancel.RWCancel, error) {
|
||||
return nil, nil
|
||||
}
|
||||
+20
-20
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*
|
||||
* This implements userspace semantics of "sticky sockets", modeled after
|
||||
* WireGuard's kernelspace implementation. This is more or less a straight port
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
* Currently there is no way to achieve this within the net package:
|
||||
* See e.g. https://github.com/golang/go/issues/17930
|
||||
* So this code is remains platform dependent.
|
||||
* So this code remains platform dependent.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||
)
|
||||
|
||||
func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) {
|
||||
@@ -47,7 +47,7 @@ func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, er
|
||||
return netlinkCancel, nil
|
||||
}
|
||||
|
||||
func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netlinkCancel *rwcancel.RWCancel) {
|
||||
func (device *Device) routineRouteListener(_ conn.Bind, netlinkSock int, netlinkCancel *rwcancel.RWCancel) {
|
||||
type peerEndpointPtr struct {
|
||||
peer *Peer
|
||||
endpoint *conn.Endpoint
|
||||
@@ -110,17 +110,17 @@ func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netl
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
pePtr.peer.Lock()
|
||||
if &pePtr.peer.endpoint != pePtr.endpoint {
|
||||
pePtr.peer.Unlock()
|
||||
pePtr.peer.endpoint.Lock()
|
||||
if &pePtr.peer.endpoint.val != pePtr.endpoint {
|
||||
pePtr.peer.endpoint.Unlock()
|
||||
break
|
||||
}
|
||||
if uint32(pePtr.peer.endpoint.(*conn.StdNetEndpoint).SrcIfidx()) == ifidx {
|
||||
pePtr.peer.Unlock()
|
||||
if uint32(pePtr.peer.endpoint.val.(*conn.StdNetEndpoint).SrcIfidx()) == ifidx {
|
||||
pePtr.peer.endpoint.Unlock()
|
||||
break
|
||||
}
|
||||
pePtr.peer.endpoint.(*conn.StdNetEndpoint).ClearSrc()
|
||||
pePtr.peer.Unlock()
|
||||
pePtr.peer.endpoint.clearSrcOnTx = true
|
||||
pePtr.peer.endpoint.Unlock()
|
||||
}
|
||||
attr = attr[attrhdr.Len:]
|
||||
}
|
||||
@@ -134,18 +134,18 @@ func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netl
|
||||
device.peers.RLock()
|
||||
i := uint32(1)
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.RLock()
|
||||
if peer.endpoint == nil {
|
||||
peer.RUnlock()
|
||||
peer.endpoint.Lock()
|
||||
if peer.endpoint.val == nil {
|
||||
peer.endpoint.Unlock()
|
||||
continue
|
||||
}
|
||||
nativeEP, _ := peer.endpoint.(*conn.StdNetEndpoint)
|
||||
nativeEP, _ := peer.endpoint.val.(*conn.StdNetEndpoint)
|
||||
if nativeEP == nil {
|
||||
peer.RUnlock()
|
||||
peer.endpoint.Unlock()
|
||||
continue
|
||||
}
|
||||
if nativeEP.DstIP().Is6() || nativeEP.SrcIfidx() == 0 {
|
||||
peer.RUnlock()
|
||||
peer.endpoint.Unlock()
|
||||
break
|
||||
}
|
||||
nlmsg := struct {
|
||||
@@ -188,10 +188,10 @@ func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netl
|
||||
reqPeerLock.Lock()
|
||||
reqPeer[i] = peerEndpointPtr{
|
||||
peer: peer,
|
||||
endpoint: &peer.endpoint,
|
||||
endpoint: &peer.endpoint.val,
|
||||
}
|
||||
reqPeerLock.Unlock()
|
||||
peer.RUnlock()
|
||||
peer.endpoint.Unlock()
|
||||
i++
|
||||
_, err := netlinkCancel.Write((*[unsafe.Sizeof(nlmsg)]byte)(unsafe.Pointer(&nlmsg))[:])
|
||||
if err != nil {
|
||||
|
||||
+3
-11
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*
|
||||
* This is based heavily on timers.c from the kernel implementation.
|
||||
*/
|
||||
@@ -100,11 +100,7 @@ func expiredRetransmitHandshake(peer *Peer) {
|
||||
peer.device.log.Verbosef("%s - Handshake did not complete after %d seconds, retrying (try %d)", peer, int(RekeyTimeout.Seconds()), peer.timers.handshakeAttempts.Load()+1)
|
||||
|
||||
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||
peer.Lock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
peer.Unlock()
|
||||
peer.markEndpointSrcForClearing()
|
||||
|
||||
peer.SendHandshakeInitiation(true)
|
||||
}
|
||||
@@ -123,11 +119,7 @@ func expiredSendKeepalive(peer *Peer) {
|
||||
func expiredNewHandshake(peer *Peer) {
|
||||
peer.device.log.Verbosef("%s - Retrying handshake because we stopped hearing back after %d seconds", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
|
||||
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||
peer.Lock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
peer.Unlock()
|
||||
peer.markEndpointSrcForClearing()
|
||||
peer.SendHandshakeInitiation(false)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -8,7 +8,7 @@ package device
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
)
|
||||
|
||||
const DefaultMTU = 1420
|
||||
|
||||
+372
-40
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||
)
|
||||
|
||||
type IPCError struct {
|
||||
@@ -97,35 +97,99 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
|
||||
sendf("fwmark=%d", device.net.fwmark)
|
||||
}
|
||||
|
||||
if device.junk.count != 0 {
|
||||
sendf("jc=%d", device.junk.count)
|
||||
}
|
||||
|
||||
if device.junk.min != 0 {
|
||||
sendf("jmin=%d", device.junk.min)
|
||||
}
|
||||
|
||||
if device.junk.max != 0 {
|
||||
sendf("jmax=%d", device.junk.max)
|
||||
}
|
||||
|
||||
if device.paddings.init != 0 {
|
||||
sendf("s1=%d", device.paddings.init)
|
||||
}
|
||||
|
||||
if device.paddings.response != 0 {
|
||||
sendf("s2=%d", device.paddings.response)
|
||||
}
|
||||
|
||||
if device.paddings.cookie != 0 {
|
||||
sendf("s3=%d", device.paddings.cookie)
|
||||
}
|
||||
|
||||
if device.paddings.transport != 0 {
|
||||
sendf("s4=%d", device.paddings.transport)
|
||||
}
|
||||
|
||||
if device.headers.init != nil {
|
||||
sendf("h1=%s", device.headers.init.GenSpec())
|
||||
}
|
||||
|
||||
if device.headers.response != nil {
|
||||
sendf("h2=%s", device.headers.response.GenSpec())
|
||||
}
|
||||
|
||||
if device.headers.cookie != nil {
|
||||
sendf("h3=%s", device.headers.cookie.GenSpec())
|
||||
}
|
||||
|
||||
if device.headers.transport != nil {
|
||||
sendf("h4=%s", device.headers.transport.GenSpec())
|
||||
}
|
||||
|
||||
for i, ipacket := range device.ipackets {
|
||||
if ipacket != nil {
|
||||
sendf("i%d=%s", i+1, ipacket.Spec)
|
||||
}
|
||||
}
|
||||
|
||||
if device.dpackets.init != nil {
|
||||
sendf("di=%s", device.dpackets.init.Spec)
|
||||
}
|
||||
|
||||
if device.dpackets.response != nil {
|
||||
sendf("dr=%s", device.dpackets.response.Spec)
|
||||
}
|
||||
|
||||
if device.dpackets.cookie != nil {
|
||||
sendf("dc=%s", device.dpackets.cookie.Spec)
|
||||
}
|
||||
|
||||
if device.dpackets.transport != nil {
|
||||
sendf("dt=%s", device.dpackets.transport.Spec)
|
||||
}
|
||||
|
||||
for _, peer := range device.peers.keyMap {
|
||||
// Serialize peer state.
|
||||
// Do the work in an anonymous function so that we can use defer.
|
||||
func() {
|
||||
peer.RLock()
|
||||
defer peer.RUnlock()
|
||||
peer.handshake.mutex.RLock()
|
||||
keyf("public_key", (*[32]byte)(&peer.handshake.remoteStatic))
|
||||
keyf("preshared_key", (*[32]byte)(&peer.handshake.presharedKey))
|
||||
peer.handshake.mutex.RUnlock()
|
||||
sendf("protocol_version=1")
|
||||
peer.endpoint.Lock()
|
||||
if peer.endpoint.val != nil {
|
||||
sendf("endpoint=%s", peer.endpoint.val.DstToString())
|
||||
}
|
||||
peer.endpoint.Unlock()
|
||||
|
||||
keyf("public_key", (*[32]byte)(&peer.handshake.remoteStatic))
|
||||
keyf("preshared_key", (*[32]byte)(&peer.handshake.presharedKey))
|
||||
sendf("protocol_version=1")
|
||||
if peer.endpoint != nil {
|
||||
sendf("endpoint=%s", peer.endpoint.DstToString())
|
||||
}
|
||||
nano := peer.lastHandshakeNano.Load()
|
||||
secs := nano / time.Second.Nanoseconds()
|
||||
nano %= time.Second.Nanoseconds()
|
||||
|
||||
nano := peer.lastHandshakeNano.Load()
|
||||
secs := nano / time.Second.Nanoseconds()
|
||||
nano %= time.Second.Nanoseconds()
|
||||
sendf("last_handshake_time_sec=%d", secs)
|
||||
sendf("last_handshake_time_nsec=%d", nano)
|
||||
sendf("tx_bytes=%d", peer.txBytes.Load())
|
||||
sendf("rx_bytes=%d", peer.rxBytes.Load())
|
||||
sendf("persistent_keepalive_interval=%d", peer.persistentKeepaliveInterval.Load())
|
||||
|
||||
sendf("last_handshake_time_sec=%d", secs)
|
||||
sendf("last_handshake_time_nsec=%d", nano)
|
||||
sendf("tx_bytes=%d", peer.txBytes.Load())
|
||||
sendf("rx_bytes=%d", peer.rxBytes.Load())
|
||||
sendf("persistent_keepalive_interval=%d", peer.persistentKeepaliveInterval.Load())
|
||||
|
||||
device.allowedips.EntriesForPeer(peer, func(prefix netip.Prefix) bool {
|
||||
sendf("allowed_ip=%s", prefix.String())
|
||||
return true
|
||||
})
|
||||
}()
|
||||
device.allowedips.EntriesForPeer(peer, func(prefix netip.Prefix) bool {
|
||||
sendf("allowed_ip=%s", prefix.String())
|
||||
return true
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -149,6 +213,7 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
ipcDev := new(ipcSetDevice)
|
||||
peer := new(ipcSetPeer)
|
||||
deviceConfig := true
|
||||
|
||||
@@ -157,12 +222,20 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
// Blank line means terminate operation.
|
||||
err := ipcDev.mergeWithDevice(device)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peer.handlePostConfig()
|
||||
return nil
|
||||
}
|
||||
key, value, ok := strings.Cut(line, "=")
|
||||
if !ok {
|
||||
return ipcErrorf(ipc.IpcErrorProtocol, "failed to parse line %q", line)
|
||||
return ipcErrorf(
|
||||
ipc.IpcErrorProtocol,
|
||||
"failed to parse line %q",
|
||||
line,
|
||||
)
|
||||
}
|
||||
|
||||
if key == "public_key" {
|
||||
@@ -188,6 +261,10 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = ipcDev.mergeWithDevice(device)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
peer.handlePostConfig()
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
@@ -237,11 +314,187 @@ func (device *Device) handleDeviceLine(key, value string) error {
|
||||
|
||||
case "replace_peers":
|
||||
if value != "true" {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set replace_peers, invalid value: %v", value)
|
||||
return ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"failed to set replace_peers, invalid value: %v",
|
||||
value,
|
||||
)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Removing all peers")
|
||||
device.RemoveAllPeers()
|
||||
|
||||
case "jc":
|
||||
jc, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jc: %w", err)
|
||||
}
|
||||
if jc <= 0 {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "jc must be a positive value")
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating junk count")
|
||||
device.junk.count = jc
|
||||
|
||||
case "jmin":
|
||||
jmin, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jmin: %w", err)
|
||||
}
|
||||
if jmin <= 0 {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "jmin must be a positive value")
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating junk min")
|
||||
device.junk.min = jmin
|
||||
|
||||
case "jmax":
|
||||
jmax, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jmax: %w", err)
|
||||
}
|
||||
if jmax <= 0 {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "jmax must be a positive value")
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating junk max")
|
||||
device.junk.max = jmax
|
||||
|
||||
case "s1":
|
||||
padding, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s1: %w", err)
|
||||
}
|
||||
if padding < 0 {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "s1 must be non-negative")
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating s1 padding")
|
||||
device.paddings.init = padding
|
||||
|
||||
case "s2":
|
||||
padding, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s2: %w", err)
|
||||
}
|
||||
if padding < 0 {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "s2 must be non-negative")
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating s2 padding")
|
||||
device.paddings.response = padding
|
||||
|
||||
case "s3":
|
||||
padding, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s3: %w", err)
|
||||
}
|
||||
if padding < 0 {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "s3 must be non-negative")
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating s3 padding")
|
||||
device.paddings.cookie = padding
|
||||
|
||||
case "s4":
|
||||
padding, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s4: %w", err)
|
||||
}
|
||||
if padding < 0 {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "s4 must be non-negative")
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating s4 padding")
|
||||
device.paddings.transport = padding
|
||||
|
||||
case "h1":
|
||||
header, err := newMagicHeader(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H1: %w", err)
|
||||
}
|
||||
device.headers.init = header
|
||||
|
||||
case "h2":
|
||||
header, err := newMagicHeader(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H2: %w", err)
|
||||
}
|
||||
device.headers.response = header
|
||||
|
||||
case "h3":
|
||||
header, err := newMagicHeader(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H3: %w", err)
|
||||
}
|
||||
device.headers.cookie = header
|
||||
|
||||
case "h4":
|
||||
header, err := newMagicHeader(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H4: %w", err)
|
||||
}
|
||||
device.headers.transport = header
|
||||
|
||||
case "i1":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I1: %w", err)
|
||||
}
|
||||
device.ipackets[0] = chain
|
||||
|
||||
case "i2":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I2: %w", err)
|
||||
}
|
||||
device.ipackets[1] = chain
|
||||
|
||||
case "i3":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I3: %w", err)
|
||||
}
|
||||
device.ipackets[2] = chain
|
||||
|
||||
case "i4":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I4: %w", err)
|
||||
}
|
||||
device.ipackets[3] = chain
|
||||
|
||||
case "i5":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I5: %w", err)
|
||||
}
|
||||
device.ipackets[4] = chain
|
||||
|
||||
case "di":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse DI: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Set DI format")
|
||||
device.dpackets.init = chain
|
||||
|
||||
case "dr":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse DR: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Set DR format")
|
||||
device.dpackets.response = chain
|
||||
|
||||
case "dc":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse DC: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Set DC format")
|
||||
device.dpackets.cookie = chain
|
||||
|
||||
case "dt":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse DT: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Set DT format")
|
||||
device.dpackets.transport = chain
|
||||
|
||||
default:
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "invalid UAPI device key: %v", key)
|
||||
}
|
||||
@@ -262,7 +515,7 @@ func (peer *ipcSetPeer) handlePostConfig() {
|
||||
return
|
||||
}
|
||||
if peer.created {
|
||||
peer.disableRoaming = peer.device.net.brokenRoaming && peer.endpoint != nil
|
||||
peer.endpoint.disableRoaming = peer.device.net.brokenRoaming && peer.endpoint.val != nil
|
||||
}
|
||||
if peer.device.isUp() {
|
||||
peer.Start()
|
||||
@@ -273,7 +526,10 @@ func (peer *ipcSetPeer) handlePostConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) handlePublicKeyLine(peer *ipcSetPeer, value string) error {
|
||||
func (device *Device) handlePublicKeyLine(
|
||||
peer *ipcSetPeer,
|
||||
value string,
|
||||
) error {
|
||||
// Load/create the peer we are configuring.
|
||||
var publicKey NoisePublicKey
|
||||
err := publicKey.FromHex(value)
|
||||
@@ -303,12 +559,19 @@ func (device *Device) handlePublicKeyLine(peer *ipcSetPeer, value string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error {
|
||||
func (device *Device) handlePeerLine(
|
||||
peer *ipcSetPeer,
|
||||
key, value string,
|
||||
) error {
|
||||
switch key {
|
||||
case "update_only":
|
||||
// allow disabling of creation
|
||||
if value != "true" {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set update only, invalid value: %v", value)
|
||||
return ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"failed to set update only, invalid value: %v",
|
||||
value,
|
||||
)
|
||||
}
|
||||
if peer.created && !peer.dummy {
|
||||
device.RemovePeer(peer.handshake.remoteStatic)
|
||||
@@ -345,16 +608,20 @@ func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set endpoint %v: %w", value, err)
|
||||
}
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
peer.endpoint = endpoint
|
||||
peer.endpoint.Lock()
|
||||
defer peer.endpoint.Unlock()
|
||||
peer.endpoint.val = endpoint
|
||||
|
||||
case "persistent_keepalive_interval":
|
||||
device.log.Verbosef("%v - UAPI: Updating persistent keepalive interval", peer.Peer)
|
||||
|
||||
secs, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set persistent keepalive interval: %w", err)
|
||||
return ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"failed to set persistent keepalive interval: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
old := peer.persistentKeepaliveInterval.Swap(uint32(secs))
|
||||
@@ -365,7 +632,11 @@ func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error
|
||||
case "replace_allowed_ips":
|
||||
device.log.Verbosef("%v - UAPI: Removing all allowedips", peer.Peer)
|
||||
if value != "true" {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to replace allowedips, invalid value: %v", value)
|
||||
return ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"failed to replace allowedips, invalid value: %v",
|
||||
value,
|
||||
)
|
||||
}
|
||||
if peer.dummy {
|
||||
return nil
|
||||
@@ -373,7 +644,14 @@ func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error
|
||||
device.allowedips.RemoveByPeer(peer.Peer)
|
||||
|
||||
case "allowed_ip":
|
||||
device.log.Verbosef("%v - UAPI: Adding allowedip", peer.Peer)
|
||||
add := true
|
||||
verb := "Adding"
|
||||
if len(value) > 0 && value[0] == '-' {
|
||||
add = false
|
||||
verb = "Removing"
|
||||
value = value[1:]
|
||||
}
|
||||
device.log.Verbosef("%v - UAPI: %s allowedip", peer.Peer, verb)
|
||||
prefix, err := netip.ParsePrefix(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set allowed ip: %w", err)
|
||||
@@ -381,7 +659,11 @@ func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error
|
||||
if peer.dummy {
|
||||
return nil
|
||||
}
|
||||
device.allowedips.Insert(prefix, peer.Peer)
|
||||
if add {
|
||||
device.allowedips.Insert(prefix, peer.Peer)
|
||||
} else {
|
||||
device.allowedips.Remove(prefix, peer.Peer)
|
||||
}
|
||||
|
||||
case "protocol_version":
|
||||
if value != "1" {
|
||||
@@ -433,7 +715,11 @@ func (device *Device) IpcHandle(socket net.Conn) {
|
||||
return
|
||||
}
|
||||
if nextByte != '\n' {
|
||||
err = ipcErrorf(ipc.IpcErrorInvalid, "trailing character in UAPI get: %q", nextByte)
|
||||
err = ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"trailing character in UAPI get: %q",
|
||||
nextByte,
|
||||
)
|
||||
break
|
||||
}
|
||||
err = device.IpcGetOperation(buffered.Writer)
|
||||
@@ -457,3 +743,49 @@ func (device *Device) IpcHandle(socket net.Conn) {
|
||||
buffered.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
type ipcSetDevice struct {
|
||||
headers struct {
|
||||
init *magicHeader
|
||||
response *magicHeader
|
||||
cookie *magicHeader
|
||||
transport *magicHeader
|
||||
}
|
||||
}
|
||||
|
||||
func (d *ipcSetDevice) mergeWithDevice(device *Device) error {
|
||||
if d.headers.init == nil {
|
||||
d.headers.init = device.headers.init
|
||||
}
|
||||
|
||||
if d.headers.response == nil {
|
||||
d.headers.response = device.headers.response
|
||||
}
|
||||
|
||||
if d.headers.cookie == nil {
|
||||
d.headers.cookie = device.headers.cookie
|
||||
}
|
||||
|
||||
if d.headers.transport == nil {
|
||||
d.headers.transport = device.headers.transport
|
||||
}
|
||||
|
||||
headers := []*magicHeader{d.headers.init, d.headers.response, d.headers.cookie, d.headers.transport}
|
||||
for i := 0; i < len(headers); i++ {
|
||||
for j := i + 1; j < len(headers); j++ {
|
||||
left := headers[i]
|
||||
right := headers[j]
|
||||
|
||||
if left.start <= right.end && right.start <= left.end {
|
||||
return errors.New("headers must not overlap")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device.headers.init = d.headers.init
|
||||
device.headers.response = d.headers.response
|
||||
device.headers.cookie = d.headers.cookie
|
||||
device.headers.transport = d.headers.transport
|
||||
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
module golang.zx2c4.com/wireguard
|
||||
module github.com/amnezia-vpn/amneziawg-go
|
||||
|
||||
go 1.20
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tevino/abool v1.2.0
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
|
||||
golang.org/x/net v0.41.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -1,14 +1,30 @@
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89 h1:260HNjMTPDya+jq5AM1zZLgG9pv9GASPAGiEEJUbRg4=
|
||||
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
|
||||
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
|
||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY=
|
||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U6A2rpebLs/MtTwFacjKb8jLdQA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE=
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc/namedpipe"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/ipc/namedpipe"
|
||||
)
|
||||
|
||||
func randomPipePath() string {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
)
|
||||
|
||||
type UAPIListener struct {
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
|
||||
// socketDirectory is variable because it is modified by a linker
|
||||
// flag in wireguard-android.
|
||||
var socketDirectory = "/var/run/wireguard"
|
||||
var socketDirectory = "/var/run/amneziawg"
|
||||
|
||||
func sockPath(iface string) string {
|
||||
return fmt.Sprintf("%s/%s.sock", socketDirectory, iface)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
@@ -8,8 +8,8 @@ package ipc
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc/namedpipe"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/ipc/namedpipe"
|
||||
)
|
||||
|
||||
// TODO: replace these with actual standard windows error numbers from the win package
|
||||
@@ -62,7 +62,7 @@ func init() {
|
||||
func UAPIListen(name string) (net.Listener, error) {
|
||||
listener, err := (&namedpipe.ListenConfig{
|
||||
SecurityDescriptor: UAPISecurityDescriptor,
|
||||
}).Listen(`\\.\pipe\ProtectedPrefix\Administrators\WireGuard\` + name)
|
||||
}).Listen(`\\.\pipe\ProtectedPrefix\Administrators\AmneziaWG\` + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,20 +46,20 @@ func warning() {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────┐")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "│ Running wireguard-go is not required because this │")
|
||||
fmt.Fprintln(os.Stderr, "│ kernel has first class support for WireGuard. For │")
|
||||
fmt.Fprintln(os.Stderr, "│ information on installing the kernel module, │")
|
||||
fmt.Fprintln(os.Stderr, "│ please visit: │")
|
||||
fmt.Fprintln(os.Stderr, "│ https://www.wireguard.com/install/ │")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────┘")
|
||||
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────────────┐")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "│ Running amneziawg-go is not required because this │")
|
||||
fmt.Fprintln(os.Stderr, "│ kernel has first class support for AmneziaWG. For │")
|
||||
fmt.Fprintln(os.Stderr, "│ information on installing the kernel module, │")
|
||||
fmt.Fprintln(os.Stderr, "│ please visit: │")
|
||||
fmt.Fprintln(os.Stderr, "| https://github.com/amnezia-vpn/amneziawg-linux-kernel-module │")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────────────┘")
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 2 && os.Args[1] == "--version" {
|
||||
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", Version, runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Printf("amneziawg-go %s\n\nUserspace AmneziaWG daemon for %s-%s.\nInformation available at https://amnezia.org\n", Version, runtime.GOOS, runtime.GOARCH)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func main() {
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
|
||||
logger.Verbosef("Starting wireguard-go version %s", Version)
|
||||
logger.Verbosef("Starting amneziawg-go version %s", Version)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to create TUN device: %v", err)
|
||||
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -12,11 +12,11 @@ import (
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -30,13 +30,13 @@ func main() {
|
||||
}
|
||||
interfaceName := os.Args[1]
|
||||
|
||||
fmt.Fprintln(os.Stderr, "Warning: this is a test program for Windows, mainly used for debugging this Go package. For a real WireGuard for Windows client, the repo you want is <https://git.zx2c4.com/wireguard-windows/>, which includes this code as a module.")
|
||||
fmt.Fprintln(os.Stderr, "Warning: this is a test program for Windows, mainly used for debugging this Go package. For a real AmneziaWG for Windows client, please visit: https://amnezia.org")
|
||||
|
||||
logger := device.NewLogger(
|
||||
device.LogLevelVerbose,
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
logger.Verbosef("Starting wireguard-go version %s", Version)
|
||||
logger.Verbosef("Starting amneziawg-go version %s", Version)
|
||||
|
||||
tun, err := tun.CreateTUN(interfaceName, 0)
|
||||
if err == nil {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
// Package replay implements an efficient anti-replay algorithm as specified in RFC 6479.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package replay
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
// Package rwcancel implements cancelable read/write operations on
|
||||
@@ -64,7 +64,7 @@ func (rw *RWCancel) ReadyRead() bool {
|
||||
|
||||
func (rw *RWCancel) ReadyWrite() bool {
|
||||
closeFd := int32(rw.closingReader.Fd())
|
||||
pollFds := []unix.PollFd{{Fd: int32(rw.fd), Events: unix.POLLOUT}, {Fd: closeFd, Events: unix.POLLOUT}}
|
||||
pollFds := []unix.PollFd{{Fd: int32(rw.fd), Events: unix.POLLOUT}, {Fd: closeFd, Events: unix.POLLIN}}
|
||||
var err error
|
||||
for {
|
||||
_, err = unix.Poll(pollFds, -1)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tai64n
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tai64n
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
+75
-15
@@ -1,26 +1,86 @@
|
||||
package tun
|
||||
|
||||
import "encoding/binary"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// TODO: Explore SIMD and/or other assembly optimizations.
|
||||
func checksumNoFold(b []byte, initial uint64) uint64 {
|
||||
ac := initial
|
||||
i := 0
|
||||
n := len(b)
|
||||
for n >= 4 {
|
||||
ac += uint64(binary.BigEndian.Uint32(b[i : i+4]))
|
||||
n -= 4
|
||||
i += 4
|
||||
tmp := make([]byte, 8)
|
||||
binary.NativeEndian.PutUint64(tmp, initial)
|
||||
ac := binary.BigEndian.Uint64(tmp)
|
||||
var carry uint64
|
||||
|
||||
for len(b) >= 128 {
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[32:40]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[40:48]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[48:56]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[56:64]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[64:72]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[72:80]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[80:88]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[88:96]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[96:104]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[104:112]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[112:120]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[120:128]), carry)
|
||||
ac += carry
|
||||
b = b[128:]
|
||||
}
|
||||
for n >= 2 {
|
||||
ac += uint64(binary.BigEndian.Uint16(b[i : i+2]))
|
||||
n -= 2
|
||||
i += 2
|
||||
if len(b) >= 64 {
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[32:40]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[40:48]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[48:56]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[56:64]), carry)
|
||||
ac += carry
|
||||
b = b[64:]
|
||||
}
|
||||
if n == 1 {
|
||||
ac += uint64(b[i]) << 8
|
||||
if len(b) >= 32 {
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry)
|
||||
ac += carry
|
||||
b = b[32:]
|
||||
}
|
||||
return ac
|
||||
if len(b) >= 16 {
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry)
|
||||
ac += carry
|
||||
b = b[16:]
|
||||
}
|
||||
if len(b) >= 8 {
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac += carry
|
||||
b = b[8:]
|
||||
}
|
||||
if len(b) >= 4 {
|
||||
ac, carry = bits.Add64(ac, uint64(binary.NativeEndian.Uint32(b[:4])), 0)
|
||||
ac += carry
|
||||
b = b[4:]
|
||||
}
|
||||
if len(b) >= 2 {
|
||||
ac, carry = bits.Add64(ac, uint64(binary.NativeEndian.Uint16(b[:2])), 0)
|
||||
ac += carry
|
||||
b = b[2:]
|
||||
}
|
||||
if len(b) == 1 {
|
||||
tmp := binary.NativeEndian.Uint16([]byte{b[0], 0})
|
||||
ac, carry = bits.Add64(ac, uint64(tmp), 0)
|
||||
ac += carry
|
||||
}
|
||||
|
||||
binary.NativeEndian.PutUint64(tmp, ac)
|
||||
return binary.BigEndian.Uint64(tmp)
|
||||
}
|
||||
|
||||
func checksum(b []byte, initial uint64) uint16 {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func checksumRef(b []byte, initial uint16) uint16 {
|
||||
ac := uint64(initial)
|
||||
|
||||
for len(b) >= 2 {
|
||||
ac += uint64(binary.BigEndian.Uint16(b))
|
||||
b = b[2:]
|
||||
}
|
||||
if len(b) == 1 {
|
||||
ac += uint64(b[0]) << 8
|
||||
}
|
||||
|
||||
for (ac >> 16) > 0 {
|
||||
ac = (ac >> 16) + (ac & 0xffff)
|
||||
}
|
||||
return uint16(ac)
|
||||
}
|
||||
|
||||
func pseudoHeaderChecksumRefNoFold(protocol uint8, srcAddr, dstAddr []byte, totalLen uint16) uint16 {
|
||||
sum := checksumRef(srcAddr, 0)
|
||||
sum = checksumRef(dstAddr, sum)
|
||||
sum = checksumRef([]byte{0, protocol}, sum)
|
||||
tmp := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(tmp, totalLen)
|
||||
return checksumRef(tmp, sum)
|
||||
}
|
||||
|
||||
func TestChecksum(t *testing.T) {
|
||||
for length := 0; length <= 9001; length++ {
|
||||
buf := make([]byte, length)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
rng.Read(buf)
|
||||
csum := checksum(buf, 0x1234)
|
||||
csumRef := checksumRef(buf, 0x1234)
|
||||
if csum != csumRef {
|
||||
t.Error("Expected checksum", csumRef, "got", csum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPseudoHeaderChecksum(t *testing.T) {
|
||||
for _, addrLen := range []int{4, 16} {
|
||||
for length := 0; length <= 9001; length++ {
|
||||
srcAddr := make([]byte, addrLen)
|
||||
dstAddr := make([]byte, addrLen)
|
||||
buf := make([]byte, length)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
rng.Read(srcAddr)
|
||||
rng.Read(dstAddr)
|
||||
rng.Read(buf)
|
||||
phSum := pseudoHeaderChecksumNoFold(unix.IPPROTO_TCP, srcAddr, dstAddr, uint16(length))
|
||||
csum := checksum(buf, phSum)
|
||||
phSumRef := pseudoHeaderChecksumRefNoFold(unix.IPPROTO_TCP, srcAddr, dstAddr, uint16(length))
|
||||
csumRef := checksumRef(buf, phSumRef)
|
||||
if csum != csumRef {
|
||||
t.Error("Expected checksumRef", csumRef, "got", csum)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkChecksum(b *testing.B) {
|
||||
lengths := []int{
|
||||
64,
|
||||
128,
|
||||
256,
|
||||
512,
|
||||
1024,
|
||||
1500,
|
||||
2048,
|
||||
4096,
|
||||
8192,
|
||||
9000,
|
||||
9001,
|
||||
}
|
||||
|
||||
for _, length := range lengths {
|
||||
b.Run(fmt.Sprintf("%d", length), func(b *testing.B) {
|
||||
buf := make([]byte, length)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
rng.Read(buf)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
checksum(buf, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/netstack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/netstack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -17,9 +17,9 @@ import (
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/netstack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
Loaded 100 of 116 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user