mirror of
https://github.com/amnezia-vpn/amneziawg-go.git
synced 2025-07-14 15:32:43 +02:00
Compare commits
83 commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
121 changed files with 5575 additions and 2259 deletions
41
.github/workflows/build-if-tag.yml
vendored
Normal file
41
.github/workflows/build-if-tag.yml
vendored
Normal file
|
@ -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 }}
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1 @@
|
||||||
wireguard-go
|
amneziawg-go
|
18
Dockerfile
Normal file
18
Dockerfile
Normal file
|
@ -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.20241018"
|
||||||
|
|
||||||
|
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
|
12
Makefile
12
Makefile
|
@ -9,23 +9,23 @@ MAKEFLAGS += --no-print-directory
|
||||||
|
|
||||||
generate-version-and-build:
|
generate-version-and-build:
|
||||||
@export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \
|
@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")" && \
|
ver="$$(printf 'package main\n\nconst Version = "%s"\n' "$$tag")" && \
|
||||||
[ "$$(cat version.go 2>/dev/null)" != "$$ver" ] && \
|
[ "$$(cat version.go 2>/dev/null)" != "$$ver" ] && \
|
||||||
echo "$$ver" > version.go && \
|
echo "$$ver" > version.go && \
|
||||||
git update-index --assume-unchanged version.go || true
|
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 "$@"
|
go build -v -o "$@"
|
||||||
|
|
||||||
install: wireguard-go
|
install: amneziawg-go
|
||||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/wireguard-go"
|
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/amneziawg-go"
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test ./...
|
go test ./...
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f wireguard-go
|
rm -f amneziawg-go
|
||||||
|
|
||||||
.PHONY: all clean test install generate-version-and-build
|
.PHONY: all clean test install generate-version-and-build
|
||||||
|
|
58
README.md
58
README.md
|
@ -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
|
## 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 [`amneziawg-tools `](https://github.com/amnezia-vpn/amneziawg-tools) to configure it, as well as the usual `ip(8)` and `ifconfig(8)` commands.
|
||||||
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.
|
|
||||||
|
|
||||||
To run with more logging you may set the environment variable `LOG_LEVEL=debug`.
|
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
|
### 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
|
### 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 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
|
### 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
|
## Building
|
||||||
|
|
||||||
This requires an installation of the latest version of [Go](https://go.dev/).
|
This requires an installation of the latest version of [Go](https://go.dev/).
|
||||||
|
|
||||||
```
|
```
|
||||||
$ git clone https://git.zx2c4.com/wireguard-go
|
$ git clone https://github.com/amnezia-vpn/amneziawg-go
|
||||||
$ cd wireguard-go
|
$ cd amneziawg-go
|
||||||
$ make
|
$ 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.
|
|
||||||
|
|
408
conn/bind_std.go
408
conn/bind_std.go
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
@ -8,6 +8,7 @@ package conn
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -29,16 +30,19 @@ var (
|
||||||
// methods for sending and receiving multiple datagrams per-syscall. See the
|
// methods for sending and receiving multiple datagrams per-syscall. See the
|
||||||
// proposal in https://github.com/golang/go/issues/45886#issuecomment-1218301564.
|
// proposal in https://github.com/golang/go/issues/45886#issuecomment-1218301564.
|
||||||
type StdNetBind struct {
|
type StdNetBind struct {
|
||||||
mu sync.Mutex // protects all fields except as specified
|
mu sync.Mutex // protects all fields except as specified
|
||||||
ipv4 *net.UDPConn
|
ipv4 *net.UDPConn
|
||||||
ipv6 *net.UDPConn
|
ipv6 *net.UDPConn
|
||||||
ipv4PC *ipv4.PacketConn // will be nil on non-Linux
|
ipv4PC *ipv4.PacketConn // will be nil on non-Linux
|
||||||
ipv6PC *ipv6.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
|
// these two fields are not guarded by mu
|
||||||
udpAddrPool sync.Pool
|
udpAddrPool sync.Pool
|
||||||
ipv4MsgsPool sync.Pool
|
msgsPool sync.Pool
|
||||||
ipv6MsgsPool sync.Pool
|
|
||||||
|
|
||||||
blackhole4 bool
|
blackhole4 bool
|
||||||
blackhole6 bool
|
blackhole6 bool
|
||||||
|
@ -54,23 +58,14 @@ func NewStdNetBind() Bind {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ipv4MsgsPool: sync.Pool{
|
msgsPool: 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{
|
|
||||||
New: func() any {
|
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)
|
msgs := make([]ipv6.Message, IdealBatchSize)
|
||||||
for i := range msgs {
|
for i := range msgs {
|
||||||
msgs[i].Buffers = make(net.Buffers, 1)
|
msgs[i].Buffers = make(net.Buffers, 1)
|
||||||
msgs[i].OOB = make([]byte, srcControlSize)
|
msgs[i].OOB = make([]byte, 0, stickyControlSize+gsoControlSize)
|
||||||
}
|
}
|
||||||
return &msgs
|
return &msgs
|
||||||
},
|
},
|
||||||
|
@ -113,7 +108,7 @@ func (e *StdNetEndpoint) DstIP() netip.Addr {
|
||||||
return e.AddrPort.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 {
|
func (e *StdNetEndpoint) DstToBytes() []byte {
|
||||||
b, _ := e.AddrPort.MarshalBinary()
|
b, _ := e.AddrPort.MarshalBinary()
|
||||||
|
@ -179,19 +174,21 @@ again:
|
||||||
}
|
}
|
||||||
var fns []ReceiveFunc
|
var fns []ReceiveFunc
|
||||||
if v4conn != nil {
|
if v4conn != nil {
|
||||||
if runtime.GOOS == "linux" {
|
s.ipv4TxOffload, s.ipv4RxOffload = supportsUDPOffload(v4conn)
|
||||||
|
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||||
v4pc = ipv4.NewPacketConn(v4conn)
|
v4pc = ipv4.NewPacketConn(v4conn)
|
||||||
s.ipv4PC = v4pc
|
s.ipv4PC = v4pc
|
||||||
}
|
}
|
||||||
fns = append(fns, s.makeReceiveIPv4(v4pc, v4conn))
|
fns = append(fns, s.makeReceiveIPv4(v4pc, v4conn, s.ipv4RxOffload))
|
||||||
s.ipv4 = v4conn
|
s.ipv4 = v4conn
|
||||||
}
|
}
|
||||||
if v6conn != nil {
|
if v6conn != nil {
|
||||||
if runtime.GOOS == "linux" {
|
s.ipv6TxOffload, s.ipv6RxOffload = supportsUDPOffload(v6conn)
|
||||||
|
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||||
v6pc = ipv6.NewPacketConn(v6conn)
|
v6pc = ipv6.NewPacketConn(v6conn)
|
||||||
s.ipv6PC = v6pc
|
s.ipv6PC = v6pc
|
||||||
}
|
}
|
||||||
fns = append(fns, s.makeReceiveIPv6(v6pc, v6conn))
|
fns = append(fns, s.makeReceiveIPv6(v6pc, v6conn, s.ipv6RxOffload))
|
||||||
s.ipv6 = v6conn
|
s.ipv6 = v6conn
|
||||||
}
|
}
|
||||||
if len(fns) == 0 {
|
if len(fns) == 0 {
|
||||||
|
@ -201,76 +198,101 @@ again:
|
||||||
return fns, uint16(port), nil
|
return fns, uint16(port), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StdNetBind) makeReceiveIPv4(pc *ipv4.PacketConn, conn *net.UDPConn) ReceiveFunc {
|
func (s *StdNetBind) putMessages(msgs *[]ipv6.Message) {
|
||||||
return func(bufs [][]byte, sizes []int, eps []Endpoint) (n int, err error) {
|
for i := range *msgs {
|
||||||
msgs := s.ipv4MsgsPool.Get().(*[]ipv4.Message)
|
(*msgs)[i].OOB = (*msgs)[i].OOB[:0]
|
||||||
defer s.ipv4MsgsPool.Put(msgs)
|
(*msgs)[i] = ipv6.Message{Buffers: (*msgs)[i].Buffers, OOB: (*msgs)[i].OOB}
|
||||||
for i := range bufs {
|
}
|
||||||
(*msgs)[i].Buffers[0] = bufs[i]
|
s.msgsPool.Put(msgs)
|
||||||
}
|
}
|
||||||
var numMsgs int
|
|
||||||
if runtime.GOOS == "linux" {
|
func (s *StdNetBind) getMessages() *[]ipv6.Message {
|
||||||
numMsgs, err = pc.ReadBatch(*msgs, 0)
|
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 {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msg := &(*msgs)[0]
|
numMsgs, err = br.ReadBatch(*msgs, 0)
|
||||||
msg.N, msg.NN, _, msg.Addr, err = conn.ReadMsgUDP(msg.Buffers[0], msg.OOB)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
numMsgs = 1
|
|
||||||
}
|
}
|
||||||
for i := 0; i < numMsgs; i++ {
|
} else {
|
||||||
msg := &(*msgs)[i]
|
msg := &(*msgs)[0]
|
||||||
sizes[i] = msg.N
|
msg.N, msg.NN, _, msg.Addr, err = conn.ReadMsgUDP(msg.Buffers[0], msg.OOB)
|
||||||
addrPort := msg.Addr.(*net.UDPAddr).AddrPort()
|
if err != nil {
|
||||||
ep := &StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation
|
return 0, err
|
||||||
getSrcFromControl(msg.OOB[:msg.NN], ep)
|
|
||||||
eps[i] = ep
|
|
||||||
}
|
}
|
||||||
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) {
|
return func(bufs [][]byte, sizes []int, eps []Endpoint) (n int, err error) {
|
||||||
msgs := s.ipv6MsgsPool.Get().(*[]ipv6.Message)
|
return s.receiveIP(pc, conn, rxOffload, bufs, sizes, eps)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: When all Binds handle IdealBatchSize, remove this dynamic function and
|
// TODO: When all Binds handle IdealBatchSize, remove this dynamic function and
|
||||||
// rename the IdealBatchSize constant to BatchSize.
|
// rename the IdealBatchSize constant to BatchSize.
|
||||||
func (s *StdNetBind) BatchSize() int {
|
func (s *StdNetBind) BatchSize() int {
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||||
return IdealBatchSize
|
return IdealBatchSize
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
|
@ -293,28 +315,42 @@ func (s *StdNetBind) Close() error {
|
||||||
}
|
}
|
||||||
s.blackhole4 = false
|
s.blackhole4 = false
|
||||||
s.blackhole6 = false
|
s.blackhole6 = false
|
||||||
|
s.ipv4TxOffload = false
|
||||||
|
s.ipv4RxOffload = false
|
||||||
|
s.ipv6TxOffload = false
|
||||||
|
s.ipv6RxOffload = false
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
return err1
|
return err1
|
||||||
}
|
}
|
||||||
return err2
|
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 {
|
func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
blackhole := s.blackhole4
|
blackhole := s.blackhole4
|
||||||
conn := s.ipv4
|
conn := s.ipv4
|
||||||
var (
|
offload := s.ipv4TxOffload
|
||||||
pc4 *ipv4.PacketConn
|
br := batchWriter(s.ipv4PC)
|
||||||
pc6 *ipv6.PacketConn
|
|
||||||
)
|
|
||||||
is6 := false
|
is6 := false
|
||||||
if endpoint.DstIP().Is6() {
|
if endpoint.DstIP().Is6() {
|
||||||
blackhole = s.blackhole6
|
blackhole = s.blackhole6
|
||||||
conn = s.ipv6
|
conn = s.ipv6
|
||||||
pc6 = s.ipv6PC
|
br = s.ipv6PC
|
||||||
is6 = true
|
is6 = true
|
||||||
} else {
|
offload = s.ipv6TxOffload
|
||||||
pc4 = s.ipv4PC
|
|
||||||
}
|
}
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
@ -324,85 +360,185 @@ func (s *StdNetBind) Send(bufs [][]byte, endpoint Endpoint) error {
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
return syscall.EAFNOSUPPORT
|
return syscall.EAFNOSUPPORT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msgs := s.getMessages()
|
||||||
|
defer s.putMessages(msgs)
|
||||||
|
ua := s.udpAddrPool.Get().(*net.UDPAddr)
|
||||||
|
defer s.udpAddrPool.Put(ua)
|
||||||
if is6 {
|
if is6 {
|
||||||
return s.send6(conn, pc6, endpoint, bufs)
|
as16 := endpoint.DstIP().As16()
|
||||||
|
copy(ua.IP, as16[:])
|
||||||
|
ua.IP = ua.IP[:16]
|
||||||
} else {
|
} 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 {
|
func (s *StdNetBind) send(conn *net.UDPConn, pc batchWriter, msgs []ipv6.Message) 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))
|
|
||||||
}
|
|
||||||
var (
|
var (
|
||||||
n int
|
n int
|
||||||
err error
|
err error
|
||||||
start int
|
start int
|
||||||
)
|
)
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||||
for {
|
for {
|
||||||
n, err = pc.WriteBatch((*msgs)[start:len(bufs)], 0)
|
n, err = pc.WriteBatch(msgs[start:], 0)
|
||||||
if err != nil || n == len((*msgs)[start:len(bufs)]) {
|
if err != nil || n == len(msgs[start:]) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
start += n
|
start += n
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i, buf := range bufs {
|
for _, msg := range msgs {
|
||||||
_, _, err = conn.WriteMsgUDP(buf, (*msgs)[i].OOB, ua)
|
_, _, err = conn.WriteMsgUDP(msg.Buffers[0], msg.OOB, msg.Addr.(*net.UDPAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.udpAddrPool.Put(ua)
|
|
||||||
s.ipv4MsgsPool.Put(msgs)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StdNetBind) send6(conn *net.UDPConn, pc *ipv6.PacketConn, ep Endpoint, bufs [][]byte) error {
|
const (
|
||||||
ua := s.udpAddrPool.Get().(*net.UDPAddr)
|
// Exceeding these values results in EMSGSIZE. They account for layer3 and
|
||||||
as16 := ep.DstIP().As16()
|
// layer4 headers. IPv6 does not need to account for itself as the payload
|
||||||
copy(ua.IP, as16[:])
|
// length field is self excluding.
|
||||||
ua.IP = ua.IP[:16]
|
maxIPv4PayloadLen = 1<<16 - 1 - 20 - 8
|
||||||
ua.Port = int(ep.(*StdNetEndpoint).Port())
|
maxIPv6PayloadLen = 1<<16 - 1 - 8
|
||||||
msgs := s.ipv6MsgsPool.Get().(*[]ipv6.Message)
|
|
||||||
for i, buf := range bufs {
|
// This is a hard limit imposed by the kernel.
|
||||||
(*msgs)[i].Buffers[0] = buf
|
udpSegmentMaxDatagrams = 64
|
||||||
(*msgs)[i].Addr = ua
|
)
|
||||||
setSrcControl(&(*msgs)[i].OOB, ep.(*StdNetEndpoint))
|
|
||||||
}
|
type setGSOFunc func(control *[]byte, gsoSize uint16)
|
||||||
|
|
||||||
|
func coalesceMessages(addr *net.UDPAddr, ep *StdNetEndpoint, bufs [][]byte, msgs []ipv6.Message, setGSO setGSOFunc) int {
|
||||||
var (
|
var (
|
||||||
n int
|
base = -1 // index of msg we are currently coalescing into
|
||||||
err error
|
gsoSize int // segmentation size of msgs[base]
|
||||||
start int
|
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" {
|
maxPayloadLen := maxIPv4PayloadLen
|
||||||
for {
|
if ep.DstIP().Is6() {
|
||||||
n, err = pc.WriteBatch((*msgs)[start:len(bufs)], 0)
|
maxPayloadLen = maxIPv6PayloadLen
|
||||||
if err != nil || n == len((*msgs)[start:len(bufs)]) {
|
}
|
||||||
break
|
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 {
|
if dgramCnt > 1 {
|
||||||
for i, buf := range bufs {
|
setGSO(&msgs[base].OOB, uint16(gsoSize))
|
||||||
_, _, err = conn.WriteMsgUDP(buf, (*msgs)[i].OOB, ua)
|
}
|
||||||
if err != nil {
|
// Reset prior to incrementing base since we are preparing to start a
|
||||||
break
|
// 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)
|
return n, nil
|
||||||
s.ipv6MsgsPool.Put(msgs)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
package conn
|
package conn
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
|
)
|
||||||
|
|
||||||
func TestStdNetBindReceiveFuncAfterClose(t *testing.T) {
|
func TestStdNetBindReceiveFuncAfterClose(t *testing.T) {
|
||||||
bind := NewStdNetBind().(*StdNetBind)
|
bind := NewStdNetBind().(*StdNetBind)
|
||||||
|
@ -20,3 +26,225 @@ func TestStdNetBindReceiveFuncAfterClose(t *testing.T) {
|
||||||
fn(bufs, sizes, eps)
|
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
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
@ -17,7 +17,7 @@ import (
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn/winrio"
|
"github.com/amnezia-vpn/amneziawg-go/conn/winrio"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package bindtest
|
package bindtest
|
||||||
|
@ -12,7 +12,7 @@ import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChannelBind struct {
|
type ChannelBind struct {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* 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.
|
// Package conn implements WireGuard's network connections.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
@ -13,6 +13,35 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"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() {
|
func init() {
|
||||||
controlFns = append(controlFns,
|
controlFns = append(controlFns,
|
||||||
|
|
||||||
|
@ -57,5 +86,24 @@ func init() {
|
||||||
}
|
}
|
||||||
return err
|
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
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
12
conn/errors_default.go
Normal file
12
conn/errors_default.go
Normal file
|
@ -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
|
||||||
|
}
|
28
conn/errors_linux.go
Normal file
28
conn/errors_linux.go
Normal file
|
@ -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
|
||||||
|
}
|
15
conn/features_default.go
Normal file
15
conn/features_default.go
Normal file
|
@ -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
|
||||||
|
}
|
31
conn/features_linux.go
Normal file
31
conn/features_linux.go
Normal file
|
@ -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
|
||||||
|
}
|
21
conn/gso_default.go
Normal file
21
conn/gso_default.go
Normal file
|
@ -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
|
65
conn/gso_linux.go
Normal file
65
conn/gso_linux.go
Normal file
|
@ -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
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
@ -21,8 +21,9 @@ func (e *StdNetEndpoint) SrcToString() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: macOS, FreeBSD and other BSDs likely do support this feature set, but
|
// TODO: macOS, FreeBSD and other BSDs likely do support the sticky sockets
|
||||||
// use alternatively named flags and need ports and require testing.
|
// {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
|
// getSrcFromControl parses the control for PKTINFO and if found updates ep with
|
||||||
// the source information found.
|
// the source information found.
|
||||||
|
@ -34,8 +35,8 @@ func getSrcFromControl(control []byte, ep *StdNetEndpoint) {
|
||||||
func setSrcControl(control *[]byte, ep *StdNetEndpoint) {
|
func setSrcControl(control *[]byte, ep *StdNetEndpoint) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// srcControlSize returns the recommended buffer size for pooling sticky control
|
// stickyControlSize returns the recommended buffer size for pooling sticky
|
||||||
// data.
|
// offloading control data.
|
||||||
const srcControlSize = 0
|
const stickyControlSize = 0
|
||||||
|
|
||||||
const StdNetSupportsStickySockets = false
|
const StdNetSupportsStickySockets = false
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
@ -105,6 +105,8 @@ func setSrcControl(control *[]byte, ep *StdNetEndpoint) {
|
||||||
*control = append(*control, ep.src...)
|
*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
|
const StdNetSupportsStickySockets = true
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package conn
|
package conn
|
||||||
|
@ -60,7 +60,7 @@ func Test_setSrcControl(t *testing.T) {
|
||||||
}
|
}
|
||||||
setSrc(ep, netip.MustParseAddr("127.0.0.1"), 5)
|
setSrc(ep, netip.MustParseAddr("127.0.0.1"), 5)
|
||||||
|
|
||||||
control := make([]byte, srcControlSize)
|
control := make([]byte, stickyControlSize)
|
||||||
|
|
||||||
setSrcControl(&control, ep)
|
setSrcControl(&control, ep)
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ func Test_setSrcControl(t *testing.T) {
|
||||||
}
|
}
|
||||||
setSrc(ep, netip.MustParseAddr("::1"), 5)
|
setSrc(ep, netip.MustParseAddr("::1"), 5)
|
||||||
|
|
||||||
control := make([]byte, srcControlSize)
|
control := make([]byte, stickyControlSize)
|
||||||
|
|
||||||
setSrcControl(&control, ep)
|
setSrcControl(&control, ep)
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ func Test_setSrcControl(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ClearOnNoSrc", func(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 := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
|
||||||
hdr.Level = 1
|
hdr.Level = 1
|
||||||
hdr.Type = 2
|
hdr.Type = 2
|
||||||
|
@ -129,7 +129,7 @@ func Test_setSrcControl(t *testing.T) {
|
||||||
|
|
||||||
func Test_getSrcFromControl(t *testing.T) {
|
func Test_getSrcFromControl(t *testing.T) {
|
||||||
t.Run("IPv4", func(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 := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
|
||||||
hdr.Level = unix.IPPROTO_IP
|
hdr.Level = unix.IPPROTO_IP
|
||||||
hdr.Type = unix.IP_PKTINFO
|
hdr.Type = unix.IP_PKTINFO
|
||||||
|
@ -149,7 +149,7 @@ func Test_getSrcFromControl(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("IPv6", func(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 := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
|
||||||
hdr.Level = unix.IPPROTO_IPV6
|
hdr.Level = unix.IPPROTO_IPV6
|
||||||
hdr.Type = unix.IPV6_PKTINFO
|
hdr.Type = unix.IPV6_PKTINFO
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package winrio
|
package winrio
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
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) {
|
func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||||
table.mutex.Lock()
|
table.mutex.Lock()
|
||||||
defer table.mutex.Unlock()
|
defer table.mutex.Unlock()
|
||||||
|
@ -230,38 +284,7 @@ func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||||
var next *list.Element
|
var next *list.Element
|
||||||
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
|
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
|
||||||
next = elem.Next()
|
next = elem.Next()
|
||||||
node := elem.Value.(*trieEntry)
|
elem.Value.(*trieEntry).remove()
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -83,7 +83,7 @@ func TestTrieRandom(t *testing.T) {
|
||||||
var peers []*Peer
|
var peers []*Peer
|
||||||
var allowedIPs AllowedIPs
|
var allowedIPs AllowedIPs
|
||||||
|
|
||||||
rand.Seed(1)
|
rng := rand.New(rand.NewSource(1))
|
||||||
|
|
||||||
for n := 0; n < NumberOfPeers; n++ {
|
for n := 0; n < NumberOfPeers; n++ {
|
||||||
peers = append(peers, &Peer{})
|
peers = append(peers, &Peer{})
|
||||||
|
@ -91,14 +91,14 @@ func TestTrieRandom(t *testing.T) {
|
||||||
|
|
||||||
for n := 0; n < NumberOfAddresses; n++ {
|
for n := 0; n < NumberOfAddresses; n++ {
|
||||||
var addr4 [4]byte
|
var addr4 [4]byte
|
||||||
rand.Read(addr4[:])
|
rng.Read(addr4[:])
|
||||||
cidr := uint8(rand.Intn(32) + 1)
|
cidr := uint8(rand.Intn(32) + 1)
|
||||||
index := rand.Intn(NumberOfPeers)
|
index := rand.Intn(NumberOfPeers)
|
||||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4(addr4), int(cidr)), peers[index])
|
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4(addr4), int(cidr)), peers[index])
|
||||||
slow4 = slow4.Insert(addr4[:], cidr, peers[index])
|
slow4 = slow4.Insert(addr4[:], cidr, peers[index])
|
||||||
|
|
||||||
var addr6 [16]byte
|
var addr6 [16]byte
|
||||||
rand.Read(addr6[:])
|
rng.Read(addr6[:])
|
||||||
cidr = uint8(rand.Intn(128) + 1)
|
cidr = uint8(rand.Intn(128) + 1)
|
||||||
index = rand.Intn(NumberOfPeers)
|
index = rand.Intn(NumberOfPeers)
|
||||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(addr6), int(cidr)), peers[index])
|
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 p = 0; ; p++ {
|
||||||
for n := 0; n < NumberOfTests; n++ {
|
for n := 0; n < NumberOfTests; n++ {
|
||||||
var addr4 [4]byte
|
var addr4 [4]byte
|
||||||
rand.Read(addr4[:])
|
rng.Read(addr4[:])
|
||||||
peer1 := slow4.Lookup(addr4[:])
|
peer1 := slow4.Lookup(addr4[:])
|
||||||
peer2 := allowedIPs.Lookup(addr4[:])
|
peer2 := allowedIPs.Lookup(addr4[:])
|
||||||
if peer1 != peer2 {
|
if peer1 != peer2 {
|
||||||
|
@ -117,7 +117,7 @@ func TestTrieRandom(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var addr6 [16]byte
|
var addr6 [16]byte
|
||||||
rand.Read(addr6[:])
|
rng.Read(addr6[:])
|
||||||
peer1 = slow6.Lookup(addr6[:])
|
peer1 = slow6.Lookup(addr6[:])
|
||||||
peer2 = allowedIPs.Lookup(addr6[:])
|
peer2 = allowedIPs.Lookup(addr6[:])
|
||||||
if peer1 != peer2 {
|
if peer1 != peer2 {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
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 trie *trieEntry
|
||||||
var peers []*Peer
|
var peers []*Peer
|
||||||
root := parentIndirection{&trie, 2}
|
root := parentIndirection{&trie, 2}
|
||||||
|
|
||||||
rand.Seed(1)
|
rng := rand.New(rand.NewSource(1))
|
||||||
|
|
||||||
const AddressLength = 4
|
const AddressLength = 4
|
||||||
|
|
||||||
|
@ -54,15 +54,15 @@ func benchmarkTrie(peerNumber, addressNumber, addressLength int, b *testing.B) {
|
||||||
|
|
||||||
for n := 0; n < addressNumber; n++ {
|
for n := 0; n < addressNumber; n++ {
|
||||||
var addr [AddressLength]byte
|
var addr [AddressLength]byte
|
||||||
rand.Read(addr[:])
|
rng.Read(addr[:])
|
||||||
cidr := uint8(rand.Uint32() % (AddressLength * 8))
|
cidr := uint8(rng.Uint32() % (AddressLength * 8))
|
||||||
index := rand.Int() % peerNumber
|
index := rng.Int() % peerNumber
|
||||||
root.insert(addr[:], cidr, peers[index])
|
root.insert(addr[:], cidr, peers[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
var addr [AddressLength]byte
|
var addr [AddressLength]byte
|
||||||
rand.Read(addr[:])
|
rng.Read(addr[:])
|
||||||
trie.lookup(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)
|
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) {
|
assertEQ := func(peer *Peer, a, b, c, d byte) {
|
||||||
p := allowedIPs.Lookup([]byte{a, b, c, d})
|
p := allowedIPs.Lookup([]byte{a, b, c, d})
|
||||||
if p != peer {
|
if p != peer {
|
||||||
|
@ -176,6 +180,21 @@ func TestTrieIPv4(t *testing.T) {
|
||||||
allowedIPs.RemoveByPeer(a)
|
allowedIPs.RemoveByPeer(a)
|
||||||
|
|
||||||
assertNEQ(a, 192, 168, 0, 1)
|
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:
|
/* 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)
|
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) {
|
assertEQ := func(peer *Peer, a, b, c, d uint32) {
|
||||||
var addr []byte
|
var addr []byte
|
||||||
addr = append(addr, expand(a)...)
|
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(d, 0x26075300, 0x60006b00, 0, 0xc05f0543, 128)
|
||||||
insert(c, 0x26075300, 0x60006b00, 0, 0, 64)
|
insert(c, 0x26075300, 0x60006b00, 0, 0, 64)
|
||||||
insert(e, 0, 0, 0, 0, 0)
|
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, 0, 0)
|
||||||
assertEQ(h, 0x24046800, 0x40040800, 0x10101010, 0x10101010)
|
assertEQ(h, 0x24046800, 0x40040800, 0x10101010, 0x10101010)
|
||||||
assertEQ(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef)
|
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)
|
||||||
}
|
}
|
||||||
|
|
144
device/awg/awg.go
Normal file
144
device/awg/awg.go
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/tevino/abool"
|
||||||
|
)
|
||||||
|
|
||||||
|
type aSecCfgType struct {
|
||||||
|
IsSet bool
|
||||||
|
JunkPacketCount int
|
||||||
|
JunkPacketMinSize int
|
||||||
|
JunkPacketMaxSize int
|
||||||
|
InitHeaderJunkSize int
|
||||||
|
ResponseHeaderJunkSize int
|
||||||
|
CookieReplyHeaderJunkSize int
|
||||||
|
TransportHeaderJunkSize int
|
||||||
|
InitPacketMagicHeader uint32
|
||||||
|
ResponsePacketMagicHeader uint32
|
||||||
|
UnderloadPacketMagicHeader uint32
|
||||||
|
TransportPacketMagicHeader uint32
|
||||||
|
// InitPacketMagicHeader Limit
|
||||||
|
// ResponsePacketMagicHeader Limit
|
||||||
|
// UnderloadPacketMagicHeader Limit
|
||||||
|
// TransportPacketMagicHeader Limit
|
||||||
|
}
|
||||||
|
|
||||||
|
type Limit struct {
|
||||||
|
Min uint32
|
||||||
|
Max uint32
|
||||||
|
HeaderType uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLimit(min, max, headerType uint32) (Limit, error) {
|
||||||
|
if min > max {
|
||||||
|
return Limit{}, fmt.Errorf("min (%d) cannot be greater than max (%d)", min, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Limit{
|
||||||
|
Min: min,
|
||||||
|
Max: max,
|
||||||
|
HeaderType: headerType,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseMagicHeader(key, value string, defaultHeaderType uint32) (Limit, error) {
|
||||||
|
// tempAwg.ASecCfg.InitPacketMagicHeader, err = awg.NewLimit(uint32(initPacketMagicHeaderMin), uint32(initPacketMagicHeaderMax), DNewLimit(min, max, headerType)efaultMessageInitiationType)
|
||||||
|
// var min, max, headerType uint32
|
||||||
|
// _, err := fmt.Sscanf(value, "%d-%d:%d", &min, &max, &headerType)
|
||||||
|
// if err != nil {
|
||||||
|
// return Limit{}, fmt.Errorf("invalid magic header format: %s", value)
|
||||||
|
// }
|
||||||
|
|
||||||
|
limits := strings.Split(value, "-")
|
||||||
|
if len(limits) != 2 {
|
||||||
|
return Limit{}, fmt.Errorf("invalid format for key: %s; %s", key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
min, err := strconv.ParseUint(limits[0], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return Limit{}, fmt.Errorf("parse min key: %s; value: ; %w", key, limits[0], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
max, err := strconv.ParseUint(limits[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return Limit{}, fmt.Errorf("parse max key: %s; value: ; %w", key, limits[0], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
limit, err := NewLimit(uint32(min), uint32(max), defaultHeaderType)
|
||||||
|
if err != nil {
|
||||||
|
return Limit{}, fmt.Errorf("new lmit key: %s; value: ; %w", key, limits[0], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return limit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Limits []Limit
|
||||||
|
|
||||||
|
func NewLimits(limits []Limit) Limits {
|
||||||
|
slices.SortFunc(limits, func(a, b Limit) int {
|
||||||
|
if a.Min < b.Min {
|
||||||
|
return -1
|
||||||
|
} else if a.Min > b.Min {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return Limits(limits)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Protocol struct {
|
||||||
|
IsASecOn abool.AtomicBool
|
||||||
|
// TODO: revision the need of the mutex
|
||||||
|
ASecMux sync.RWMutex
|
||||||
|
ASecCfg aSecCfgType
|
||||||
|
JunkCreator junkCreator
|
||||||
|
|
||||||
|
HandshakeHandler SpecialHandshakeHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protocol *Protocol) CreateInitHeaderJunk() ([]byte, error) {
|
||||||
|
return protocol.createHeaderJunk(protocol.ASecCfg.InitHeaderJunkSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protocol *Protocol) CreateResponseHeaderJunk() ([]byte, error) {
|
||||||
|
return protocol.createHeaderJunk(protocol.ASecCfg.ResponseHeaderJunkSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protocol *Protocol) CreateCookieReplyHeaderJunk() ([]byte, error) {
|
||||||
|
return protocol.createHeaderJunk(protocol.ASecCfg.CookieReplyHeaderJunkSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protocol *Protocol) CreateTransportHeaderJunk(packetSize int) ([]byte, error) {
|
||||||
|
return protocol.createHeaderJunk(protocol.ASecCfg.TransportHeaderJunkSize, packetSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protocol *Protocol) createHeaderJunk(junkSize int, optExtraSize ...int) ([]byte, error) {
|
||||||
|
extraSize := 0
|
||||||
|
if len(optExtraSize) == 1 {
|
||||||
|
extraSize = optExtraSize[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
var junk []byte
|
||||||
|
protocol.ASecMux.RLock()
|
||||||
|
if junkSize != 0 {
|
||||||
|
buf := make([]byte, 0, junkSize+extraSize)
|
||||||
|
writer := bytes.NewBuffer(buf[:0])
|
||||||
|
err := protocol.JunkCreator.AppendJunk(writer, junkSize)
|
||||||
|
if err != nil {
|
||||||
|
protocol.ASecMux.RUnlock()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
junk = writer.Bytes()
|
||||||
|
}
|
||||||
|
protocol.ASecMux.RUnlock()
|
||||||
|
|
||||||
|
return junk, nil
|
||||||
|
}
|
37
device/awg/internal/mock.go
Normal file
37
device/awg/internal/mock.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
type mockGenerator struct {
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMockGenerator(size int) mockGenerator {
|
||||||
|
return mockGenerator{size: size}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockGenerator) Generate() []byte {
|
||||||
|
return make([]byte, m.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockGenerator) Size() int {
|
||||||
|
return m.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockGenerator) Name() string {
|
||||||
|
return "mock"
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockByteGenerator struct {
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMockByteGenerator(data []byte) mockByteGenerator {
|
||||||
|
return mockByteGenerator{data: data}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bg mockByteGenerator) Generate() []byte {
|
||||||
|
return bg.data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bg mockByteGenerator) Size() int {
|
||||||
|
return len(bg.data)
|
||||||
|
}
|
70
device/awg/junk_creator.go
Normal file
70
device/awg/junk_creator.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
crand "crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
v2 "math/rand/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type junkCreator struct {
|
||||||
|
aSecCfg aSecCfgType
|
||||||
|
cha8Rand *v2.ChaCha8
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: refactor param to only pass the junk related params
|
||||||
|
func NewJunkCreator(aSecCfg aSecCfgType) (junkCreator, error) {
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
_, err := crand.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return junkCreator{}, err
|
||||||
|
}
|
||||||
|
return junkCreator{aSecCfg: aSecCfg, cha8Rand: v2.NewChaCha8([32]byte(buf))}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called with aSecMux RLocked
|
||||||
|
func (jc *junkCreator) CreateJunkPackets(junks *[][]byte) error {
|
||||||
|
if jc.aSecCfg.JunkPacketCount == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for range jc.aSecCfg.JunkPacketCount {
|
||||||
|
packetSize := jc.randomPacketSize()
|
||||||
|
junk, err := jc.randomJunkWithSize(packetSize)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create junk packet: %v", err)
|
||||||
|
}
|
||||||
|
*junks = append(*junks, junk)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called with aSecMux RLocked
|
||||||
|
func (jc *junkCreator) randomPacketSize() int {
|
||||||
|
return int(
|
||||||
|
jc.cha8Rand.Uint64()%uint64(
|
||||||
|
jc.aSecCfg.JunkPacketMaxSize-jc.aSecCfg.JunkPacketMinSize,
|
||||||
|
),
|
||||||
|
) + jc.aSecCfg.JunkPacketMinSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called with aSecMux RLocked
|
||||||
|
func (jc *junkCreator) AppendJunk(writer *bytes.Buffer, size int) error {
|
||||||
|
headerJunk, err := jc.randomJunkWithSize(size)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create header junk: %v", err)
|
||||||
|
}
|
||||||
|
_, err = writer.Write(headerJunk)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write header junk: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called with aSecMux RLocked
|
||||||
|
func (jc *junkCreator) randomJunkWithSize(size int) ([]byte, error) {
|
||||||
|
// TODO: use a memory pool to allocate
|
||||||
|
junk := make([]byte, size)
|
||||||
|
_, err := jc.cha8Rand.Read(junk)
|
||||||
|
return junk, err
|
||||||
|
}
|
115
device/awg/junk_creator_test.go
Normal file
115
device/awg/junk_creator_test.go
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setUpJunkCreator(t *testing.T) (junkCreator, error) {
|
||||||
|
jc, err := NewJunkCreator(aSecCfgType{
|
||||||
|
IsSet: true,
|
||||||
|
JunkPacketCount: 5,
|
||||||
|
JunkPacketMinSize: 500,
|
||||||
|
JunkPacketMaxSize: 1000,
|
||||||
|
InitHeaderJunkSize: 30,
|
||||||
|
ResponseHeaderJunkSize: 40,
|
||||||
|
InitPacketMagicHeader: 123456,
|
||||||
|
ResponsePacketMagicHeader: 67543,
|
||||||
|
UnderloadPacketMagicHeader: 32345,
|
||||||
|
TransportPacketMagicHeader: 123123,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to create junk creator %v", err)
|
||||||
|
return junkCreator{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return jc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_junkCreator_createJunkPackets(t *testing.T) {
|
||||||
|
jc, err := setUpJunkCreator(t)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Run("valid", func(t *testing.T) {
|
||||||
|
got := make([][]byte, 0, jc.aSecCfg.JunkPacketCount)
|
||||||
|
err := jc.CreateJunkPackets(&got)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(
|
||||||
|
"junkCreator.createJunkPackets() = %v; failed",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
for _, junk := range got {
|
||||||
|
key := string(junk)
|
||||||
|
if seen[key] {
|
||||||
|
t.Errorf(
|
||||||
|
"junkCreator.createJunkPackets() = %v, duplicate key: %v",
|
||||||
|
got,
|
||||||
|
junk,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen[key] = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_junkCreator_randomJunkWithSize(t *testing.T) {
|
||||||
|
t.Run("valid", func(t *testing.T) {
|
||||||
|
jc, err := setUpJunkCreator(t)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r1, _ := jc.randomJunkWithSize(10)
|
||||||
|
r2, _ := jc.randomJunkWithSize(10)
|
||||||
|
fmt.Printf("%v\n%v\n", r1, r2)
|
||||||
|
if bytes.Equal(r1, r2) {
|
||||||
|
t.Errorf("same junks %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_junkCreator_randomPacketSize(t *testing.T) {
|
||||||
|
jc, err := setUpJunkCreator(t)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for range [30]struct{}{} {
|
||||||
|
t.Run("valid", func(t *testing.T) {
|
||||||
|
if got := jc.randomPacketSize(); jc.aSecCfg.JunkPacketMinSize > got ||
|
||||||
|
got > jc.aSecCfg.JunkPacketMaxSize {
|
||||||
|
t.Errorf(
|
||||||
|
"junkCreator.randomPacketSize() = %v, not between range [%v,%v]",
|
||||||
|
got,
|
||||||
|
jc.aSecCfg.JunkPacketMinSize,
|
||||||
|
jc.aSecCfg.JunkPacketMaxSize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_junkCreator_appendJunk(t *testing.T) {
|
||||||
|
jc, err := setUpJunkCreator(t)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Run("valid", func(t *testing.T) {
|
||||||
|
s := "apple"
|
||||||
|
buffer := bytes.NewBuffer([]byte(s))
|
||||||
|
err := jc.AppendJunk(buffer, 30)
|
||||||
|
if err != nil &&
|
||||||
|
buffer.Len() != len(s)+30 {
|
||||||
|
t.Error("appendWithJunk() size don't match")
|
||||||
|
}
|
||||||
|
read := make([]byte, 50)
|
||||||
|
buffer.Read(read)
|
||||||
|
fmt.Println(string(read))
|
||||||
|
})
|
||||||
|
}
|
73
device/awg/special_handshake_handler.go
Normal file
73
device/awg/special_handshake_handler.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tevino/abool"
|
||||||
|
"go.uber.org/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: atomic?/ and better way to use this
|
||||||
|
var PacketCounter *atomic.Uint64 = atomic.NewUint64(0)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
var WaitResponse = struct {
|
||||||
|
Channel chan struct{}
|
||||||
|
ShouldWait *abool.AtomicBool
|
||||||
|
}{
|
||||||
|
make(chan struct{}, 1),
|
||||||
|
abool.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpecialHandshakeHandler struct {
|
||||||
|
isFirstDone bool
|
||||||
|
SpecialJunk TagJunkPacketGenerators
|
||||||
|
ControlledJunk TagJunkPacketGenerators
|
||||||
|
|
||||||
|
nextItime time.Time
|
||||||
|
ITimeout time.Duration // seconds
|
||||||
|
|
||||||
|
IsSet bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *SpecialHandshakeHandler) Validate() error {
|
||||||
|
var errs []error
|
||||||
|
if err := handler.SpecialJunk.Validate(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
if err := handler.ControlledJunk.Validate(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
return errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *SpecialHandshakeHandler) GenerateSpecialJunk() [][]byte {
|
||||||
|
if !handler.SpecialJunk.IsDefined() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: create tests
|
||||||
|
if !handler.isFirstDone {
|
||||||
|
handler.isFirstDone = true
|
||||||
|
} else if !handler.isTimeToSendSpecial() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rv := handler.SpecialJunk.GeneratePackets()
|
||||||
|
handler.nextItime = time.Now().Add(handler.ITimeout)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *SpecialHandshakeHandler) isTimeToSendSpecial() bool {
|
||||||
|
return time.Now().After(handler.nextItime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler *SpecialHandshakeHandler) GenerateControlledJunk() [][]byte {
|
||||||
|
if !handler.ControlledJunk.IsDefined() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler.ControlledJunk.GeneratePackets()
|
||||||
|
}
|
190
device/awg/tag_generator.go
Normal file
190
device/awg/tag_generator.go
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
crand "crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
v2 "math/rand/v2"
|
||||||
|
// "go.uber.org/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Generator interface {
|
||||||
|
Generate() []byte
|
||||||
|
Size() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type newGenerator func(string) (Generator, error)
|
||||||
|
|
||||||
|
type BytesGenerator struct {
|
||||||
|
value []byte
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bg *BytesGenerator) Generate() []byte {
|
||||||
|
return bg.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bg *BytesGenerator) Size() int {
|
||||||
|
return bg.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBytesGenerator(param string) (Generator, error) {
|
||||||
|
hasPrefix := strings.HasPrefix(param, "0x") || strings.HasPrefix(param, "0X")
|
||||||
|
if !hasPrefix {
|
||||||
|
return nil, fmt.Errorf("not correct hex: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
hex, err := hexToBytes(param)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("hexToBytes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BytesGenerator{value: hex, size: len(hex)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hexToBytes(hexStr string) ([]byte, error) {
|
||||||
|
hexStr = strings.TrimPrefix(hexStr, "0x")
|
||||||
|
hexStr = strings.TrimPrefix(hexStr, "0X")
|
||||||
|
|
||||||
|
// Ensure even length (pad with leading zero if needed)
|
||||||
|
if len(hexStr)%2 != 0 {
|
||||||
|
hexStr = "0" + hexStr
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.DecodeString(hexStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RandomPacketGenerator struct {
|
||||||
|
cha8Rand *v2.ChaCha8
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rpg *RandomPacketGenerator) Generate() []byte {
|
||||||
|
junk := make([]byte, rpg.size)
|
||||||
|
rpg.cha8Rand.Read(junk)
|
||||||
|
return junk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rpg *RandomPacketGenerator) Size() int {
|
||||||
|
return rpg.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRandomPacketGenerator(param string) (Generator, error) {
|
||||||
|
size, err := strconv.Atoi(param)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("random packet parse int: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if size > 1000 {
|
||||||
|
return nil, fmt.Errorf("random packet size must be less than 1000")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
_, err = crand.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("random packet crand read: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RandomPacketGenerator{
|
||||||
|
cha8Rand: v2.NewChaCha8([32]byte(buf)),
|
||||||
|
size: size,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimestampGenerator struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg *TimestampGenerator) Generate() []byte {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(buf, uint64(time.Now().Unix()))
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg *TimestampGenerator) Size() int {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTimestampGenerator(param string) (Generator, error) {
|
||||||
|
if len(param) != 0 {
|
||||||
|
return nil, fmt.Errorf("timestamp param needs to be empty: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TimestampGenerator{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type WaitTimeoutGenerator struct {
|
||||||
|
waitTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wtg *WaitTimeoutGenerator) Generate() []byte {
|
||||||
|
time.Sleep(wtg.waitTimeout)
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wtg *WaitTimeoutGenerator) Size() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWaitTimeoutGenerator(param string) (Generator, error) {
|
||||||
|
timeout, err := strconv.Atoi(param)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("timeout parse int: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout > 5000 {
|
||||||
|
return nil, fmt.Errorf("timeout must be less than 5000ms")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &WaitTimeoutGenerator{
|
||||||
|
waitTimeout: time.Duration(timeout) * time.Millisecond,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketCounterGenerator struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketCounterGenerator) Generate() []byte {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
// TODO: better way to handle counter tag
|
||||||
|
binary.BigEndian.PutUint64(buf, PacketCounter.Load())
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PacketCounterGenerator) Size() int {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPacketCounterGenerator(param string) (Generator, error) {
|
||||||
|
if len(param) != 0 {
|
||||||
|
return nil, fmt.Errorf("packet counter param needs to be empty: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &PacketCounterGenerator{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type WaitResponseGenerator struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WaitResponseGenerator) Generate() []byte {
|
||||||
|
WaitResponse.ShouldWait.Set()
|
||||||
|
<-WaitResponse.Channel
|
||||||
|
WaitResponse.ShouldWait.UnSet()
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WaitResponseGenerator) Size() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWaitResponseGenerator(param string) (Generator, error) {
|
||||||
|
if len(param) != 0 {
|
||||||
|
return nil, fmt.Errorf("wait response param needs to be empty: %s", param)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &WaitResponseGenerator{}, nil
|
||||||
|
}
|
189
device/awg/tag_generator_test.go
Normal file
189
device/awg/tag_generator_test.go
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_newBytesGenerator(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
param string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []byte
|
||||||
|
wantErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
args: args{
|
||||||
|
param: "",
|
||||||
|
},
|
||||||
|
wantErr: fmt.Errorf("not correct hex"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong start",
|
||||||
|
args: args{
|
||||||
|
param: "123456",
|
||||||
|
},
|
||||||
|
wantErr: fmt.Errorf("not correct hex"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not only hex value with X",
|
||||||
|
args: args{
|
||||||
|
param: "0X12345q",
|
||||||
|
},
|
||||||
|
wantErr: fmt.Errorf("not correct hex"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not only hex value with x",
|
||||||
|
args: args{
|
||||||
|
param: "0x12345q",
|
||||||
|
},
|
||||||
|
wantErr: fmt.Errorf("not correct hex"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid hex",
|
||||||
|
args: args{
|
||||||
|
param: "0xf6ab3267fa",
|
||||||
|
},
|
||||||
|
want: []byte{0xf6, 0xab, 0x32, 0x67, 0xfa},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid hex with odd length",
|
||||||
|
args: args{
|
||||||
|
param: "0xfab3267fa",
|
||||||
|
},
|
||||||
|
want: []byte{0xf, 0xab, 0x32, 0x67, 0xfa},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := newBytesGenerator(tt.args.param)
|
||||||
|
|
||||||
|
if tt.wantErr != nil {
|
||||||
|
require.ErrorAs(t, err, &tt.wantErr)
|
||||||
|
require.Nil(t, got)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, got)
|
||||||
|
|
||||||
|
gotValues := got.Generate()
|
||||||
|
require.Equal(t, tt.want, gotValues)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_newRandomPacketGenerator(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
param string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
args: args{
|
||||||
|
param: "",
|
||||||
|
},
|
||||||
|
wantErr: fmt.Errorf("parse int"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not an int",
|
||||||
|
args: args{
|
||||||
|
param: "x",
|
||||||
|
},
|
||||||
|
wantErr: fmt.Errorf("parse int"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "too large",
|
||||||
|
args: args{
|
||||||
|
param: "1001",
|
||||||
|
},
|
||||||
|
wantErr: fmt.Errorf("random packet size must be less than 1000"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid",
|
||||||
|
args: args{
|
||||||
|
param: "12",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := newRandomPacketGenerator(tt.args.param)
|
||||||
|
if tt.wantErr != nil {
|
||||||
|
require.ErrorAs(t, err, &tt.wantErr)
|
||||||
|
require.Nil(t, got)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotNil(t, got)
|
||||||
|
first := got.Generate()
|
||||||
|
|
||||||
|
second := got.Generate()
|
||||||
|
require.NotEqual(t, first, second)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketCounterGenerator(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
param string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid empty param",
|
||||||
|
param: "",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid non-empty param",
|
||||||
|
param: "anything",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc // capture range variable
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
gen, err := newPacketCounterGenerator(tc.param)
|
||||||
|
if tc.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 8, gen.Size())
|
||||||
|
|
||||||
|
// Reset counter to known value for test
|
||||||
|
initialCount := uint64(42)
|
||||||
|
PacketCounter.Store(initialCount)
|
||||||
|
|
||||||
|
output := gen.Generate()
|
||||||
|
require.Equal(t, 8, len(output))
|
||||||
|
|
||||||
|
// Verify counter value in output
|
||||||
|
counterValue := binary.BigEndian.Uint64(output)
|
||||||
|
require.Equal(t, initialCount, counterValue)
|
||||||
|
|
||||||
|
// Increment counter and verify change
|
||||||
|
PacketCounter.Add(1)
|
||||||
|
output = gen.Generate()
|
||||||
|
counterValue = binary.BigEndian.Uint64(output)
|
||||||
|
require.Equal(t, initialCount+1, counterValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
59
device/awg/tag_junk_packet_generator.go
Normal file
59
device/awg/tag_junk_packet_generator.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TagJunkPacketGenerator struct {
|
||||||
|
name string
|
||||||
|
tagValue string
|
||||||
|
|
||||||
|
packetSize int
|
||||||
|
generators []Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTagJunkPacketGenerator(name, tagValue string, size int) TagJunkPacketGenerator {
|
||||||
|
return TagJunkPacketGenerator{
|
||||||
|
name: name,
|
||||||
|
tagValue: tagValue,
|
||||||
|
generators: make([]Generator, 0, size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg *TagJunkPacketGenerator) append(generator Generator) {
|
||||||
|
tg.generators = append(tg.generators, generator)
|
||||||
|
tg.packetSize += generator.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg *TagJunkPacketGenerator) generatePacket() []byte {
|
||||||
|
packet := make([]byte, 0, tg.packetSize)
|
||||||
|
for _, generator := range tg.generators {
|
||||||
|
packet = append(packet, generator.Generate()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg *TagJunkPacketGenerator) Name() string {
|
||||||
|
return tg.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg *TagJunkPacketGenerator) nameIndex() (int, error) {
|
||||||
|
if len(tg.name) != 2 {
|
||||||
|
return 0, fmt.Errorf("name must be 2 character long: %s", tg.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
index, err := strconv.Atoi(tg.name[1:2])
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("name 2 char should be an int %w", err)
|
||||||
|
}
|
||||||
|
return index, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg *TagJunkPacketGenerator) IpcGetFields() IpcFields {
|
||||||
|
return IpcFields{
|
||||||
|
Key: tg.name,
|
||||||
|
Value: tg.tagValue,
|
||||||
|
}
|
||||||
|
}
|
210
device/awg/tag_junk_packet_generator_test.go
Normal file
210
device/awg/tag_junk_packet_generator_test.go
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/amnezia-vpn/amneziawg-go/device/awg/internal"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewTagJunkGenerator(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
genName string
|
||||||
|
size int
|
||||||
|
expected TagJunkPacketGenerator
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Create new generator with empty name",
|
||||||
|
genName: "",
|
||||||
|
size: 0,
|
||||||
|
expected: TagJunkPacketGenerator{
|
||||||
|
name: "",
|
||||||
|
packetSize: 0,
|
||||||
|
generators: make([]Generator, 0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Create new generator with valid name",
|
||||||
|
genName: "T1",
|
||||||
|
size: 0,
|
||||||
|
expected: TagJunkPacketGenerator{
|
||||||
|
name: "T1",
|
||||||
|
packetSize: 0,
|
||||||
|
generators: make([]Generator, 0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Create new generator with non-zero size",
|
||||||
|
genName: "T2",
|
||||||
|
size: 5,
|
||||||
|
expected: TagJunkPacketGenerator{
|
||||||
|
name: "T2",
|
||||||
|
packetSize: 0,
|
||||||
|
generators: make([]Generator, 5),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc // capture range variable
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
result := newTagJunkPacketGenerator(tc.genName, "", tc.size)
|
||||||
|
require.Equal(t, tc.expected.name, result.name)
|
||||||
|
require.Equal(t, tc.expected.packetSize, result.packetSize)
|
||||||
|
require.Equal(t, cap(result.generators), len(tc.expected.generators))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagJunkGeneratorAppend(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
initialState TagJunkPacketGenerator
|
||||||
|
mockSize int
|
||||||
|
expectedLength int
|
||||||
|
expectedSize int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Append to empty generator",
|
||||||
|
initialState: newTagJunkPacketGenerator("T1", "", 0),
|
||||||
|
mockSize: 5,
|
||||||
|
expectedLength: 1,
|
||||||
|
expectedSize: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Append to non-empty generator",
|
||||||
|
initialState: TagJunkPacketGenerator{
|
||||||
|
name: "T2",
|
||||||
|
packetSize: 10,
|
||||||
|
generators: make([]Generator, 2),
|
||||||
|
},
|
||||||
|
mockSize: 7,
|
||||||
|
expectedLength: 3, // 2 existing + 1 new
|
||||||
|
expectedSize: 17, // 10 + 7
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc // capture range variable
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tg := tc.initialState
|
||||||
|
mockGen := internal.NewMockGenerator(tc.mockSize)
|
||||||
|
|
||||||
|
tg.append(mockGen)
|
||||||
|
|
||||||
|
require.Equal(t, tc.expectedLength, len(tg.generators))
|
||||||
|
require.Equal(t, tc.expectedSize, tg.packetSize)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagJunkGeneratorGenerate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Create mock generators for testing
|
||||||
|
mockGen1 := internal.NewMockByteGenerator([]byte{0x01, 0x02})
|
||||||
|
mockGen2 := internal.NewMockByteGenerator([]byte{0x03, 0x04, 0x05})
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
setupGenerator func() TagJunkPacketGenerator
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Generate with empty generators",
|
||||||
|
setupGenerator: func() TagJunkPacketGenerator {
|
||||||
|
return newTagJunkPacketGenerator("T1", "", 0)
|
||||||
|
},
|
||||||
|
expected: []byte{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Generate with single generator",
|
||||||
|
setupGenerator: func() TagJunkPacketGenerator {
|
||||||
|
tg := newTagJunkPacketGenerator("T2", "", 0)
|
||||||
|
tg.append(mockGen1)
|
||||||
|
return tg
|
||||||
|
},
|
||||||
|
expected: []byte{0x01, 0x02},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Generate with multiple generators",
|
||||||
|
setupGenerator: func() TagJunkPacketGenerator {
|
||||||
|
tg := newTagJunkPacketGenerator("T3", "", 0)
|
||||||
|
tg.append(mockGen1)
|
||||||
|
tg.append(mockGen2)
|
||||||
|
return tg
|
||||||
|
},
|
||||||
|
expected: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc // capture range variable
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tg := tc.setupGenerator()
|
||||||
|
result := tg.generatePacket()
|
||||||
|
|
||||||
|
require.Equal(t, tc.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagJunkGeneratorNameIndex(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
generatorName string
|
||||||
|
expectedIndex int
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid name with digit",
|
||||||
|
generatorName: "T5",
|
||||||
|
expectedIndex: 5,
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid name - too short",
|
||||||
|
generatorName: "T",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid name - too long",
|
||||||
|
generatorName: "T55",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid name - non-digit second character",
|
||||||
|
generatorName: "TX",
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
tc := tc // capture range variable
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tg := TagJunkPacketGenerator{name: tc.generatorName}
|
||||||
|
index, err := tg.nameIndex()
|
||||||
|
|
||||||
|
if tc.expectError {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedIndex, index)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
66
device/awg/tag_junk_packet_generators.go
Normal file
66
device/awg/tag_junk_packet_generators.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type TagJunkPacketGenerators struct {
|
||||||
|
tagGenerators []TagJunkPacketGenerator
|
||||||
|
length int
|
||||||
|
DefaultJunkCount int // Jc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (generators *TagJunkPacketGenerators) AppendGenerator(
|
||||||
|
generator TagJunkPacketGenerator,
|
||||||
|
) {
|
||||||
|
generators.tagGenerators = append(generators.tagGenerators, generator)
|
||||||
|
generators.length++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (generators *TagJunkPacketGenerators) IsDefined() bool {
|
||||||
|
return len(generators.tagGenerators) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate that packets were defined consecutively
|
||||||
|
func (generators *TagJunkPacketGenerators) Validate() error {
|
||||||
|
seen := make([]bool, len(generators.tagGenerators))
|
||||||
|
for _, generator := range generators.tagGenerators {
|
||||||
|
index, err := generator.nameIndex()
|
||||||
|
if index > len(generators.tagGenerators) {
|
||||||
|
return fmt.Errorf("junk packet index should be consecutive")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("name index: %w", err)
|
||||||
|
} else {
|
||||||
|
seen[index-1] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, found := range seen {
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("junk packet index should be consecutive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (generators *TagJunkPacketGenerators) GeneratePackets() [][]byte {
|
||||||
|
var rv = make([][]byte, 0, generators.length+generators.DefaultJunkCount)
|
||||||
|
|
||||||
|
for i, tagGenerator := range generators.tagGenerators {
|
||||||
|
rv = append(rv, make([]byte, tagGenerator.packetSize))
|
||||||
|
copy(rv[i], tagGenerator.generatePacket())
|
||||||
|
PacketCounter.Inc()
|
||||||
|
}
|
||||||
|
PacketCounter.Add(uint64(generators.DefaultJunkCount))
|
||||||
|
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tg *TagJunkPacketGenerators) IpcGetFields() []IpcFields {
|
||||||
|
rv := make([]IpcFields, 0, len(tg.tagGenerators))
|
||||||
|
for _, generator := range tg.tagGenerators {
|
||||||
|
rv = append(rv, generator.IpcGetFields())
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv
|
||||||
|
}
|
149
device/awg/tag_junk_packet_generators_test.go
Normal file
149
device/awg/tag_junk_packet_generators_test.go
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/amnezia-vpn/amneziawg-go/device/awg/internal"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTagJunkGeneratorHandlerAppendGenerator(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
generator TagJunkPacketGenerator
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "append single generator",
|
||||||
|
generator: newTagJunkPacketGenerator("t1", "", 10),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
generators := &TagJunkPacketGenerators{}
|
||||||
|
|
||||||
|
// Initial length should be 0
|
||||||
|
require.Equal(t, 0, generators.length)
|
||||||
|
require.Empty(t, generators.tagGenerators)
|
||||||
|
|
||||||
|
// After append, length should be 1 and generator should be added
|
||||||
|
generators.AppendGenerator(tt.generator)
|
||||||
|
require.Equal(t, 1, generators.length)
|
||||||
|
require.Len(t, generators.tagGenerators, 1)
|
||||||
|
require.Equal(t, tt.generator, generators.tagGenerators[0])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagJunkGeneratorHandlerValidate(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
generators []TagJunkPacketGenerator
|
||||||
|
wantErr bool
|
||||||
|
errMsg string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "bad start",
|
||||||
|
generators: []TagJunkPacketGenerator{
|
||||||
|
newTagJunkPacketGenerator("t3", "", 10),
|
||||||
|
newTagJunkPacketGenerator("t4", "", 10),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "junk packet index should be consecutive",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-consecutive indices",
|
||||||
|
generators: []TagJunkPacketGenerator{
|
||||||
|
newTagJunkPacketGenerator("t1", "", 10),
|
||||||
|
newTagJunkPacketGenerator("t3", "", 10), // Missing t2
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "junk packet index should be consecutive",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "consecutive indices",
|
||||||
|
generators: []TagJunkPacketGenerator{
|
||||||
|
newTagJunkPacketGenerator("t1", "", 10),
|
||||||
|
newTagJunkPacketGenerator("t2", "", 10),
|
||||||
|
newTagJunkPacketGenerator("t3", "", 10),
|
||||||
|
newTagJunkPacketGenerator("t4", "", 10),
|
||||||
|
newTagJunkPacketGenerator("t5", "", 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nameIndex error",
|
||||||
|
generators: []TagJunkPacketGenerator{
|
||||||
|
newTagJunkPacketGenerator("error", "", 10),
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "name must be 2 character long",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
generators := &TagJunkPacketGenerators{}
|
||||||
|
for _, gen := range tt.generators {
|
||||||
|
generators.AppendGenerator(gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := generators.Validate()
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), tt.errMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTagJunkGeneratorHandlerGenerate(t *testing.T) {
|
||||||
|
mockByte1 := []byte{0x01, 0x02}
|
||||||
|
mockByte2 := []byte{0x03, 0x04, 0x05}
|
||||||
|
mockGen1 := internal.NewMockByteGenerator(mockByte1)
|
||||||
|
mockGen2 := internal.NewMockByteGenerator(mockByte2)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setupGenerator func() []TagJunkPacketGenerator
|
||||||
|
expected [][]byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "generate with no default junk",
|
||||||
|
setupGenerator: func() []TagJunkPacketGenerator {
|
||||||
|
tg1 := newTagJunkPacketGenerator("t1", "", 0)
|
||||||
|
tg1.append(mockGen1)
|
||||||
|
tg1.append(mockGen2)
|
||||||
|
tg2 := newTagJunkPacketGenerator("t2", "", 0)
|
||||||
|
tg2.append(mockGen2)
|
||||||
|
tg2.append(mockGen1)
|
||||||
|
|
||||||
|
return []TagJunkPacketGenerator{tg1, tg2}
|
||||||
|
},
|
||||||
|
expected: [][]byte{
|
||||||
|
append(mockByte1, mockByte2...),
|
||||||
|
append(mockByte2, mockByte1...),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
generators := &TagJunkPacketGenerators{}
|
||||||
|
tagGenerators := tt.setupGenerator()
|
||||||
|
for _, gen := range tagGenerators {
|
||||||
|
generators.AppendGenerator(gen)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := generators.GeneratePackets()
|
||||||
|
require.Equal(t, result, tt.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
112
device/awg/tag_parser.go
Normal file
112
device/awg/tag_parser.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"maps"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IpcFields struct{ Key, Value string }
|
||||||
|
|
||||||
|
type EnumTag string
|
||||||
|
|
||||||
|
const (
|
||||||
|
BytesEnumTag EnumTag = "b"
|
||||||
|
CounterEnumTag EnumTag = "c"
|
||||||
|
TimestampEnumTag EnumTag = "t"
|
||||||
|
RandomBytesEnumTag EnumTag = "r"
|
||||||
|
WaitTimeoutEnumTag EnumTag = "wt"
|
||||||
|
WaitResponseEnumTag EnumTag = "wr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var generatorCreator = map[EnumTag]newGenerator{
|
||||||
|
BytesEnumTag: newBytesGenerator,
|
||||||
|
CounterEnumTag: newPacketCounterGenerator,
|
||||||
|
TimestampEnumTag: newTimestampGenerator,
|
||||||
|
RandomBytesEnumTag: newRandomPacketGenerator,
|
||||||
|
WaitTimeoutEnumTag: newWaitTimeoutGenerator,
|
||||||
|
// WaitResponseEnumTag: newWaitResponseGenerator,
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper map to determine enumTags are unique
|
||||||
|
var uniqueTags = map[EnumTag]bool{
|
||||||
|
CounterEnumTag: false,
|
||||||
|
TimestampEnumTag: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
Name EnumTag
|
||||||
|
Param string
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTag(input string) (Tag, error) {
|
||||||
|
// Regular expression to match <tagname optional_param>
|
||||||
|
re := regexp.MustCompile(`([a-zA-Z]+)(?:\s+([^>]+))?>`)
|
||||||
|
|
||||||
|
match := re.FindStringSubmatch(input)
|
||||||
|
tag := Tag{
|
||||||
|
Name: EnumTag(match[1]),
|
||||||
|
}
|
||||||
|
if len(match) > 2 && match[2] != "" {
|
||||||
|
tag.Param = strings.TrimSpace(match[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse(name, input string) (TagJunkPacketGenerator, error) {
|
||||||
|
inputSlice := strings.Split(input, "<")
|
||||||
|
if len(inputSlice) <= 1 {
|
||||||
|
return TagJunkPacketGenerator{}, fmt.Errorf("empty input: %s", input)
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueTagCheck := make(map[EnumTag]bool, len(uniqueTags))
|
||||||
|
maps.Copy(uniqueTagCheck, uniqueTags)
|
||||||
|
|
||||||
|
// skip byproduct of split
|
||||||
|
inputSlice = inputSlice[1:]
|
||||||
|
rv := newTagJunkPacketGenerator(name, input, len(inputSlice))
|
||||||
|
for _, inputParam := range inputSlice {
|
||||||
|
if len(inputParam) <= 1 {
|
||||||
|
return TagJunkPacketGenerator{}, fmt.Errorf(
|
||||||
|
"empty tag in input: %s",
|
||||||
|
inputSlice,
|
||||||
|
)
|
||||||
|
} else if strings.Count(inputParam, ">") != 1 {
|
||||||
|
return TagJunkPacketGenerator{}, fmt.Errorf("ill formated input: %s", input)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, _ := parseTag(inputParam)
|
||||||
|
creator, ok := generatorCreator[tag.Name]
|
||||||
|
if !ok {
|
||||||
|
return TagJunkPacketGenerator{}, fmt.Errorf("invalid tag: %s", tag.Name)
|
||||||
|
}
|
||||||
|
if present, ok := uniqueTagCheck[tag.Name]; ok {
|
||||||
|
if present {
|
||||||
|
return TagJunkPacketGenerator{}, fmt.Errorf(
|
||||||
|
"tag %s needs to be unique",
|
||||||
|
tag.Name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
uniqueTagCheck[tag.Name] = true
|
||||||
|
}
|
||||||
|
generator, err := creator(tag.Param)
|
||||||
|
if err != nil {
|
||||||
|
return TagJunkPacketGenerator{}, fmt.Errorf("gen: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle counter tag
|
||||||
|
// if tag.Name == CounterEnumTag {
|
||||||
|
// packetCounter, ok := generator.(*PacketCounterGenerator)
|
||||||
|
// if !ok {
|
||||||
|
// log.Fatalf("packet counter generator expected, got %T", generator)
|
||||||
|
// }
|
||||||
|
// PacketCounter = packetCounter.counter
|
||||||
|
// }
|
||||||
|
|
||||||
|
rv.append(generator)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv, nil
|
||||||
|
}
|
77
device/awg/tag_parser_test.go
Normal file
77
device/awg/tag_parser_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package awg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid name",
|
||||||
|
args: args{name: "apple", input: ""},
|
||||||
|
wantErr: fmt.Errorf("ill formated input"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
args: args{name: "i1", input: ""},
|
||||||
|
wantErr: fmt.Errorf("ill formated input"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "extra >",
|
||||||
|
args: args{name: "i1", input: "<b 0xf6ab3267fa><c>>"},
|
||||||
|
wantErr: fmt.Errorf("ill formated input"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "extra <",
|
||||||
|
args: args{name: "i1", input: "<<b 0xf6ab3267fa><c>"},
|
||||||
|
wantErr: fmt.Errorf("empty tag in input"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty <>",
|
||||||
|
args: args{name: "i1", input: "<><b 0xf6ab3267fa><c>"},
|
||||||
|
wantErr: fmt.Errorf("empty tag in input"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid tag",
|
||||||
|
args: args{name: "i1", input: "<q 0xf6ab3267fa>"},
|
||||||
|
wantErr: fmt.Errorf("invalid tag"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "counter uniqueness violation",
|
||||||
|
args: args{name: "i1", input: "<c><c>"},
|
||||||
|
wantErr: fmt.Errorf("parse tag needs to be unique"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "timestamp uniqueness violation",
|
||||||
|
args: args{name: "i1", input: "<t><t>"},
|
||||||
|
wantErr: fmt.Errorf("parse tag needs to be unique"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid",
|
||||||
|
args: args{input: "<b 0xf6ab3267fa><c><b 0xf6ab><t><r 10><wt 10>"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := Parse(tt.args.name, tt.args.input)
|
||||||
|
|
||||||
|
// TODO: ErrorAs doesn't work as you think
|
||||||
|
if tt.wantErr != nil {
|
||||||
|
require.ErrorAs(t, err, &tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.Nil(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -8,7 +8,7 @@ package device
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DummyDatagram struct {
|
type DummyDatagram struct {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -19,13 +19,13 @@ import (
|
||||||
// call wg.Done to remove the initial reference.
|
// call wg.Done to remove the initial reference.
|
||||||
// When the refcount hits 0, the queue's channel is closed.
|
// When the refcount hits 0, the queue's channel is closed.
|
||||||
type outboundQueue struct {
|
type outboundQueue struct {
|
||||||
c chan *QueueOutboundElement
|
c chan *QueueOutboundElementsContainer
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOutboundQueue() *outboundQueue {
|
func newOutboundQueue() *outboundQueue {
|
||||||
q := &outboundQueue{
|
q := &outboundQueue{
|
||||||
c: make(chan *QueueOutboundElement, QueueOutboundSize),
|
c: make(chan *QueueOutboundElementsContainer, QueueOutboundSize),
|
||||||
}
|
}
|
||||||
q.wg.Add(1)
|
q.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -37,13 +37,13 @@ func newOutboundQueue() *outboundQueue {
|
||||||
|
|
||||||
// A inboundQueue is similar to an outboundQueue; see those docs.
|
// A inboundQueue is similar to an outboundQueue; see those docs.
|
||||||
type inboundQueue struct {
|
type inboundQueue struct {
|
||||||
c chan *QueueInboundElement
|
c chan *QueueInboundElementsContainer
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInboundQueue() *inboundQueue {
|
func newInboundQueue() *inboundQueue {
|
||||||
q := &inboundQueue{
|
q := &inboundQueue{
|
||||||
c: make(chan *QueueInboundElement, QueueInboundSize),
|
c: make(chan *QueueInboundElementsContainer, QueueInboundSize),
|
||||||
}
|
}
|
||||||
q.wg.Add(1)
|
q.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -72,7 +72,7 @@ func newHandshakeQueue() *handshakeQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
type autodrainingInboundQueue struct {
|
type autodrainingInboundQueue struct {
|
||||||
c chan *[]*QueueInboundElement
|
c chan *QueueInboundElementsContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAutodrainingInboundQueue returns a channel that will be drained when it gets GC'd.
|
// 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.
|
// some other means, such as sending a sentinel nil values.
|
||||||
func newAutodrainingInboundQueue(device *Device) *autodrainingInboundQueue {
|
func newAutodrainingInboundQueue(device *Device) *autodrainingInboundQueue {
|
||||||
q := &autodrainingInboundQueue{
|
q := &autodrainingInboundQueue{
|
||||||
c: make(chan *[]*QueueInboundElement, QueueInboundSize),
|
c: make(chan *QueueInboundElementsContainer, QueueInboundSize),
|
||||||
}
|
}
|
||||||
runtime.SetFinalizer(q, device.flushInboundQueue)
|
runtime.SetFinalizer(q, device.flushInboundQueue)
|
||||||
return q
|
return q
|
||||||
|
@ -90,13 +90,13 @@ func newAutodrainingInboundQueue(device *Device) *autodrainingInboundQueue {
|
||||||
func (device *Device) flushInboundQueue(q *autodrainingInboundQueue) {
|
func (device *Device) flushInboundQueue(q *autodrainingInboundQueue) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case elems := <-q.c:
|
case elemsContainer := <-q.c:
|
||||||
for _, elem := range *elems {
|
elemsContainer.Lock()
|
||||||
elem.Lock()
|
for _, elem := range elemsContainer.elems {
|
||||||
device.PutMessageBuffer(elem.buffer)
|
device.PutMessageBuffer(elem.buffer)
|
||||||
device.PutInboundElement(elem)
|
device.PutInboundElement(elem)
|
||||||
}
|
}
|
||||||
device.PutInboundElementsSlice(elems)
|
device.PutInboundElementsContainer(elemsContainer)
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ func (device *Device) flushInboundQueue(q *autodrainingInboundQueue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type autodrainingOutboundQueue struct {
|
type autodrainingOutboundQueue struct {
|
||||||
c chan *[]*QueueOutboundElement
|
c chan *QueueOutboundElementsContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAutodrainingOutboundQueue returns a channel that will be drained when it gets GC'd.
|
// 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.
|
// All sends to the channel must be best-effort, because there may be no receivers.
|
||||||
func newAutodrainingOutboundQueue(device *Device) *autodrainingOutboundQueue {
|
func newAutodrainingOutboundQueue(device *Device) *autodrainingOutboundQueue {
|
||||||
q := &autodrainingOutboundQueue{
|
q := &autodrainingOutboundQueue{
|
||||||
c: make(chan *[]*QueueOutboundElement, QueueOutboundSize),
|
c: make(chan *QueueOutboundElementsContainer, QueueOutboundSize),
|
||||||
}
|
}
|
||||||
runtime.SetFinalizer(q, device.flushOutboundQueue)
|
runtime.SetFinalizer(q, device.flushOutboundQueue)
|
||||||
return q
|
return q
|
||||||
|
@ -123,13 +123,13 @@ func newAutodrainingOutboundQueue(device *Device) *autodrainingOutboundQueue {
|
||||||
func (device *Device) flushOutboundQueue(q *autodrainingOutboundQueue) {
|
func (device *Device) flushOutboundQueue(q *autodrainingOutboundQueue) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case elems := <-q.c:
|
case elemsContainer := <-q.c:
|
||||||
for _, elem := range *elems {
|
elemsContainer.Lock()
|
||||||
elem.Lock()
|
for _, elem := range elemsContainer.elems {
|
||||||
device.PutMessageBuffer(elem.buffer)
|
device.PutMessageBuffer(elem.buffer)
|
||||||
device.PutOutboundElement(elem)
|
device.PutOutboundElement(elem)
|
||||||
}
|
}
|
||||||
device.PutOutboundElementsSlice(elems)
|
device.PutOutboundElementsContainer(elemsContainer)
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
479
device/device.go
479
device/device.go
|
@ -1,24 +1,60 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/ipc"
|
"github.com/amnezia-vpn/amneziawg-go/device/awg"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/ratelimiter"
|
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/rwcancel"
|
"github.com/amnezia-vpn/amneziawg-go/ratelimiter"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tun"
|
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||||
"github.com/tevino/abool/v2"
|
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Version uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
VersionDefault Version = iota
|
||||||
|
VersionAwg
|
||||||
|
VersionAwgSpecialHandshake
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
type AtomicVersion struct {
|
||||||
|
value atomic.Uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAtomicVersion(v Version) *AtomicVersion {
|
||||||
|
av := &AtomicVersion{}
|
||||||
|
av.Store(v)
|
||||||
|
return av
|
||||||
|
}
|
||||||
|
|
||||||
|
func (av *AtomicVersion) Load() Version {
|
||||||
|
return Version(av.value.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (av *AtomicVersion) Store(v Version) {
|
||||||
|
av.value.Store(uint32(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (av *AtomicVersion) CompareAndSwap(old, new Version) bool {
|
||||||
|
return av.value.CompareAndSwap(uint32(old), uint32(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (av *AtomicVersion) Swap(new Version) Version {
|
||||||
|
return Version(av.value.Swap(uint32(new)))
|
||||||
|
}
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
state struct {
|
state struct {
|
||||||
// state holds the device's state. It is accessed atomically.
|
// state holds the device's state. It is accessed atomically.
|
||||||
|
@ -70,11 +106,11 @@ type Device struct {
|
||||||
cookieChecker CookieChecker
|
cookieChecker CookieChecker
|
||||||
|
|
||||||
pool struct {
|
pool struct {
|
||||||
outboundElementsSlice *WaitPool
|
inboundElementsContainer *WaitPool
|
||||||
inboundElementsSlice *WaitPool
|
outboundElementsContainer *WaitPool
|
||||||
messageBuffers *WaitPool
|
messageBuffers *WaitPool
|
||||||
inboundElements *WaitPool
|
inboundElements *WaitPool
|
||||||
outboundElements *WaitPool
|
outboundElements *WaitPool
|
||||||
}
|
}
|
||||||
|
|
||||||
queue struct {
|
queue struct {
|
||||||
|
@ -92,21 +128,8 @@ type Device struct {
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
log *Logger
|
log *Logger
|
||||||
|
|
||||||
isASecOn abool.AtomicBool
|
version Version
|
||||||
aSecMux sync.RWMutex
|
awg awg.Protocol
|
||||||
aSecCfg aSecCfgType
|
|
||||||
}
|
|
||||||
|
|
||||||
type aSecCfgType struct {
|
|
||||||
junkPacketCount int
|
|
||||||
junkPacketMinSize int
|
|
||||||
junkPacketMaxSize int
|
|
||||||
initPacketJunkSize int
|
|
||||||
responsePacketJunkSize int
|
|
||||||
initPacketMagicHeader uint32
|
|
||||||
responsePacketMagicHeader uint32
|
|
||||||
underloadPacketMagicHeader uint32
|
|
||||||
transportPacketMagicHeader uint32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// deviceState represents the state of a Device.
|
// deviceState represents the state of a Device.
|
||||||
|
@ -387,10 +410,10 @@ func (device *Device) RemoveAllPeers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) Close() {
|
func (device *Device) Close() {
|
||||||
device.ipcMutex.Lock()
|
|
||||||
defer device.ipcMutex.Unlock()
|
|
||||||
device.state.Lock()
|
device.state.Lock()
|
||||||
defer device.state.Unlock()
|
defer device.state.Unlock()
|
||||||
|
device.ipcMutex.Lock()
|
||||||
|
defer device.ipcMutex.Unlock()
|
||||||
if device.isClosed() {
|
if device.isClosed() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -414,6 +437,8 @@ func (device *Device) Close() {
|
||||||
|
|
||||||
device.rate.limiter.Close()
|
device.rate.limiter.Close()
|
||||||
|
|
||||||
|
device.resetProtocol()
|
||||||
|
|
||||||
device.log.Verbosef("Device closed")
|
device.log.Verbosef("Device closed")
|
||||||
close(device.closed)
|
close(device.closed)
|
||||||
}
|
}
|
||||||
|
@ -480,11 +505,7 @@ func (device *Device) BindSetMark(mark uint32) error {
|
||||||
// clear cached source addresses
|
// clear cached source addresses
|
||||||
device.peers.RLock()
|
device.peers.RLock()
|
||||||
for _, peer := range device.peers.keyMap {
|
for _, peer := range device.peers.keyMap {
|
||||||
peer.Lock()
|
peer.markEndpointSrcForClearing()
|
||||||
defer peer.Unlock()
|
|
||||||
if peer.endpoint != nil {
|
|
||||||
peer.endpoint.ClearSrc()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
device.peers.RUnlock()
|
device.peers.RUnlock()
|
||||||
|
|
||||||
|
@ -534,18 +555,14 @@ func (device *Device) BindUpdate() error {
|
||||||
// clear cached source addresses
|
// clear cached source addresses
|
||||||
device.peers.RLock()
|
device.peers.RLock()
|
||||||
for _, peer := range device.peers.keyMap {
|
for _, peer := range device.peers.keyMap {
|
||||||
peer.Lock()
|
peer.markEndpointSrcForClearing()
|
||||||
defer peer.Unlock()
|
|
||||||
if peer.endpoint != nil {
|
|
||||||
peer.endpoint.ClearSrc()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
device.peers.RUnlock()
|
device.peers.RUnlock()
|
||||||
|
|
||||||
// start receiving routines
|
// start receiving routines
|
||||||
device.net.stopping.Add(len(recvFns))
|
device.net.stopping.Add(len(recvFns))
|
||||||
device.queue.decryption.wg.Add(len(recvFns)) // each RoutineReceiveIncoming goroutine writes to device.queue.decryption
|
device.queue.decryption.wg.Add(len(recvFns)) // each RoutineReceiveIncoming goroutine writes to device.queue.decryption
|
||||||
device.queue.handshake.wg.Add(len(recvFns)) // each RoutineReceiveIncoming goroutine writes to device.queue.handshake
|
device.queue.handshake.wg.Add(len(recvFns)) // each RoutineReceiveIncoming goroutine writes to device.queue.handshake
|
||||||
batchSize := netc.bind.BatchSize()
|
batchSize := netc.bind.BatchSize()
|
||||||
for _, fn := range recvFns {
|
for _, fn := range recvFns {
|
||||||
go device.RoutineReceiveIncoming(batchSize, fn)
|
go device.RoutineReceiveIncoming(batchSize, fn)
|
||||||
|
@ -561,203 +578,261 @@ func (device *Device) BindClose() error {
|
||||||
device.net.Unlock()
|
device.net.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
func (device *Device) isAdvancedSecurityOn() bool {
|
func (device *Device) isAWG() bool {
|
||||||
return device.isASecOn.IsSet()
|
return device.version >= VersionAwg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) handlePostConfig(tempASecCfg *aSecCfgType) (err error) {
|
func (device *Device) resetProtocol() {
|
||||||
|
// restore default message type values
|
||||||
|
MessageInitiationType = DefaultMessageInitiationType
|
||||||
|
MessageResponseType = DefaultMessageResponseType
|
||||||
|
MessageCookieReplyType = DefaultMessageCookieReplyType
|
||||||
|
MessageTransportType = DefaultMessageTransportType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (device *Device) handlePostConfig(tempAwg *awg.Protocol) error {
|
||||||
|
if !tempAwg.ASecCfg.IsSet && !tempAwg.HandshakeHandler.IsSet {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
|
||||||
isASecOn := false
|
isASecOn := false
|
||||||
device.aSecMux.Lock()
|
device.awg.ASecMux.Lock()
|
||||||
if tempASecCfg.junkPacketCount < 0 {
|
if tempAwg.ASecCfg.JunkPacketCount < 0 {
|
||||||
err = ipcErrorf(
|
errs = append(errs, ipcErrorf(
|
||||||
ipc.IpcErrorInvalid,
|
ipc.IpcErrorInvalid,
|
||||||
"JunkPacketCount should be non negative",
|
"JunkPacketCount should be non negative",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
} else if tempASecCfg.junkPacketCount > 0 {
|
}
|
||||||
device.log.Verbosef("UAPI: Updating junk_packet_count")
|
device.awg.ASecCfg.JunkPacketCount = tempAwg.ASecCfg.JunkPacketCount
|
||||||
device.aSecCfg.junkPacketCount = tempASecCfg.junkPacketCount
|
if tempAwg.ASecCfg.JunkPacketCount != 0 {
|
||||||
isASecOn = true
|
isASecOn = true
|
||||||
}
|
}
|
||||||
if tempASecCfg.junkPacketMinSize != 0 {
|
|
||||||
device.log.Verbosef("UAPI: Updating junk_packet_min_size")
|
|
||||||
device.aSecCfg.junkPacketMinSize = tempASecCfg.junkPacketMinSize
|
|
||||||
isASecOn = true
|
|
||||||
}
|
|
||||||
if tempASecCfg.junkPacketMaxSize != 0 {
|
|
||||||
if tempASecCfg.junkPacketMaxSize == tempASecCfg.junkPacketMinSize {
|
|
||||||
tempASecCfg.junkPacketMaxSize++ // to make rand gen work
|
|
||||||
}
|
|
||||||
if tempASecCfg.junkPacketMaxSize >= MaxSegmentSize{
|
|
||||||
device.aSecCfg.junkPacketMinSize = 0
|
|
||||||
device.aSecCfg.junkPacketMaxSize = 1
|
|
||||||
if err != nil {
|
|
||||||
err = ipcErrorf(
|
|
||||||
ipc.IpcErrorInvalid,
|
|
||||||
"JunkPacketMaxSize: %d; should be smaller than maxSegmentSize: %d; %w",
|
|
||||||
tempASecCfg.junkPacketMaxSize,
|
|
||||||
MaxSegmentSize,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
err = ipcErrorf(
|
|
||||||
ipc.IpcErrorInvalid,
|
|
||||||
"JunkPacketMaxSize: %d; should be smaller than maxSegmentSize: %d",
|
|
||||||
tempASecCfg.junkPacketMaxSize,
|
|
||||||
MaxSegmentSize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if tempASecCfg.junkPacketMaxSize < tempASecCfg.junkPacketMinSize {
|
|
||||||
if err != nil {
|
|
||||||
err = ipcErrorf(
|
|
||||||
ipc.IpcErrorInvalid,
|
|
||||||
"maxSize: %d; should be greater than minSize: %d; %w",
|
|
||||||
tempASecCfg.junkPacketMaxSize,
|
|
||||||
tempASecCfg.junkPacketMinSize,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
err = ipcErrorf(
|
|
||||||
ipc.IpcErrorInvalid,
|
|
||||||
"maxSize: %d; should be greater than minSize: %d",
|
|
||||||
tempASecCfg.junkPacketMaxSize,
|
|
||||||
tempASecCfg.junkPacketMinSize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device.log.Verbosef("UAPI: Updating junk_packet_max_size")
|
|
||||||
device.aSecCfg.junkPacketMaxSize = tempASecCfg.junkPacketMaxSize
|
|
||||||
isASecOn = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tempASecCfg.initPacketJunkSize != 0 {
|
|
||||||
if MessageInitiationSize+tempASecCfg.initPacketJunkSize >= MaxSegmentSize {
|
|
||||||
if err != nil {
|
|
||||||
err = ipcErrorf(
|
|
||||||
ipc.IpcErrorInvalid,
|
|
||||||
`init header size(148) + junkSize:%d;
|
|
||||||
should be smaller than maxSegmentSize: %d; %w`,
|
|
||||||
tempASecCfg.initPacketJunkSize,
|
|
||||||
MaxSegmentSize,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
err = ipcErrorf(
|
|
||||||
ipc.IpcErrorInvalid,
|
|
||||||
`init header size(148) + junkSize:%d;
|
|
||||||
should be smaller than maxSegmentSize: %d`,
|
|
||||||
tempASecCfg.initPacketJunkSize,
|
|
||||||
MaxSegmentSize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device.log.Verbosef("UAPI: Updating init_packet_junk_size")
|
|
||||||
device.aSecCfg.initPacketJunkSize = tempASecCfg.initPacketJunkSize
|
|
||||||
isASecOn = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tempASecCfg.responsePacketJunkSize != 0 {
|
|
||||||
if MessageResponseSize+tempASecCfg.responsePacketJunkSize >= MaxSegmentSize {
|
|
||||||
if err != nil {
|
|
||||||
err = ipcErrorf(
|
|
||||||
ipc.IpcErrorInvalid,
|
|
||||||
`response header size(92) + junkSize:%d;
|
|
||||||
should be smaller than maxSegmentSize: %d; %w`,
|
|
||||||
tempASecCfg.responsePacketJunkSize,
|
|
||||||
MaxSegmentSize,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
err = ipcErrorf(
|
|
||||||
ipc.IpcErrorInvalid,
|
|
||||||
`response header size(92) + junkSize:%d;
|
|
||||||
should be smaller than maxSegmentSize: %d`,
|
|
||||||
tempASecCfg.responsePacketJunkSize,
|
|
||||||
MaxSegmentSize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device.log.Verbosef("UAPI: Updating response_packet_junk_size")
|
|
||||||
device.aSecCfg.responsePacketJunkSize = tempASecCfg.responsePacketJunkSize
|
|
||||||
isASecOn = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if tempASecCfg.initPacketMagicHeader > 4 {
|
device.awg.ASecCfg.JunkPacketMinSize = tempAwg.ASecCfg.JunkPacketMinSize
|
||||||
|
if tempAwg.ASecCfg.JunkPacketMinSize != 0 {
|
||||||
|
isASecOn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if device.awg.ASecCfg.JunkPacketCount > 0 &&
|
||||||
|
tempAwg.ASecCfg.JunkPacketMaxSize == tempAwg.ASecCfg.JunkPacketMinSize {
|
||||||
|
|
||||||
|
tempAwg.ASecCfg.JunkPacketMaxSize++ // to make rand gen work
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempAwg.ASecCfg.JunkPacketMaxSize >= MaxSegmentSize {
|
||||||
|
device.awg.ASecCfg.JunkPacketMinSize = 0
|
||||||
|
device.awg.ASecCfg.JunkPacketMaxSize = 1
|
||||||
|
errs = append(errs, ipcErrorf(
|
||||||
|
ipc.IpcErrorInvalid,
|
||||||
|
"JunkPacketMaxSize: %d; should be smaller than maxSegmentSize: %d",
|
||||||
|
tempAwg.ASecCfg.JunkPacketMaxSize,
|
||||||
|
MaxSegmentSize,
|
||||||
|
))
|
||||||
|
} else if tempAwg.ASecCfg.JunkPacketMaxSize < tempAwg.ASecCfg.JunkPacketMinSize {
|
||||||
|
errs = append(errs, ipcErrorf(
|
||||||
|
ipc.IpcErrorInvalid,
|
||||||
|
"maxSize: %d; should be greater than minSize: %d",
|
||||||
|
tempAwg.ASecCfg.JunkPacketMaxSize,
|
||||||
|
tempAwg.ASecCfg.JunkPacketMinSize,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
device.awg.ASecCfg.JunkPacketMaxSize = tempAwg.ASecCfg.JunkPacketMaxSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempAwg.ASecCfg.JunkPacketMaxSize != 0 {
|
||||||
|
isASecOn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
newInitSize := MessageInitiationSize + tempAwg.ASecCfg.InitHeaderJunkSize
|
||||||
|
|
||||||
|
if newInitSize >= MaxSegmentSize {
|
||||||
|
errs = append(errs, ipcErrorf(
|
||||||
|
ipc.IpcErrorInvalid,
|
||||||
|
`init header size(148) + junkSize:%d; should be smaller than maxSegmentSize: %d`,
|
||||||
|
tempAwg.ASecCfg.InitHeaderJunkSize,
|
||||||
|
MaxSegmentSize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
device.awg.ASecCfg.InitHeaderJunkSize = tempAwg.ASecCfg.InitHeaderJunkSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempAwg.ASecCfg.InitHeaderJunkSize != 0 {
|
||||||
|
isASecOn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
newResponseSize := MessageResponseSize + tempAwg.ASecCfg.ResponseHeaderJunkSize
|
||||||
|
|
||||||
|
if newResponseSize >= MaxSegmentSize {
|
||||||
|
errs = append(errs, ipcErrorf(
|
||||||
|
ipc.IpcErrorInvalid,
|
||||||
|
`response header size(92) + junkSize:%d; should be smaller than maxSegmentSize: %d`,
|
||||||
|
tempAwg.ASecCfg.ResponseHeaderJunkSize,
|
||||||
|
MaxSegmentSize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
device.awg.ASecCfg.ResponseHeaderJunkSize = tempAwg.ASecCfg.ResponseHeaderJunkSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempAwg.ASecCfg.ResponseHeaderJunkSize != 0 {
|
||||||
|
isASecOn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
newCookieSize := MessageCookieReplySize + tempAwg.ASecCfg.CookieReplyHeaderJunkSize
|
||||||
|
|
||||||
|
if newCookieSize >= MaxSegmentSize {
|
||||||
|
errs = append(errs, ipcErrorf(
|
||||||
|
ipc.IpcErrorInvalid,
|
||||||
|
`cookie reply size(92) + junkSize:%d; should be smaller than maxSegmentSize: %d`,
|
||||||
|
tempAwg.ASecCfg.CookieReplyHeaderJunkSize,
|
||||||
|
MaxSegmentSize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
device.awg.ASecCfg.CookieReplyHeaderJunkSize = tempAwg.ASecCfg.CookieReplyHeaderJunkSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempAwg.ASecCfg.CookieReplyHeaderJunkSize != 0 {
|
||||||
|
isASecOn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
newTransportSize := MessageTransportSize + tempAwg.ASecCfg.TransportHeaderJunkSize
|
||||||
|
|
||||||
|
if newTransportSize >= MaxSegmentSize {
|
||||||
|
errs = append(errs, ipcErrorf(
|
||||||
|
ipc.IpcErrorInvalid,
|
||||||
|
`transport size(92) + junkSize:%d; should be smaller than maxSegmentSize: %d`,
|
||||||
|
tempAwg.ASecCfg.TransportHeaderJunkSize,
|
||||||
|
MaxSegmentSize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
device.awg.ASecCfg.TransportHeaderJunkSize = tempAwg.ASecCfg.TransportHeaderJunkSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempAwg.ASecCfg.TransportHeaderJunkSize != 0 {
|
||||||
|
isASecOn = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempAwg.ASecCfg.InitPacketMagicHeader > 4 {
|
||||||
isASecOn = true
|
isASecOn = true
|
||||||
device.log.Verbosef("UAPI: Updating init_packet_magic_header")
|
device.log.Verbosef("UAPI: Updating init_packet_magic_header")
|
||||||
device.aSecCfg.initPacketMagicHeader = tempASecCfg.initPacketMagicHeader
|
device.awg.ASecCfg.InitPacketMagicHeader = tempAwg.ASecCfg.InitPacketMagicHeader
|
||||||
MessageInitiationType = device.aSecCfg.initPacketMagicHeader
|
MessageInitiationType = device.awg.ASecCfg.InitPacketMagicHeader
|
||||||
} else {
|
} else {
|
||||||
device.log.Verbosef("UAPI: Using default init type")
|
device.log.Verbosef("UAPI: Using default init type")
|
||||||
MessageInitiationType = 1
|
MessageInitiationType = DefaultMessageInitiationType
|
||||||
}
|
}
|
||||||
if tempASecCfg.responsePacketMagicHeader > 4 {
|
|
||||||
|
if tempAwg.ASecCfg.ResponsePacketMagicHeader > 4 {
|
||||||
isASecOn = true
|
isASecOn = true
|
||||||
device.log.Verbosef("UAPI: Updating response_packet_magic_header")
|
device.log.Verbosef("UAPI: Updating response_packet_magic_header")
|
||||||
device.aSecCfg.responsePacketMagicHeader = tempASecCfg.responsePacketMagicHeader
|
device.awg.ASecCfg.ResponsePacketMagicHeader = tempAwg.ASecCfg.ResponsePacketMagicHeader
|
||||||
MessageResponseType = device.aSecCfg.responsePacketMagicHeader
|
MessageResponseType = device.awg.ASecCfg.ResponsePacketMagicHeader
|
||||||
} else {
|
} else {
|
||||||
device.log.Verbosef("UAPI: Using default response type")
|
device.log.Verbosef("UAPI: Using default response type")
|
||||||
MessageResponseType = 2
|
MessageResponseType = DefaultMessageResponseType
|
||||||
}
|
}
|
||||||
if tempASecCfg.underloadPacketMagicHeader > 4 {
|
|
||||||
|
if tempAwg.ASecCfg.UnderloadPacketMagicHeader > 4 {
|
||||||
isASecOn = true
|
isASecOn = true
|
||||||
device.log.Verbosef("UAPI: Updating underload_packet_magic_header")
|
device.log.Verbosef("UAPI: Updating underload_packet_magic_header")
|
||||||
device.aSecCfg.underloadPacketMagicHeader = tempASecCfg.underloadPacketMagicHeader
|
device.awg.ASecCfg.UnderloadPacketMagicHeader = tempAwg.ASecCfg.UnderloadPacketMagicHeader
|
||||||
MessageCookieReplyType = device.aSecCfg.underloadPacketMagicHeader
|
MessageCookieReplyType = device.awg.ASecCfg.UnderloadPacketMagicHeader
|
||||||
} else {
|
} else {
|
||||||
device.log.Verbosef("UAPI: Using default underload type")
|
device.log.Verbosef("UAPI: Using default underload type")
|
||||||
MessageCookieReplyType = 3
|
MessageCookieReplyType = DefaultMessageCookieReplyType
|
||||||
}
|
}
|
||||||
if tempASecCfg.transportPacketMagicHeader > 4 {
|
|
||||||
|
if tempAwg.ASecCfg.TransportPacketMagicHeader > 4 {
|
||||||
isASecOn = true
|
isASecOn = true
|
||||||
device.log.Verbosef("UAPI: Updating transport_packet_magic_header")
|
device.log.Verbosef("UAPI: Updating transport_packet_magic_header")
|
||||||
device.aSecCfg.transportPacketMagicHeader = tempASecCfg.transportPacketMagicHeader
|
device.awg.ASecCfg.TransportPacketMagicHeader = tempAwg.ASecCfg.TransportPacketMagicHeader
|
||||||
MessageTransportType = device.aSecCfg.transportPacketMagicHeader
|
MessageTransportType = device.awg.ASecCfg.TransportPacketMagicHeader
|
||||||
} else {
|
} else {
|
||||||
device.log.Verbosef("UAPI: Using default transport type")
|
device.log.Verbosef("UAPI: Using default transport type")
|
||||||
|
MessageTransportType = DefaultMessageTransportType
|
||||||
MessageTransportType = 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newInitSize := MessageInitiationSize + device.aSecCfg.initPacketJunkSize
|
isSameHeaderMap := map[uint32]struct{}{
|
||||||
newResponseSize := MessageResponseSize + device.aSecCfg.responsePacketJunkSize
|
MessageInitiationType: {},
|
||||||
|
MessageResponseType: {},
|
||||||
|
MessageCookieReplyType: {},
|
||||||
|
MessageTransportType: {},
|
||||||
|
}
|
||||||
|
|
||||||
if newInitSize == newResponseSize {
|
// size will be different if same values
|
||||||
if err != nil {
|
if len(isSameHeaderMap) != 4 {
|
||||||
err = ipcErrorf(
|
errs = append(errs, ipcErrorf(
|
||||||
ipc.IpcErrorInvalid,
|
ipc.IpcErrorInvalid,
|
||||||
`new init size:%d; and new response size:%d; should differ; %w`,
|
`magic headers should differ; got: init:%d; recv:%d; unde:%d; tran:%d`,
|
||||||
newInitSize,
|
MessageInitiationType,
|
||||||
newResponseSize,
|
MessageResponseType,
|
||||||
err,
|
MessageCookieReplyType,
|
||||||
)
|
MessageTransportType,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
isSameSizeMap := map[int]struct{}{
|
||||||
|
newInitSize: {},
|
||||||
|
newResponseSize: {},
|
||||||
|
newCookieSize: {},
|
||||||
|
newTransportSize: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(isSameSizeMap) != 4 {
|
||||||
|
errs = append(errs, ipcErrorf(
|
||||||
|
ipc.IpcErrorInvalid,
|
||||||
|
`new sizes should differ; init: %d; response: %d; cookie: %d; trans: %d`,
|
||||||
|
newInitSize,
|
||||||
|
newResponseSize,
|
||||||
|
newCookieSize,
|
||||||
|
newTransportSize,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
msgTypeToJunkSize = map[uint32]int{
|
||||||
|
MessageInitiationType: device.awg.ASecCfg.InitHeaderJunkSize,
|
||||||
|
MessageResponseType: device.awg.ASecCfg.ResponseHeaderJunkSize,
|
||||||
|
MessageCookieReplyType: device.awg.ASecCfg.CookieReplyHeaderJunkSize,
|
||||||
|
MessageTransportType: device.awg.ASecCfg.TransportHeaderJunkSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
packetSizeToMsgType = map[int]uint32{
|
||||||
|
newInitSize: MessageInitiationType,
|
||||||
|
newResponseSize: MessageResponseType,
|
||||||
|
newCookieSize: MessageCookieReplyType,
|
||||||
|
newTransportSize: MessageTransportType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device.awg.IsASecOn.SetTo(isASecOn)
|
||||||
|
var err error
|
||||||
|
device.awg.JunkCreator, err = awg.NewJunkCreator(device.awg.ASecCfg)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempAwg.HandshakeHandler.IsSet {
|
||||||
|
if err := tempAwg.HandshakeHandler.Validate(); err != nil {
|
||||||
|
errs = append(errs, ipcErrorf(
|
||||||
|
ipc.IpcErrorInvalid, "handshake handler validate: %w", err))
|
||||||
} else {
|
} else {
|
||||||
err = ipcErrorf(
|
device.awg.HandshakeHandler = tempAwg.HandshakeHandler
|
||||||
ipc.IpcErrorInvalid,
|
device.awg.HandshakeHandler.ControlledJunk.DefaultJunkCount = tempAwg.ASecCfg.JunkPacketCount
|
||||||
`new init size:%d; and new response size:%d; should differ`,
|
device.awg.HandshakeHandler.SpecialJunk.DefaultJunkCount = tempAwg.ASecCfg.JunkPacketCount
|
||||||
newInitSize,
|
device.version = VersionAwgSpecialHandshake
|
||||||
newResponseSize,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
packetSizeToMsgType = map[int]uint32{
|
device.version = VersionAwg
|
||||||
newInitSize: MessageInitiationType,
|
|
||||||
newResponseSize: MessageResponseType,
|
|
||||||
MessageCookieReplySize: MessageCookieReplyType,
|
|
||||||
MessageTransportSize: MessageTransportType,
|
|
||||||
}
|
|
||||||
|
|
||||||
msgTypeToJunkSize = map[uint32]int{
|
|
||||||
MessageInitiationType: device.aSecCfg.initPacketJunkSize,
|
|
||||||
MessageResponseType: device.aSecCfg.responsePacketJunkSize,
|
|
||||||
MessageCookieReplyType: 0,
|
|
||||||
MessageTransportType: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
device.isASecOn.SetTo(isASecOn)
|
device.awg.ASecMux.Unlock()
|
||||||
device.aSecMux.Unlock()
|
|
||||||
|
|
||||||
return nil
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,32 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"go.uber.org/atomic"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn/bindtest"
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tun"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tun/tuntest"
|
"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.
|
// 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.
|
// genConfigs generates a pair of configs that connect to each other.
|
||||||
// The configs use distinct, probably-usable ports.
|
// 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
|
var key1, key2 NoisePrivateKey
|
||||||
_, err := rand.Read(key1[:])
|
_, err := rand.Read(key1[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -62,7 +65,8 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||||
}
|
}
|
||||||
pub1, pub2 := key1.publicKey(), key2.publicKey()
|
pub1, pub2 := key1.publicKey(), key2.publicKey()
|
||||||
|
|
||||||
cfgs[0] = uapiCfg(
|
args0 := append([]string(nil), cfg...)
|
||||||
|
args0 = append(args0, []string{
|
||||||
"private_key", hex.EncodeToString(key1[:]),
|
"private_key", hex.EncodeToString(key1[:]),
|
||||||
"listen_port", "0",
|
"listen_port", "0",
|
||||||
"replace_peers", "true",
|
"replace_peers", "true",
|
||||||
|
@ -70,12 +74,16 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||||
"protocol_version", "1",
|
"protocol_version", "1",
|
||||||
"replace_allowed_ips", "true",
|
"replace_allowed_ips", "true",
|
||||||
"allowed_ip", "1.0.0.2/32",
|
"allowed_ip", "1.0.0.2/32",
|
||||||
)
|
}...)
|
||||||
|
cfgs[0] = uapiCfg(args0...)
|
||||||
|
|
||||||
endpointCfgs[0] = uapiCfg(
|
endpointCfgs[0] = uapiCfg(
|
||||||
"public_key", hex.EncodeToString(pub2[:]),
|
"public_key", hex.EncodeToString(pub2[:]),
|
||||||
"endpoint", "127.0.0.1:%d",
|
"endpoint", "127.0.0.1:%d",
|
||||||
)
|
)
|
||||||
cfgs[1] = uapiCfg(
|
|
||||||
|
args1 := append([]string(nil), cfg...)
|
||||||
|
args1 = append(args1, []string{
|
||||||
"private_key", hex.EncodeToString(key2[:]),
|
"private_key", hex.EncodeToString(key2[:]),
|
||||||
"listen_port", "0",
|
"listen_port", "0",
|
||||||
"replace_peers", "true",
|
"replace_peers", "true",
|
||||||
|
@ -83,66 +91,9 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||||
"protocol_version", "1",
|
"protocol_version", "1",
|
||||||
"replace_allowed_ips", "true",
|
"replace_allowed_ips", "true",
|
||||||
"allowed_ip", "1.0.0.1/32",
|
"allowed_ip", "1.0.0.1/32",
|
||||||
)
|
}...)
|
||||||
endpointCfgs[1] = uapiCfg(
|
|
||||||
"public_key", hex.EncodeToString(pub1[:]),
|
|
||||||
"endpoint", "127.0.0.1:%d",
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func genASecurityConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
cfgs[1] = uapiCfg(args1...)
|
||||||
var key1, key2 NoisePrivateKey
|
|
||||||
_, err := rand.Read(key1[:])
|
|
||||||
if err != nil {
|
|
||||||
tb.Errorf("unable to generate private key random bytes: %v", err)
|
|
||||||
}
|
|
||||||
_, err = rand.Read(key2[:])
|
|
||||||
if err != nil {
|
|
||||||
tb.Errorf("unable to generate private key random bytes: %v", err)
|
|
||||||
}
|
|
||||||
pub1, pub2 := key1.publicKey(), key2.publicKey()
|
|
||||||
|
|
||||||
cfgs[0] = uapiCfg(
|
|
||||||
"private_key", hex.EncodeToString(key1[:]),
|
|
||||||
"listen_port", "0",
|
|
||||||
"replace_peers", "true",
|
|
||||||
"jc", "5",
|
|
||||||
"jmin", "500",
|
|
||||||
"jmax", "501",
|
|
||||||
"s1", "30",
|
|
||||||
"s2", "40",
|
|
||||||
"h1", "123456",
|
|
||||||
"h2", "67543",
|
|
||||||
"h4", "32345",
|
|
||||||
"h3", "123123",
|
|
||||||
"public_key", hex.EncodeToString(pub2[:]),
|
|
||||||
"protocol_version", "1",
|
|
||||||
"replace_allowed_ips", "true",
|
|
||||||
"allowed_ip", "1.0.0.2/32",
|
|
||||||
)
|
|
||||||
endpointCfgs[0] = uapiCfg(
|
|
||||||
"public_key", hex.EncodeToString(pub2[:]),
|
|
||||||
"endpoint", "127.0.0.1:%d",
|
|
||||||
)
|
|
||||||
cfgs[1] = uapiCfg(
|
|
||||||
"private_key", hex.EncodeToString(key2[:]),
|
|
||||||
"listen_port", "0",
|
|
||||||
"replace_peers", "true",
|
|
||||||
"jc", "5",
|
|
||||||
"jmin", "500",
|
|
||||||
"jmax", "501",
|
|
||||||
"s1", "30",
|
|
||||||
"s2", "40",
|
|
||||||
"h1", "123456",
|
|
||||||
"h2", "67543",
|
|
||||||
"h4", "32345",
|
|
||||||
"h3", "123123",
|
|
||||||
"public_key", hex.EncodeToString(pub1[:]),
|
|
||||||
"protocol_version", "1",
|
|
||||||
"replace_allowed_ips", "true",
|
|
||||||
"allowed_ip", "1.0.0.1/32",
|
|
||||||
)
|
|
||||||
endpointCfgs[1] = uapiCfg(
|
endpointCfgs[1] = uapiCfg(
|
||||||
"public_key", hex.EncodeToString(pub1[:]),
|
"public_key", hex.EncodeToString(pub1[:]),
|
||||||
"endpoint", "127.0.0.1:%d",
|
"endpoint", "127.0.0.1:%d",
|
||||||
|
@ -185,9 +136,10 @@ func (pair *testPair) Send(
|
||||||
// pong is the new ping
|
// pong is the new ping
|
||||||
p0, p1 = p1, p0
|
p0, p1 = p1, p0
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := tuntest.Ping(p0.ip, p1.ip)
|
msg := tuntest.Ping(p0.ip, p1.ip)
|
||||||
p1.tun.Outbound <- msg
|
p1.tun.Outbound <- msg
|
||||||
timer := time.NewTimer(5 * time.Second)
|
timer := time.NewTimer(6 * time.Second)
|
||||||
defer timer.Stop()
|
defer timer.Stop()
|
||||||
var err error
|
var err error
|
||||||
select {
|
select {
|
||||||
|
@ -214,14 +166,12 @@ func (pair *testPair) Send(
|
||||||
// genTestPair creates a testPair.
|
// genTestPair creates a testPair.
|
||||||
func genTestPair(
|
func genTestPair(
|
||||||
tb testing.TB,
|
tb testing.TB,
|
||||||
realSocket, withASecurity bool,
|
realSocket bool,
|
||||||
|
extraCfg ...string,
|
||||||
) (pair testPair) {
|
) (pair testPair) {
|
||||||
var cfg, endpointCfg [2]string
|
var cfg, endpointCfg [2]string
|
||||||
if withASecurity {
|
cfg, endpointCfg = genConfigs(tb, extraCfg...)
|
||||||
cfg, endpointCfg = genASecurityConfigs(tb)
|
|
||||||
} else {
|
|
||||||
cfg, endpointCfg = genConfigs(tb)
|
|
||||||
}
|
|
||||||
var binds [2]conn.Bind
|
var binds [2]conn.Bind
|
||||||
if realSocket {
|
if realSocket {
|
||||||
binds[0], binds[1] = conn.NewDefaultBind(), conn.NewDefaultBind()
|
binds[0], binds[1] = conn.NewDefaultBind(), conn.NewDefaultBind()
|
||||||
|
@ -237,7 +187,7 @@ func genTestPair(
|
||||||
if _, ok := tb.(*testing.B); ok && !testing.Verbose() {
|
if _, ok := tb.(*testing.B); ok && !testing.Verbose() {
|
||||||
level = LogLevelError
|
level = LogLevelError
|
||||||
}
|
}
|
||||||
p.dev = NewDevice(p.tun.TUN(),binds[i],NewLogger(level, fmt.Sprintf("dev%d: ", i)))
|
p.dev = NewDevice(p.tun.TUN(), binds[i], NewLogger(level, fmt.Sprintf("dev%d: ", i)))
|
||||||
if err := p.dev.IpcSet(cfg[i]); err != nil {
|
if err := p.dev.IpcSet(cfg[i]); err != nil {
|
||||||
tb.Errorf("failed to configure device %d: %v", i, err)
|
tb.Errorf("failed to configure device %d: %v", i, err)
|
||||||
p.dev.Close()
|
p.dev.Close()
|
||||||
|
@ -265,7 +215,7 @@ func genTestPair(
|
||||||
|
|
||||||
func TestTwoDevicePing(t *testing.T) {
|
func TestTwoDevicePing(t *testing.T) {
|
||||||
goroutineLeakCheck(t)
|
goroutineLeakCheck(t)
|
||||||
pair := genTestPair(t, true, false)
|
pair := genTestPair(t, true)
|
||||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||||
pair.Send(t, Ping, nil)
|
pair.Send(t, Ping, nil)
|
||||||
})
|
})
|
||||||
|
@ -274,9 +224,23 @@ func TestTwoDevicePing(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTwoDevicePingASecurity(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)
|
goroutineLeakCheck(t)
|
||||||
pair := genTestPair(t, true, true)
|
|
||||||
|
pair := genTestPair(t, true,
|
||||||
|
"jc", "5",
|
||||||
|
"jmin", "500",
|
||||||
|
"jmax", "1000",
|
||||||
|
"s1", "30",
|
||||||
|
"s2", "40",
|
||||||
|
"s3", "50",
|
||||||
|
"s4", "5",
|
||||||
|
"h1", "123456",
|
||||||
|
"h2", "67543",
|
||||||
|
"h3", "123123",
|
||||||
|
"h4", "32345",
|
||||||
|
)
|
||||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||||
pair.Send(t, Ping, nil)
|
pair.Send(t, Ping, nil)
|
||||||
})
|
})
|
||||||
|
@ -285,16 +249,61 @@ func TestTwoDevicePingASecurity(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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><wt 10>",
|
||||||
|
"i2", "<b 0xf6ab3267fa><r 100>",
|
||||||
|
"j1", "<b 0xffffffff><c><b 0xf6ab><t><r 10>",
|
||||||
|
"j2", "<c><b 0xf6ab><t><wt 1000>",
|
||||||
|
"j3", "<t><b 0xf6ab><c><r 10>",
|
||||||
|
"itime", "60",
|
||||||
|
// "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 TestUpDown(t *testing.T) {
|
func TestUpDown(t *testing.T) {
|
||||||
goroutineLeakCheck(t)
|
goroutineLeakCheck(t)
|
||||||
const itrials = 50
|
const itrials = 50
|
||||||
const otrials = 10
|
const otrials = 10
|
||||||
|
|
||||||
for n := 0; n < otrials; n++ {
|
for n := 0; n < otrials; n++ {
|
||||||
pair := genTestPair(t, false, false)
|
pair := genTestPair(t, false)
|
||||||
for i := range pair {
|
for i := range pair {
|
||||||
for k := range pair[i].dev.peers.keyMap {
|
for k := range pair[i].dev.peers.keyMap {
|
||||||
pair[i].dev.IpcSet(fmt.Sprintf("public_key=%s\npersistent_keepalive_interval=1\n",hex.EncodeToString(k[:])))
|
pair[i].dev.IpcSet(fmt.Sprintf("public_key=%s\npersistent_keepalive_interval=1\n", hex.EncodeToString(k[:])))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
@ -325,7 +334,7 @@ func TestUpDown(t *testing.T) {
|
||||||
// TestConcurrencySafety does other things concurrently with tunnel use.
|
// TestConcurrencySafety does other things concurrently with tunnel use.
|
||||||
// It is intended to be used with the race detector to catch data races.
|
// It is intended to be used with the race detector to catch data races.
|
||||||
func TestConcurrencySafety(t *testing.T) {
|
func TestConcurrencySafety(t *testing.T) {
|
||||||
pair := genTestPair(t, true, false)
|
pair := genTestPair(t, true)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
const warmupIters = 10
|
const warmupIters = 10
|
||||||
|
@ -406,7 +415,7 @@ func TestConcurrencySafety(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkLatency(b *testing.B) {
|
func BenchmarkLatency(b *testing.B) {
|
||||||
pair := genTestPair(b, true, false)
|
pair := genTestPair(b, true)
|
||||||
|
|
||||||
// Establish a connection.
|
// Establish a connection.
|
||||||
pair.Send(b, Ping, nil)
|
pair.Send(b, Ping, nil)
|
||||||
|
@ -420,7 +429,7 @@ func BenchmarkLatency(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkThroughput(b *testing.B) {
|
func BenchmarkThroughput(b *testing.B) {
|
||||||
pair := genTestPair(b, true, false)
|
pair := genTestPair(b, true)
|
||||||
|
|
||||||
// Establish a connection.
|
// Establish a connection.
|
||||||
pair.Send(b, Ping, nil)
|
pair.Send(b, Ping, nil)
|
||||||
|
@ -464,7 +473,7 @@ func BenchmarkThroughput(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkUAPIGet(b *testing.B) {
|
func BenchmarkUAPIGet(b *testing.B) {
|
||||||
pair := genTestPair(b, true, false)
|
pair := genTestPair(b, true)
|
||||||
pair.Send(b, Ping, nil)
|
pair.Send(b, Ping, nil)
|
||||||
pair.Send(b, Pong, nil)
|
pair.Send(b, Pong, nil)
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
@ -513,7 +522,7 @@ func (b *fakeBindSized) Open(
|
||||||
|
|
||||||
func (b *fakeBindSized) Close() error { return nil }
|
func (b *fakeBindSized) Close() error { return nil }
|
||||||
|
|
||||||
func (b *fakeBindSized) SetMark(mark uint32) 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) Send(bufs [][]byte, ep conn.Endpoint) error { return nil }
|
||||||
|
|
||||||
|
@ -527,7 +536,9 @@ type fakeTUNDeviceSized struct {
|
||||||
|
|
||||||
func (t *fakeTUNDeviceSized) File() *os.File { return nil }
|
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) 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) Write(bufs [][]byte, offset int) (int, error) { return 0, nil }
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -11,7 +11,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/replay"
|
"github.com/amnezia-vpn/amneziawg-go/replay"
|
||||||
)
|
)
|
||||||
|
|
||||||
/* Due to limitations in Go and /x/crypto there is currently
|
/* Due to limitations in Go and /x/crypto there is currently
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -11,9 +11,9 @@ func (device *Device) DisableSomeRoamingForBrokenMobileSemantics() {
|
||||||
device.net.brokenRoaming = true
|
device.net.brokenRoaming = true
|
||||||
device.peers.RLock()
|
device.peers.RLock()
|
||||||
for _, peer := range device.peers.keyMap {
|
for _, peer := range device.peers.keyMap {
|
||||||
peer.Lock()
|
peer.endpoint.Lock()
|
||||||
peer.disableRoaming = peer.endpoint != nil
|
peer.endpoint.disableRoaming = peer.endpoint.val != nil
|
||||||
peer.Unlock()
|
peer.endpoint.Unlock()
|
||||||
}
|
}
|
||||||
device.peers.RUnlock()
|
device.peers.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -15,7 +15,7 @@ import (
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/crypto/poly1305"
|
"golang.org/x/crypto/poly1305"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tai64n"
|
"github.com/amnezia-vpn/amneziawg-go/tai64n"
|
||||||
)
|
)
|
||||||
|
|
||||||
type handshakeState int
|
type handshakeState int
|
||||||
|
@ -52,11 +52,18 @@ const (
|
||||||
WGLabelCookie = "cookie--"
|
WGLabelCookie = "cookie--"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultMessageInitiationType uint32 = 1
|
||||||
|
DefaultMessageResponseType uint32 = 2
|
||||||
|
DefaultMessageCookieReplyType uint32 = 3
|
||||||
|
DefaultMessageTransportType uint32 = 4
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
MessageInitiationType uint32 = 1
|
MessageInitiationType uint32 = DefaultMessageInitiationType
|
||||||
MessageResponseType uint32 = 2
|
MessageResponseType uint32 = DefaultMessageResponseType
|
||||||
MessageCookieReplyType uint32 = 3
|
MessageCookieReplyType uint32 = DefaultMessageCookieReplyType
|
||||||
MessageTransportType uint32 = 4
|
MessageTransportType uint32 = DefaultMessageTransportType
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -75,9 +82,10 @@ const (
|
||||||
MessageTransportOffsetContent = 16
|
MessageTransportOffsetContent = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
var packetSizeToMsgType map[int]uint32
|
var (
|
||||||
|
packetSizeToMsgType map[int]uint32
|
||||||
var msgTypeToJunkSize map[uint32]int
|
msgTypeToJunkSize map[uint32]int
|
||||||
|
)
|
||||||
|
|
||||||
/* Type is an 8-bit field, followed by 3 nul bytes,
|
/* Type is an 8-bit field, followed by 3 nul bytes,
|
||||||
* by marshalling the messages in little-endian byteorder
|
* by marshalling the messages in little-endian byteorder
|
||||||
|
@ -197,12 +205,12 @@ func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, e
|
||||||
|
|
||||||
handshake.mixHash(handshake.remoteStatic[:])
|
handshake.mixHash(handshake.remoteStatic[:])
|
||||||
|
|
||||||
device.aSecMux.RLock()
|
device.awg.ASecMux.RLock()
|
||||||
msg := MessageInitiation{
|
msg := MessageInitiation{
|
||||||
Type: MessageInitiationType,
|
Type: MessageInitiationType,
|
||||||
Ephemeral: handshake.localEphemeral.publicKey(),
|
Ephemeral: handshake.localEphemeral.publicKey(),
|
||||||
}
|
}
|
||||||
device.aSecMux.RUnlock()
|
device.awg.ASecMux.RUnlock()
|
||||||
|
|
||||||
handshake.mixKey(msg.Ephemeral[:])
|
handshake.mixKey(msg.Ephemeral[:])
|
||||||
handshake.mixHash(msg.Ephemeral[:])
|
handshake.mixHash(msg.Ephemeral[:])
|
||||||
|
@ -256,12 +264,12 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
|
||||||
chainKey [blake2s.Size]byte
|
chainKey [blake2s.Size]byte
|
||||||
)
|
)
|
||||||
|
|
||||||
device.aSecMux.RLock()
|
device.awg.ASecMux.RLock()
|
||||||
if msg.Type != MessageInitiationType {
|
if msg.Type != MessageInitiationType {
|
||||||
device.aSecMux.RUnlock()
|
device.awg.ASecMux.RUnlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
device.aSecMux.RUnlock()
|
device.awg.ASecMux.RUnlock()
|
||||||
|
|
||||||
device.staticIdentity.RLock()
|
device.staticIdentity.RLock()
|
||||||
defer device.staticIdentity.RUnlock()
|
defer device.staticIdentity.RUnlock()
|
||||||
|
@ -376,9 +384,9 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg MessageResponse
|
var msg MessageResponse
|
||||||
device.aSecMux.RLock()
|
device.awg.ASecMux.RLock()
|
||||||
msg.Type = MessageResponseType
|
msg.Type = MessageResponseType
|
||||||
device.aSecMux.RUnlock()
|
device.awg.ASecMux.RUnlock()
|
||||||
msg.Sender = handshake.localIndex
|
msg.Sender = handshake.localIndex
|
||||||
msg.Receiver = handshake.remoteIndex
|
msg.Receiver = handshake.remoteIndex
|
||||||
|
|
||||||
|
@ -428,12 +436,12 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
|
func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
|
||||||
device.aSecMux.RLock()
|
device.awg.ASecMux.RLock()
|
||||||
if msg.Type != MessageResponseType {
|
if msg.Type != MessageResponseType {
|
||||||
device.aSecMux.RUnlock()
|
device.awg.ASecMux.RUnlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
device.aSecMux.RUnlock()
|
device.awg.ASecMux.RUnlock()
|
||||||
|
|
||||||
// lookup handshake by receiver
|
// lookup handshake by receiver
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -10,8 +10,8 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tun/tuntest"
|
"github.com/amnezia-vpn/amneziawg-go/tun/tuntest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCurveWrappers(t *testing.T) {
|
func TestCurveWrappers(t *testing.T) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -12,22 +12,26 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
|
"github.com/amnezia-vpn/amneziawg-go/device/awg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
isRunning atomic.Bool
|
isRunning atomic.Bool
|
||||||
sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
|
|
||||||
keypairs Keypairs
|
keypairs Keypairs
|
||||||
handshake Handshake
|
handshake Handshake
|
||||||
device *Device
|
device *Device
|
||||||
endpoint conn.Endpoint
|
|
||||||
stopping sync.WaitGroup // routines pending stop
|
stopping sync.WaitGroup // routines pending stop
|
||||||
txBytes atomic.Uint64 // bytes send to peer (endpoint)
|
txBytes atomic.Uint64 // bytes send to peer (endpoint)
|
||||||
rxBytes atomic.Uint64 // bytes received from peer
|
rxBytes atomic.Uint64 // bytes received from peer
|
||||||
lastHandshakeNano atomic.Int64 // nano seconds since epoch
|
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 {
|
timers struct {
|
||||||
retransmitHandshake *Timer
|
retransmitHandshake *Timer
|
||||||
|
@ -45,9 +49,9 @@ type Peer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
queue struct {
|
queue struct {
|
||||||
staged chan *[]*QueueOutboundElement // staged packets before a handshake is available
|
staged chan *QueueOutboundElementsContainer // staged packets before a handshake is available
|
||||||
outbound *autodrainingOutboundQueue // sequential ordering of udp transmission
|
outbound *autodrainingOutboundQueue // sequential ordering of udp transmission
|
||||||
inbound *autodrainingInboundQueue // sequential ordering of tun writing
|
inbound *autodrainingInboundQueue // sequential ordering of tun writing
|
||||||
}
|
}
|
||||||
|
|
||||||
cookieGenerator CookieGenerator
|
cookieGenerator CookieGenerator
|
||||||
|
@ -74,14 +78,12 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||||
|
|
||||||
// create peer
|
// create peer
|
||||||
peer := new(Peer)
|
peer := new(Peer)
|
||||||
peer.Lock()
|
|
||||||
defer peer.Unlock()
|
|
||||||
|
|
||||||
peer.cookieGenerator.Init(pk)
|
peer.cookieGenerator.Init(pk)
|
||||||
peer.device = device
|
peer.device = device
|
||||||
peer.queue.outbound = newAutodrainingOutboundQueue(device)
|
peer.queue.outbound = newAutodrainingOutboundQueue(device)
|
||||||
peer.queue.inbound = newAutodrainingInboundQueue(device)
|
peer.queue.inbound = newAutodrainingInboundQueue(device)
|
||||||
peer.queue.staged = make(chan *[]*QueueOutboundElement, QueueStagedSize)
|
peer.queue.staged = make(chan *QueueOutboundElementsContainer, QueueStagedSize)
|
||||||
|
|
||||||
// map public key
|
// map public key
|
||||||
_, ok := device.peers.keyMap[pk]
|
_, ok := device.peers.keyMap[pk]
|
||||||
|
@ -97,7 +99,11 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||||
handshake.mutex.Unlock()
|
handshake.mutex.Unlock()
|
||||||
|
|
||||||
// reset endpoint
|
// 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
|
// init timers
|
||||||
peer.timersInit()
|
peer.timersInit()
|
||||||
|
@ -108,6 +114,16 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||||
return peer, nil
|
return peer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (peer *Peer) SendAndCountBuffers(buffers [][]byte) error {
|
||||||
|
err := peer.SendBuffers(buffers)
|
||||||
|
if err == nil {
|
||||||
|
awg.PacketCounter.Add(uint64(len(buffers)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (peer *Peer) SendBuffers(buffers [][]byte) error {
|
func (peer *Peer) SendBuffers(buffers [][]byte) error {
|
||||||
peer.device.net.RLock()
|
peer.device.net.RLock()
|
||||||
defer peer.device.net.RUnlock()
|
defer peer.device.net.RUnlock()
|
||||||
|
@ -116,14 +132,19 @@ func (peer *Peer) SendBuffers(buffers [][]byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.RLock()
|
peer.endpoint.Lock()
|
||||||
defer peer.RUnlock()
|
endpoint := peer.endpoint.val
|
||||||
|
if endpoint == nil {
|
||||||
if peer.endpoint == nil {
|
peer.endpoint.Unlock()
|
||||||
return errors.New("no known endpoint for peer")
|
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 {
|
if err == nil {
|
||||||
var totalLen uint64
|
var totalLen uint64
|
||||||
for _, b := range buffers {
|
for _, b := range buffers {
|
||||||
|
@ -267,10 +288,20 @@ func (peer *Peer) Stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (peer *Peer) SetEndpointFromPacket(endpoint conn.Endpoint) {
|
func (peer *Peer) SetEndpointFromPacket(endpoint conn.Endpoint) {
|
||||||
if peer.disableRoaming {
|
peer.endpoint.Lock()
|
||||||
|
defer peer.endpoint.Unlock()
|
||||||
|
if peer.endpoint.disableRoaming {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
peer.Lock()
|
peer.endpoint.clearSrcOnTx = false
|
||||||
peer.endpoint = endpoint
|
peer.endpoint.val = endpoint
|
||||||
peer.Unlock()
|
}
|
||||||
|
|
||||||
|
func (peer *Peer) markEndpointSrcForClearing() {
|
||||||
|
peer.endpoint.Lock()
|
||||||
|
defer peer.endpoint.Unlock()
|
||||||
|
if peer.endpoint.val == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
peer.endpoint.clearSrcOnTx = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WaitPool struct {
|
type WaitPool struct {
|
||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
cond sync.Cond
|
cond sync.Cond
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
count atomic.Uint32
|
count uint32 // Get calls not yet Put back
|
||||||
max uint32
|
max uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,10 +26,10 @@ func NewWaitPool(max uint32, new func() any) *WaitPool {
|
||||||
func (p *WaitPool) Get() any {
|
func (p *WaitPool) Get() any {
|
||||||
if p.max != 0 {
|
if p.max != 0 {
|
||||||
p.lock.Lock()
|
p.lock.Lock()
|
||||||
for p.count.Load() >= p.max {
|
for p.count >= p.max {
|
||||||
p.cond.Wait()
|
p.cond.Wait()
|
||||||
}
|
}
|
||||||
p.count.Add(1)
|
p.count++
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
}
|
}
|
||||||
return p.pool.Get()
|
return p.pool.Get()
|
||||||
|
@ -41,18 +40,20 @@ func (p *WaitPool) Put(x any) {
|
||||||
if p.max == 0 {
|
if p.max == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.count.Add(^uint32(0))
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
p.count--
|
||||||
p.cond.Signal()
|
p.cond.Signal()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) PopulatePools() {
|
func (device *Device) PopulatePools() {
|
||||||
device.pool.outboundElementsSlice = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
device.pool.inboundElementsContainer = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
||||||
s := make([]*QueueOutboundElement, 0, device.BatchSize())
|
|
||||||
return &s
|
|
||||||
})
|
|
||||||
device.pool.inboundElementsSlice = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
|
||||||
s := make([]*QueueInboundElement, 0, device.BatchSize())
|
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 {
|
device.pool.messageBuffers = NewWaitPool(PreallocatedBuffersPerPool, func() any {
|
||||||
return new([MaxMessageSize]byte)
|
return new([MaxMessageSize]byte)
|
||||||
|
@ -65,28 +66,32 @@ func (device *Device) PopulatePools() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) GetOutboundElementsSlice() *[]*QueueOutboundElement {
|
func (device *Device) GetInboundElementsContainer() *QueueInboundElementsContainer {
|
||||||
return device.pool.outboundElementsSlice.Get().(*[]*QueueOutboundElement)
|
c := device.pool.inboundElementsContainer.Get().(*QueueInboundElementsContainer)
|
||||||
|
c.Mutex = sync.Mutex{}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) PutOutboundElementsSlice(s *[]*QueueOutboundElement) {
|
func (device *Device) PutInboundElementsContainer(c *QueueInboundElementsContainer) {
|
||||||
for i := range *s {
|
for i := range c.elems {
|
||||||
(*s)[i] = nil
|
c.elems[i] = nil
|
||||||
}
|
}
|
||||||
*s = (*s)[:0]
|
c.elems = c.elems[:0]
|
||||||
device.pool.outboundElementsSlice.Put(s)
|
device.pool.inboundElementsContainer.Put(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) GetInboundElementsSlice() *[]*QueueInboundElement {
|
func (device *Device) GetOutboundElementsContainer() *QueueOutboundElementsContainer {
|
||||||
return device.pool.inboundElementsSlice.Get().(*[]*QueueInboundElement)
|
c := device.pool.outboundElementsContainer.Get().(*QueueOutboundElementsContainer)
|
||||||
|
c.Mutex = sync.Mutex{}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) PutInboundElementsSlice(s *[]*QueueInboundElement) {
|
func (device *Device) PutOutboundElementsContainer(c *QueueOutboundElementsContainer) {
|
||||||
for i := range *s {
|
for i := range c.elems {
|
||||||
(*s)[i] = nil
|
c.elems[i] = nil
|
||||||
}
|
}
|
||||||
*s = (*s)[:0]
|
c.elems = c.elems[:0]
|
||||||
device.pool.inboundElementsSlice.Put(s)
|
device.pool.outboundElementsContainer.Put(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
|
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -32,7 +32,9 @@ func TestWaitPool(t *testing.T) {
|
||||||
wg.Add(workers)
|
wg.Add(workers)
|
||||||
var max atomic.Uint32
|
var max atomic.Uint32
|
||||||
updateMax := func() {
|
updateMax := func() {
|
||||||
count := p.count.Load()
|
p.lock.Lock()
|
||||||
|
count := p.count
|
||||||
|
p.lock.Unlock()
|
||||||
if count > p.max {
|
if count > p.max {
|
||||||
t.Errorf("count (%d) > max (%d)", count, p.max)
|
t.Errorf("count (%d) > max (%d)", count, p.max)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
||||||
import "github.com/amnezia-vpn/amnezia-wg/conn"
|
import "github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
|
|
||||||
/* Reduce memory consumption for Android */
|
/* Reduce memory consumption for Android */
|
||||||
|
|
||||||
|
@ -14,6 +14,6 @@ const (
|
||||||
QueueOutboundSize = 1024
|
QueueOutboundSize = 1024
|
||||||
QueueInboundSize = 1024
|
QueueInboundSize = 1024
|
||||||
QueueHandshakeSize = 1024
|
QueueHandshakeSize = 1024
|
||||||
MaxSegmentSize = 2200
|
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
|
||||||
PreallocatedBuffersPerPool = 4096
|
PreallocatedBuffersPerPool = 4096
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
||||||
import "github.com/amnezia-vpn/amnezia-wg/conn"
|
import "github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
QueueStagedSize = conn.IdealBatchSize
|
QueueStagedSize = conn.IdealBatchSize
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -13,7 +13,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
"golang.org/x/net/ipv6"
|
"golang.org/x/net/ipv6"
|
||||||
|
@ -27,7 +27,6 @@ type QueueHandshakeElement struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueueInboundElement struct {
|
type QueueInboundElement struct {
|
||||||
sync.Mutex
|
|
||||||
buffer *[MaxMessageSize]byte
|
buffer *[MaxMessageSize]byte
|
||||||
packet []byte
|
packet []byte
|
||||||
counter uint64
|
counter uint64
|
||||||
|
@ -35,6 +34,11 @@ type QueueInboundElement struct {
|
||||||
endpoint conn.Endpoint
|
endpoint conn.Endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueueInboundElementsContainer struct {
|
||||||
|
sync.Mutex
|
||||||
|
elems []*QueueInboundElement
|
||||||
|
}
|
||||||
|
|
||||||
// clearPointers clears elem fields that contain pointers.
|
// clearPointers clears elem fields that contain pointers.
|
||||||
// This makes the garbage collector's life easier and
|
// This makes the garbage collector's life easier and
|
||||||
// avoids accidentally keeping other objects around unnecessarily.
|
// avoids accidentally keeping other objects around unnecessarily.
|
||||||
|
@ -90,7 +94,7 @@ func (device *Device) RoutineReceiveIncoming(
|
||||||
count int
|
count int
|
||||||
endpoints = make([]conn.Endpoint, maxBatchSize)
|
endpoints = make([]conn.Endpoint, maxBatchSize)
|
||||||
deathSpiral int
|
deathSpiral int
|
||||||
elemsByPeer = make(map[*Peer]*[]*QueueInboundElement, maxBatchSize)
|
elemsByPeer = make(map[*Peer]*QueueInboundElementsContainer, maxBatchSize)
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := range bufsArrs {
|
for i := range bufsArrs {
|
||||||
|
@ -125,7 +129,7 @@ func (device *Device) RoutineReceiveIncoming(
|
||||||
}
|
}
|
||||||
deathSpiral = 0
|
deathSpiral = 0
|
||||||
|
|
||||||
device.aSecMux.RLock()
|
device.awg.ASecMux.RLock()
|
||||||
// handle each packet in the batch
|
// handle each packet in the batch
|
||||||
for i, size := range sizes[:count] {
|
for i, size := range sizes[:count] {
|
||||||
if size < MinMessageSize {
|
if size < MinMessageSize {
|
||||||
|
@ -133,30 +137,45 @@ func (device *Device) RoutineReceiveIncoming(
|
||||||
}
|
}
|
||||||
|
|
||||||
// check size of packet
|
// check size of packet
|
||||||
|
|
||||||
packet := bufsArrs[i][:size]
|
packet := bufsArrs[i][:size]
|
||||||
var msgType uint32
|
var msgType uint32
|
||||||
if device.isAdvancedSecurityOn() {
|
if device.isAWG() {
|
||||||
|
// TODO:
|
||||||
|
// if awg.WaitResponse.ShouldWait.IsSet() {
|
||||||
|
// awg.WaitResponse.Channel <- struct{}{}
|
||||||
|
// }
|
||||||
|
|
||||||
if assumedMsgType, ok := packetSizeToMsgType[size]; ok {
|
if assumedMsgType, ok := packetSizeToMsgType[size]; ok {
|
||||||
junkSize := msgTypeToJunkSize[assumedMsgType]
|
junkSize := msgTypeToJunkSize[assumedMsgType]
|
||||||
// transport size can align with other header types;
|
// transport size can align with other header types;
|
||||||
// making sure we have the right msgType
|
// making sure we have the right msgType
|
||||||
msgType = binary.LittleEndian.Uint32(packet[junkSize:4])
|
msgType = binary.LittleEndian.Uint32(packet[junkSize : junkSize+4])
|
||||||
if msgType == assumedMsgType {
|
if msgType == assumedMsgType {
|
||||||
packet = packet[junkSize:]
|
packet = packet[junkSize:]
|
||||||
} else {
|
} else {
|
||||||
|
device.log.Verbosef("transport packet lined up with another msg type")
|
||||||
msgType = binary.LittleEndian.Uint32(packet[:4])
|
msgType = binary.LittleEndian.Uint32(packet[:4])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msgType = binary.LittleEndian.Uint32(packet[:4])
|
transportJunkSize := device.awg.ASecCfg.TransportHeaderJunkSize
|
||||||
|
msgType = binary.LittleEndian.Uint32(packet[transportJunkSize : transportJunkSize+4])
|
||||||
if msgType != MessageTransportType {
|
if msgType != MessageTransportType {
|
||||||
device.log.Verbosef("ASec: Received message with unknown type")
|
// probably a junk packet
|
||||||
|
device.log.Verbosef("aSec: Received message with unknown type: %d", msgType)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove junk from bufsArrs by shifting the packet
|
||||||
|
// this buffer is also used for decryption, so it needs to be corrected
|
||||||
|
copy(bufsArrs[i][:size], packet[transportJunkSize:])
|
||||||
|
size -= transportJunkSize
|
||||||
|
// need to reinitialize packet as well
|
||||||
|
packet = packet[:size]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msgType = binary.LittleEndian.Uint32(packet[:4])
|
msgType = binary.LittleEndian.Uint32(packet[:4])
|
||||||
}
|
}
|
||||||
|
|
||||||
switch msgType {
|
switch msgType {
|
||||||
|
|
||||||
// check if transport
|
// check if transport
|
||||||
|
@ -194,15 +213,14 @@ func (device *Device) RoutineReceiveIncoming(
|
||||||
elem.keypair = keypair
|
elem.keypair = keypair
|
||||||
elem.endpoint = endpoints[i]
|
elem.endpoint = endpoints[i]
|
||||||
elem.counter = 0
|
elem.counter = 0
|
||||||
elem.Mutex = sync.Mutex{}
|
|
||||||
elem.Lock()
|
|
||||||
|
|
||||||
elemsForPeer, ok := elemsByPeer[peer]
|
elemsForPeer, ok := elemsByPeer[peer]
|
||||||
if !ok {
|
if !ok {
|
||||||
elemsForPeer = device.GetInboundElementsSlice()
|
elemsForPeer = device.GetInboundElementsContainer()
|
||||||
|
elemsForPeer.Lock()
|
||||||
elemsByPeer[peer] = elemsForPeer
|
elemsByPeer[peer] = elemsForPeer
|
||||||
}
|
}
|
||||||
*elemsForPeer = append(*elemsForPeer, elem)
|
elemsForPeer.elems = append(elemsForPeer.elems, elem)
|
||||||
bufsArrs[i] = device.GetMessageBuffer()
|
bufsArrs[i] = device.GetMessageBuffer()
|
||||||
bufs[i] = bufsArrs[i][:]
|
bufs[i] = bufsArrs[i][:]
|
||||||
continue
|
continue
|
||||||
|
@ -241,19 +259,17 @@ func (device *Device) RoutineReceiveIncoming(
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
device.aSecMux.RUnlock()
|
device.awg.ASecMux.RUnlock()
|
||||||
for peer, elems := range elemsByPeer {
|
for peer, elemsContainer := range elemsByPeer {
|
||||||
if peer.isRunning.Load() {
|
if peer.isRunning.Load() {
|
||||||
peer.queue.inbound.c <- elems
|
peer.queue.inbound.c <- elemsContainer
|
||||||
for _, elem := range *elems {
|
device.queue.decryption.c <- elemsContainer
|
||||||
device.queue.decryption.c <- elem
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for _, elem := range *elems {
|
for _, elem := range elemsContainer.elems {
|
||||||
device.PutMessageBuffer(elem.buffer)
|
device.PutMessageBuffer(elem.buffer)
|
||||||
device.PutInboundElement(elem)
|
device.PutInboundElement(elem)
|
||||||
}
|
}
|
||||||
device.PutInboundElementsSlice(elems)
|
device.PutInboundElementsContainer(elemsContainer)
|
||||||
}
|
}
|
||||||
delete(elemsByPeer, peer)
|
delete(elemsByPeer, peer)
|
||||||
}
|
}
|
||||||
|
@ -266,26 +282,28 @@ func (device *Device) RoutineDecryption(id int) {
|
||||||
defer device.log.Verbosef("Routine: decryption worker %d - stopped", id)
|
defer device.log.Verbosef("Routine: decryption worker %d - stopped", id)
|
||||||
device.log.Verbosef("Routine: decryption worker %d - started", id)
|
device.log.Verbosef("Routine: decryption worker %d - started", id)
|
||||||
|
|
||||||
for elem := range device.queue.decryption.c {
|
for elemsContainer := range device.queue.decryption.c {
|
||||||
// split message into fields
|
for _, elem := range elemsContainer.elems {
|
||||||
counter := elem.packet[MessageTransportOffsetCounter:MessageTransportOffsetContent]
|
// split message into fields
|
||||||
content := elem.packet[MessageTransportOffsetContent:]
|
counter := elem.packet[MessageTransportOffsetCounter:MessageTransportOffsetContent]
|
||||||
|
content := elem.packet[MessageTransportOffsetContent:]
|
||||||
|
|
||||||
// decrypt and release to consumer
|
// decrypt and release to consumer
|
||||||
var err error
|
var err error
|
||||||
elem.counter = binary.LittleEndian.Uint64(counter)
|
elem.counter = binary.LittleEndian.Uint64(counter)
|
||||||
// copy counter to nonce
|
// copy counter to nonce
|
||||||
binary.LittleEndian.PutUint64(nonce[0x4:0xc], elem.counter)
|
binary.LittleEndian.PutUint64(nonce[0x4:0xc], elem.counter)
|
||||||
elem.packet, err = elem.keypair.receive.Open(
|
elem.packet, err = elem.keypair.receive.Open(
|
||||||
content[:0],
|
content[:0],
|
||||||
nonce[:],
|
nonce[:],
|
||||||
content,
|
content,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elem.packet = nil
|
elem.packet = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elem.Unlock()
|
elemsContainer.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +318,7 @@ func (device *Device) RoutineHandshake(id int) {
|
||||||
|
|
||||||
for elem := range device.queue.handshake.c {
|
for elem := range device.queue.handshake.c {
|
||||||
|
|
||||||
device.aSecMux.RLock()
|
device.awg.ASecMux.RLock()
|
||||||
|
|
||||||
// handle cookie fields and ratelimiting
|
// handle cookie fields and ratelimiting
|
||||||
|
|
||||||
|
@ -452,7 +470,7 @@ func (device *Device) RoutineHandshake(id int) {
|
||||||
peer.SendKeepalive()
|
peer.SendKeepalive()
|
||||||
}
|
}
|
||||||
skip:
|
skip:
|
||||||
device.aSecMux.RUnlock()
|
device.awg.ASecMux.RUnlock()
|
||||||
device.PutMessageBuffer(elem.buffer)
|
device.PutMessageBuffer(elem.buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -467,12 +485,15 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||||
|
|
||||||
bufs := make([][]byte, 0, maxBatchSize)
|
bufs := make([][]byte, 0, maxBatchSize)
|
||||||
|
|
||||||
for elems := range peer.queue.inbound.c {
|
for elemsContainer := range peer.queue.inbound.c {
|
||||||
if elems == nil {
|
if elemsContainer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, elem := range *elems {
|
elemsContainer.Lock()
|
||||||
elem.Lock()
|
validTailPacket := -1
|
||||||
|
dataPacketReceived := false
|
||||||
|
rxBytesLen := uint64(0)
|
||||||
|
for i, elem := range elemsContainer.elems {
|
||||||
if elem.packet == nil {
|
if elem.packet == nil {
|
||||||
// decryption failed
|
// decryption failed
|
||||||
continue
|
continue
|
||||||
|
@ -482,21 +503,19 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.SetEndpointFromPacket(elem.endpoint)
|
validTailPacket = i
|
||||||
if peer.ReceivedWithKeypair(elem.keypair) {
|
if peer.ReceivedWithKeypair(elem.keypair) {
|
||||||
|
peer.SetEndpointFromPacket(elem.endpoint)
|
||||||
peer.timersHandshakeComplete()
|
peer.timersHandshakeComplete()
|
||||||
peer.SendStagedPackets()
|
peer.SendStagedPackets()
|
||||||
}
|
}
|
||||||
peer.keepKeyFreshReceiving()
|
rxBytesLen += uint64(len(elem.packet) + MinMessageSize)
|
||||||
peer.timersAnyAuthenticatedPacketTraversal()
|
|
||||||
peer.timersAnyAuthenticatedPacketReceived()
|
|
||||||
peer.rxBytes.Add(uint64(len(elem.packet) + MinMessageSize))
|
|
||||||
|
|
||||||
if len(elem.packet) == 0 {
|
if len(elem.packet) == 0 {
|
||||||
device.log.Verbosef("%v - Receiving keepalive packet", peer)
|
device.log.Verbosef("%v - Receiving keepalive packet", peer)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
peer.timersDataReceived()
|
dataPacketReceived = true
|
||||||
|
|
||||||
switch elem.packet[0] >> 4 {
|
switch elem.packet[0] >> 4 {
|
||||||
case 4:
|
case 4:
|
||||||
|
@ -545,17 +564,28 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||||
elem.buffer[:MessageTransportOffsetContent+len(elem.packet)],
|
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 {
|
if len(bufs) > 0 {
|
||||||
_, err := device.tun.device.Write(bufs, MessageTransportOffsetContent)
|
_, err := device.tun.device.Write(bufs, MessageTransportOffsetContent)
|
||||||
if err != nil && !device.isClosed() {
|
if err != nil && !device.isClosed() {
|
||||||
device.log.Errorf("Failed to write packets to TUN device: %v", err)
|
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.PutMessageBuffer(elem.buffer)
|
||||||
device.PutInboundElement(elem)
|
device.PutInboundElement(elem)
|
||||||
}
|
}
|
||||||
bufs = bufs[:0]
|
bufs = bufs[:0]
|
||||||
device.PutInboundElementsSlice(elems)
|
device.PutInboundElementsContainer(elemsContainer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
266
device/send.go
266
device/send.go
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -9,13 +9,13 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tun"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
|
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
"golang.org/x/net/ipv6"
|
"golang.org/x/net/ipv6"
|
||||||
|
@ -46,7 +46,6 @@ import (
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type QueueOutboundElement struct {
|
type QueueOutboundElement struct {
|
||||||
sync.Mutex
|
|
||||||
buffer *[MaxMessageSize]byte // slice holding the packet data
|
buffer *[MaxMessageSize]byte // slice holding the packet data
|
||||||
packet []byte // slice of "buffer" (always!)
|
packet []byte // slice of "buffer" (always!)
|
||||||
nonce uint64 // nonce for encryption
|
nonce uint64 // nonce for encryption
|
||||||
|
@ -54,10 +53,14 @@ type QueueOutboundElement struct {
|
||||||
peer *Peer // related peer
|
peer *Peer // related peer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueueOutboundElementsContainer struct {
|
||||||
|
sync.Mutex
|
||||||
|
elems []*QueueOutboundElement
|
||||||
|
}
|
||||||
|
|
||||||
func (device *Device) NewOutboundElement() *QueueOutboundElement {
|
func (device *Device) NewOutboundElement() *QueueOutboundElement {
|
||||||
elem := device.GetOutboundElement()
|
elem := device.GetOutboundElement()
|
||||||
elem.buffer = device.GetMessageBuffer()
|
elem.buffer = device.GetMessageBuffer()
|
||||||
elem.Mutex = sync.Mutex{}
|
|
||||||
elem.nonce = 0
|
elem.nonce = 0
|
||||||
// keypair and peer were cleared (if necessary) by clearPointers.
|
// keypair and peer were cleared (if necessary) by clearPointers.
|
||||||
return elem
|
return elem
|
||||||
|
@ -79,15 +82,15 @@ func (elem *QueueOutboundElement) clearPointers() {
|
||||||
func (peer *Peer) SendKeepalive() {
|
func (peer *Peer) SendKeepalive() {
|
||||||
if len(peer.queue.staged) == 0 && peer.isRunning.Load() {
|
if len(peer.queue.staged) == 0 && peer.isRunning.Load() {
|
||||||
elem := peer.device.NewOutboundElement()
|
elem := peer.device.NewOutboundElement()
|
||||||
elems := peer.device.GetOutboundElementsSlice()
|
elemsContainer := peer.device.GetOutboundElementsContainer()
|
||||||
*elems = append(*elems, elem)
|
elemsContainer.elems = append(elemsContainer.elems, elem)
|
||||||
select {
|
select {
|
||||||
case peer.queue.staged <- elems:
|
case peer.queue.staged <- elemsContainer:
|
||||||
peer.device.log.Verbosef("%v - Sending keepalive packet", peer)
|
peer.device.log.Verbosef("%v - Sending keepalive packet", peer)
|
||||||
default:
|
default:
|
||||||
peer.device.PutMessageBuffer(elem.buffer)
|
peer.device.PutMessageBuffer(elem.buffer)
|
||||||
peer.device.PutOutboundElement(elem)
|
peer.device.PutOutboundElement(elem)
|
||||||
peer.device.PutOutboundElementsSlice(elems)
|
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
peer.SendStagedPackets()
|
peer.SendStagedPackets()
|
||||||
|
@ -121,30 +124,52 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var sendBuffer [][]byte
|
var sendBuffer [][]byte
|
||||||
|
|
||||||
// so only packet processed for cookie generation
|
// so only packet processed for cookie generation
|
||||||
var junkedHeader []byte
|
var junkedHeader []byte
|
||||||
if peer.device.isAdvancedSecurityOn() {
|
if peer.device.version >= VersionAwg {
|
||||||
peer.device.aSecMux.RLock()
|
var junks [][]byte
|
||||||
junks, err := peer.createJunkPackets()
|
if peer.device.version == VersionAwgSpecialHandshake {
|
||||||
|
peer.device.awg.ASecMux.RLock()
|
||||||
|
// set junks depending on packet type
|
||||||
|
junks = peer.device.awg.HandshakeHandler.GenerateSpecialJunk()
|
||||||
|
if junks == nil {
|
||||||
|
junks = peer.device.awg.HandshakeHandler.GenerateControlledJunk()
|
||||||
|
if junks != nil {
|
||||||
|
peer.device.log.Verbosef("%v - Controlled junks sent", peer)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
peer.device.log.Verbosef("%v - Special junks sent", peer)
|
||||||
|
}
|
||||||
|
peer.device.awg.ASecMux.RUnlock()
|
||||||
|
} else {
|
||||||
|
junks = make([][]byte, 0, peer.device.awg.ASecCfg.JunkPacketCount)
|
||||||
|
}
|
||||||
|
peer.device.awg.ASecMux.RLock()
|
||||||
|
err := peer.device.awg.JunkCreator.CreateJunkPackets(&junks)
|
||||||
|
peer.device.awg.ASecMux.RUnlock()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
peer.device.aSecMux.RUnlock()
|
|
||||||
peer.device.log.Errorf("%v - %v", peer, err)
|
peer.device.log.Errorf("%v - %v", peer, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sendBuffer = append(sendBuffer, junks...)
|
|
||||||
if peer.device.aSecCfg.initPacketJunkSize != 0 {
|
if len(junks) > 0 {
|
||||||
buf := make([]byte, 0, peer.device.aSecCfg.initPacketJunkSize)
|
err = peer.SendBuffers(junks)
|
||||||
writer := bytes.NewBuffer(buf[:0])
|
|
||||||
err = appendJunk(writer, peer.device.aSecCfg.initPacketJunkSize)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
peer.device.aSecMux.RUnlock()
|
peer.device.log.Errorf("%v - Failed to send junk packets: %v", peer, err)
|
||||||
peer.device.log.Errorf("%v - %v", peer, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
junkedHeader = writer.Bytes()
|
|
||||||
}
|
}
|
||||||
peer.device.aSecMux.RUnlock()
|
|
||||||
|
junkedHeader, err = peer.device.awg.CreateInitHeaderJunk()
|
||||||
|
if err != nil {
|
||||||
|
peer.device.log.Errorf("%v - %v", peer, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf [MessageInitiationSize]byte
|
var buf [MessageInitiationSize]byte
|
||||||
writer := bytes.NewBuffer(buf[:0])
|
writer := bytes.NewBuffer(buf[:0])
|
||||||
binary.Write(writer, binary.LittleEndian, msg)
|
binary.Write(writer, binary.LittleEndian, msg)
|
||||||
|
@ -157,7 +182,7 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||||
|
|
||||||
sendBuffer = append(sendBuffer, junkedHeader)
|
sendBuffer = append(sendBuffer, junkedHeader)
|
||||||
|
|
||||||
err = peer.SendBuffers(sendBuffer)
|
err = peer.SendAndCountBuffers(sendBuffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err)
|
peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err)
|
||||||
}
|
}
|
||||||
|
@ -178,22 +203,13 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||||
peer.device.log.Errorf("%v - Failed to create response message: %v", peer, err)
|
peer.device.log.Errorf("%v - Failed to create response message: %v", peer, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var junkedHeader []byte
|
|
||||||
if peer.device.isAdvancedSecurityOn() {
|
junkedHeader, err := peer.device.awg.CreateResponseHeaderJunk()
|
||||||
peer.device.aSecMux.RLock()
|
if err != nil {
|
||||||
if peer.device.aSecCfg.responsePacketJunkSize != 0 {
|
peer.device.log.Errorf("%v - %v", peer, err)
|
||||||
buf := make([]byte, 0, peer.device.aSecCfg.responsePacketJunkSize)
|
return err
|
||||||
writer := bytes.NewBuffer(buf[:0])
|
|
||||||
err = appendJunk(writer, peer.device.aSecCfg.responsePacketJunkSize)
|
|
||||||
if err != nil {
|
|
||||||
peer.device.aSecMux.RUnlock()
|
|
||||||
peer.device.log.Errorf("%v - %v", peer, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
junkedHeader = writer.Bytes()
|
|
||||||
}
|
|
||||||
peer.device.aSecMux.RUnlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf [MessageResponseSize]byte
|
var buf [MessageResponseSize]byte
|
||||||
writer := bytes.NewBuffer(buf[:0])
|
writer := bytes.NewBuffer(buf[:0])
|
||||||
|
|
||||||
|
@ -213,7 +229,7 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||||
peer.timersAnyAuthenticatedPacketSent()
|
peer.timersAnyAuthenticatedPacketSent()
|
||||||
|
|
||||||
// TODO: allocation could be avoided
|
// TODO: allocation could be avoided
|
||||||
err = peer.SendBuffers([][]byte{junkedHeader})
|
err = peer.SendAndCountBuffers([][]byte{junkedHeader})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err)
|
peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err)
|
||||||
}
|
}
|
||||||
|
@ -236,11 +252,19 @@ func (device *Device) SendHandshakeCookie(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
junkedHeader, err := device.awg.CreateCookieReplyHeaderJunk()
|
||||||
|
if err != nil {
|
||||||
|
device.log.Errorf("%v - %v", device, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var buf [MessageCookieReplySize]byte
|
var buf [MessageCookieReplySize]byte
|
||||||
writer := bytes.NewBuffer(buf[:0])
|
writer := bytes.NewBuffer(buf[:0])
|
||||||
binary.Write(writer, binary.LittleEndian, reply)
|
binary.Write(writer, binary.LittleEndian, reply)
|
||||||
|
|
||||||
|
junkedHeader = append(junkedHeader, writer.Bytes()...)
|
||||||
// TODO: allocation could be avoided
|
// TODO: allocation could be avoided
|
||||||
device.net.bind.Send([][]byte{writer.Bytes()}, initiatingElem.endpoint)
|
device.net.bind.Send([][]byte{junkedHeader}, initiatingElem.endpoint)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +293,7 @@ func (device *Device) RoutineReadFromTUN() {
|
||||||
readErr error
|
readErr error
|
||||||
elems = make([]*QueueOutboundElement, batchSize)
|
elems = make([]*QueueOutboundElement, batchSize)
|
||||||
bufs = make([][]byte, batchSize)
|
bufs = make([][]byte, batchSize)
|
||||||
elemsByPeer = make(map[*Peer]*[]*QueueOutboundElement, batchSize)
|
elemsByPeer = make(map[*Peer]*QueueOutboundElementsContainer, batchSize)
|
||||||
count = 0
|
count = 0
|
||||||
sizes = make([]int, batchSize)
|
sizes = make([]int, batchSize)
|
||||||
offset = MessageTransportHeaderSize
|
offset = MessageTransportHeaderSize
|
||||||
|
@ -326,10 +350,10 @@ func (device *Device) RoutineReadFromTUN() {
|
||||||
}
|
}
|
||||||
elemsForPeer, ok := elemsByPeer[peer]
|
elemsForPeer, ok := elemsByPeer[peer]
|
||||||
if !ok {
|
if !ok {
|
||||||
elemsForPeer = device.GetOutboundElementsSlice()
|
elemsForPeer = device.GetOutboundElementsContainer()
|
||||||
elemsByPeer[peer] = elemsForPeer
|
elemsByPeer[peer] = elemsForPeer
|
||||||
}
|
}
|
||||||
*elemsForPeer = append(*elemsForPeer, elem)
|
elemsForPeer.elems = append(elemsForPeer.elems, elem)
|
||||||
elems[i] = device.NewOutboundElement()
|
elems[i] = device.NewOutboundElement()
|
||||||
bufs[i] = elems[i].buffer[:]
|
bufs[i] = elems[i].buffer[:]
|
||||||
}
|
}
|
||||||
|
@ -339,11 +363,11 @@ func (device *Device) RoutineReadFromTUN() {
|
||||||
peer.StagePackets(elemsForPeer)
|
peer.StagePackets(elemsForPeer)
|
||||||
peer.SendStagedPackets()
|
peer.SendStagedPackets()
|
||||||
} else {
|
} else {
|
||||||
for _, elem := range *elemsForPeer {
|
for _, elem := range elemsForPeer.elems {
|
||||||
device.PutMessageBuffer(elem.buffer)
|
device.PutMessageBuffer(elem.buffer)
|
||||||
device.PutOutboundElement(elem)
|
device.PutOutboundElement(elem)
|
||||||
}
|
}
|
||||||
device.PutOutboundElementsSlice(elemsForPeer)
|
device.PutOutboundElementsContainer(elemsForPeer)
|
||||||
}
|
}
|
||||||
delete(elemsByPeer, peer)
|
delete(elemsByPeer, peer)
|
||||||
}
|
}
|
||||||
|
@ -367,7 +391,7 @@ func (device *Device) RoutineReadFromTUN() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (peer *Peer) StagePackets(elems *[]*QueueOutboundElement) {
|
func (peer *Peer) StagePackets(elems *QueueOutboundElementsContainer) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case peer.queue.staged <- elems:
|
case peer.queue.staged <- elems:
|
||||||
|
@ -376,11 +400,11 @@ func (peer *Peer) StagePackets(elems *[]*QueueOutboundElement) {
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case tooOld := <-peer.queue.staged:
|
case tooOld := <-peer.queue.staged:
|
||||||
for _, elem := range *tooOld {
|
for _, elem := range tooOld.elems {
|
||||||
peer.device.PutMessageBuffer(elem.buffer)
|
peer.device.PutMessageBuffer(elem.buffer)
|
||||||
peer.device.PutOutboundElement(elem)
|
peer.device.PutOutboundElement(elem)
|
||||||
}
|
}
|
||||||
peer.device.PutOutboundElementsSlice(tooOld)
|
peer.device.PutOutboundElementsContainer(tooOld)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,54 +423,52 @@ top:
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var elemsOOO *[]*QueueOutboundElement
|
var elemsContainerOOO *QueueOutboundElementsContainer
|
||||||
select {
|
select {
|
||||||
case elems := <-peer.queue.staged:
|
case elemsContainer := <-peer.queue.staged:
|
||||||
i := 0
|
i := 0
|
||||||
for _, elem := range *elems {
|
for _, elem := range elemsContainer.elems {
|
||||||
elem.peer = peer
|
elem.peer = peer
|
||||||
elem.nonce = keypair.sendNonce.Add(1) - 1
|
elem.nonce = keypair.sendNonce.Add(1) - 1
|
||||||
if elem.nonce >= RejectAfterMessages {
|
if elem.nonce >= RejectAfterMessages {
|
||||||
keypair.sendNonce.Store(RejectAfterMessages)
|
keypair.sendNonce.Store(RejectAfterMessages)
|
||||||
if elemsOOO == nil {
|
if elemsContainerOOO == nil {
|
||||||
elemsOOO = peer.device.GetOutboundElementsSlice()
|
elemsContainerOOO = peer.device.GetOutboundElementsContainer()
|
||||||
}
|
}
|
||||||
*elemsOOO = append(*elemsOOO, elem)
|
elemsContainerOOO.elems = append(elemsContainerOOO.elems, elem)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
(*elems)[i] = elem
|
elemsContainer.elems[i] = elem
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.keypair = keypair
|
elem.keypair = keypair
|
||||||
elem.Lock()
|
|
||||||
}
|
}
|
||||||
*elems = (*elems)[:i]
|
elemsContainer.Lock()
|
||||||
|
elemsContainer.elems = elemsContainer.elems[:i]
|
||||||
|
|
||||||
if elemsOOO != nil {
|
if elemsContainerOOO != nil {
|
||||||
peer.StagePackets(elemsOOO) // XXX: Out of order, but we can't front-load go chans
|
peer.StagePackets(elemsContainerOOO) // XXX: Out of order, but we can't front-load go chans
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(*elems) == 0 {
|
if len(elemsContainer.elems) == 0 {
|
||||||
peer.device.PutOutboundElementsSlice(elems)
|
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||||
goto top
|
goto top
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to parallel and sequential queue
|
// add to parallel and sequential queue
|
||||||
if peer.isRunning.Load() {
|
if peer.isRunning.Load() {
|
||||||
peer.queue.outbound.c <- elems
|
peer.queue.outbound.c <- elemsContainer
|
||||||
for _, elem := range *elems {
|
peer.device.queue.encryption.c <- elemsContainer
|
||||||
peer.device.queue.encryption.c <- elem
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for _, elem := range *elems {
|
for _, elem := range elemsContainer.elems {
|
||||||
peer.device.PutMessageBuffer(elem.buffer)
|
peer.device.PutMessageBuffer(elem.buffer)
|
||||||
peer.device.PutOutboundElement(elem)
|
peer.device.PutOutboundElement(elem)
|
||||||
}
|
}
|
||||||
peer.device.PutOutboundElementsSlice(elems)
|
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if elemsOOO != nil {
|
if elemsContainerOOO != nil {
|
||||||
goto top
|
goto top
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -455,40 +477,15 @@ top:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (peer *Peer) createJunkPackets() ([][]byte, error) {
|
|
||||||
if peer.device.aSecCfg.junkPacketCount == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
junks := make([][]byte, 0, peer.device.aSecCfg.junkPacketCount)
|
|
||||||
for i := 0; i < peer.device.aSecCfg.junkPacketCount; i++ {
|
|
||||||
packetSize := rand.Intn(
|
|
||||||
peer.device.aSecCfg.junkPacketMaxSize-peer.device.aSecCfg.junkPacketMinSize,
|
|
||||||
) + peer.device.aSecCfg.junkPacketMinSize
|
|
||||||
|
|
||||||
junk, err := randomJunkWithSize(packetSize)
|
|
||||||
if err != nil {
|
|
||||||
peer.device.log.Errorf(
|
|
||||||
"%v - Failed to create junk packet: %v",
|
|
||||||
peer,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
junks = append(junks, junk)
|
|
||||||
}
|
|
||||||
return junks, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (peer *Peer) FlushStagedPackets() {
|
func (peer *Peer) FlushStagedPackets() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case elems := <-peer.queue.staged:
|
case elemsContainer := <-peer.queue.staged:
|
||||||
for _, elem := range *elems {
|
for _, elem := range elemsContainer.elems {
|
||||||
peer.device.PutMessageBuffer(elem.buffer)
|
peer.device.PutMessageBuffer(elem.buffer)
|
||||||
peer.device.PutOutboundElement(elem)
|
peer.device.PutOutboundElement(elem)
|
||||||
}
|
}
|
||||||
peer.device.PutOutboundElementsSlice(elems)
|
peer.device.PutOutboundElementsContainer(elemsContainer)
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -522,30 +519,34 @@ func (device *Device) RoutineEncryption(id int) {
|
||||||
defer device.log.Verbosef("Routine: encryption worker %d - stopped", id)
|
defer device.log.Verbosef("Routine: encryption worker %d - stopped", id)
|
||||||
device.log.Verbosef("Routine: encryption worker %d - started", id)
|
device.log.Verbosef("Routine: encryption worker %d - started", id)
|
||||||
|
|
||||||
for elem := range device.queue.encryption.c {
|
for elemsContainer := range device.queue.encryption.c {
|
||||||
// populate header fields
|
for _, elem := range elemsContainer.elems {
|
||||||
header := elem.buffer[:MessageTransportHeaderSize]
|
// populate header fields
|
||||||
|
header := elem.buffer[:MessageTransportHeaderSize]
|
||||||
|
|
||||||
fieldType := header[0:4]
|
fieldType := header[0:4]
|
||||||
fieldReceiver := header[4:8]
|
fieldReceiver := header[4:8]
|
||||||
fieldNonce := header[8:16]
|
fieldNonce := header[8:16]
|
||||||
|
|
||||||
binary.LittleEndian.PutUint32(fieldType, MessageTransportType)
|
binary.LittleEndian.PutUint32(fieldType, MessageTransportType)
|
||||||
binary.LittleEndian.PutUint32(fieldReceiver, elem.keypair.remoteIndex)
|
binary.LittleEndian.PutUint32(fieldReceiver, elem.keypair.remoteIndex)
|
||||||
binary.LittleEndian.PutUint64(fieldNonce, elem.nonce)
|
binary.LittleEndian.PutUint64(fieldNonce, elem.nonce)
|
||||||
|
|
||||||
// pad content to multiple of 16
|
// pad content to multiple of 16
|
||||||
paddingSize := calculatePaddingSize(
|
paddingSize := calculatePaddingSize(len(elem.packet), int(device.tun.mtu.Load()))
|
||||||
len(elem.packet),
|
elem.packet = append(elem.packet, paddingZeros[:paddingSize]...)
|
||||||
int(device.tun.mtu.Load()),
|
|
||||||
)
|
|
||||||
elem.packet = append(elem.packet, paddingZeros[:paddingSize]...)
|
|
||||||
|
|
||||||
// encrypt content and release to consumer
|
// encrypt content and release to consumer
|
||||||
|
|
||||||
binary.LittleEndian.PutUint64(nonce[4:], elem.nonce)
|
binary.LittleEndian.PutUint64(nonce[4:], elem.nonce)
|
||||||
elem.packet = elem.keypair.send.Seal(header, nonce[:], elem.packet, nil)
|
elem.packet = elem.keypair.send.Seal(
|
||||||
elem.Unlock()
|
header,
|
||||||
|
nonce[:],
|
||||||
|
elem.packet,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
elemsContainer.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,9 +560,9 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||||
|
|
||||||
bufs := make([][]byte, 0, maxBatchSize)
|
bufs := make([][]byte, 0, maxBatchSize)
|
||||||
|
|
||||||
for elems := range peer.queue.outbound.c {
|
for elemsContainer := range peer.queue.outbound.c {
|
||||||
bufs = bufs[:0]
|
bufs = bufs[:0]
|
||||||
if elems == nil {
|
if elemsContainer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !peer.isRunning.Load() {
|
if !peer.isRunning.Load() {
|
||||||
|
@ -571,18 +572,27 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||||
// The timers and SendBuffers code are resilient to a few stragglers.
|
// The timers and SendBuffers code are resilient to a few stragglers.
|
||||||
// TODO: rework peer shutdown order to ensure
|
// TODO: rework peer shutdown order to ensure
|
||||||
// that we never accidentally keep timers alive longer than necessary.
|
// that we never accidentally keep timers alive longer than necessary.
|
||||||
for _, elem := range *elems {
|
elemsContainer.Lock()
|
||||||
elem.Lock()
|
for _, elem := range elemsContainer.elems {
|
||||||
device.PutMessageBuffer(elem.buffer)
|
device.PutMessageBuffer(elem.buffer)
|
||||||
device.PutOutboundElement(elem)
|
device.PutOutboundElement(elem)
|
||||||
}
|
}
|
||||||
|
device.PutOutboundElementsContainer(elemsContainer)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
dataSent := false
|
dataSent := false
|
||||||
for _, elem := range *elems {
|
elemsContainer.Lock()
|
||||||
elem.Lock()
|
for _, elem := range elemsContainer.elems {
|
||||||
if len(elem.packet) != MessageKeepaliveSize {
|
if len(elem.packet) != MessageKeepaliveSize {
|
||||||
dataSent = true
|
dataSent = true
|
||||||
|
|
||||||
|
junkedHeader, err := device.awg.CreateTransportHeaderJunk(len(elem.packet))
|
||||||
|
if err != nil {
|
||||||
|
device.log.Errorf("%v - %v", device, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.packet = append(junkedHeader, elem.packet...)
|
||||||
}
|
}
|
||||||
bufs = append(bufs, elem.packet)
|
bufs = append(bufs, elem.packet)
|
||||||
}
|
}
|
||||||
|
@ -590,15 +600,23 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||||
peer.timersAnyAuthenticatedPacketTraversal()
|
peer.timersAnyAuthenticatedPacketTraversal()
|
||||||
peer.timersAnyAuthenticatedPacketSent()
|
peer.timersAnyAuthenticatedPacketSent()
|
||||||
|
|
||||||
err := peer.SendBuffers(bufs)
|
err := peer.SendAndCountBuffers(bufs)
|
||||||
if dataSent {
|
if dataSent {
|
||||||
peer.timersDataSent()
|
peer.timersDataSent()
|
||||||
}
|
}
|
||||||
for _, elem := range *elems {
|
|
||||||
|
for _, elem := range elemsContainer.elems {
|
||||||
device.PutMessageBuffer(elem.buffer)
|
device.PutMessageBuffer(elem.buffer)
|
||||||
device.PutOutboundElement(elem)
|
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 {
|
if err != nil {
|
||||||
device.log.Errorf("%v - Failed to send data packets: %v", peer, err)
|
device.log.Errorf("%v - Failed to send data packets: %v", peer, err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
package device
|
package device
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/rwcancel"
|
"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
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* 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
|
* This implements userspace semantics of "sticky sockets", modeled after
|
||||||
* WireGuard's kernelspace implementation. This is more or less a straight port
|
* 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:
|
* Currently there is no way to achieve this within the net package:
|
||||||
* See e.g. https://github.com/golang/go/issues/17930
|
* 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
|
package device
|
||||||
|
@ -20,8 +20,8 @@ import (
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/rwcancel"
|
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) {
|
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
|
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 {
|
type peerEndpointPtr struct {
|
||||||
peer *Peer
|
peer *Peer
|
||||||
endpoint *conn.Endpoint
|
endpoint *conn.Endpoint
|
||||||
|
@ -110,17 +110,17 @@ func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netl
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
pePtr.peer.Lock()
|
pePtr.peer.endpoint.Lock()
|
||||||
if &pePtr.peer.endpoint != pePtr.endpoint {
|
if &pePtr.peer.endpoint.val != pePtr.endpoint {
|
||||||
pePtr.peer.Unlock()
|
pePtr.peer.endpoint.Unlock()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if uint32(pePtr.peer.endpoint.(*conn.StdNetEndpoint).SrcIfidx()) == ifidx {
|
if uint32(pePtr.peer.endpoint.val.(*conn.StdNetEndpoint).SrcIfidx()) == ifidx {
|
||||||
pePtr.peer.Unlock()
|
pePtr.peer.endpoint.Unlock()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
pePtr.peer.endpoint.(*conn.StdNetEndpoint).ClearSrc()
|
pePtr.peer.endpoint.clearSrcOnTx = true
|
||||||
pePtr.peer.Unlock()
|
pePtr.peer.endpoint.Unlock()
|
||||||
}
|
}
|
||||||
attr = attr[attrhdr.Len:]
|
attr = attr[attrhdr.Len:]
|
||||||
}
|
}
|
||||||
|
@ -134,18 +134,18 @@ func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netl
|
||||||
device.peers.RLock()
|
device.peers.RLock()
|
||||||
i := uint32(1)
|
i := uint32(1)
|
||||||
for _, peer := range device.peers.keyMap {
|
for _, peer := range device.peers.keyMap {
|
||||||
peer.RLock()
|
peer.endpoint.Lock()
|
||||||
if peer.endpoint == nil {
|
if peer.endpoint.val == nil {
|
||||||
peer.RUnlock()
|
peer.endpoint.Unlock()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
nativeEP, _ := peer.endpoint.(*conn.StdNetEndpoint)
|
nativeEP, _ := peer.endpoint.val.(*conn.StdNetEndpoint)
|
||||||
if nativeEP == nil {
|
if nativeEP == nil {
|
||||||
peer.RUnlock()
|
peer.endpoint.Unlock()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if nativeEP.DstIP().Is6() || nativeEP.SrcIfidx() == 0 {
|
if nativeEP.DstIP().Is6() || nativeEP.SrcIfidx() == 0 {
|
||||||
peer.RUnlock()
|
peer.endpoint.Unlock()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
nlmsg := struct {
|
nlmsg := struct {
|
||||||
|
@ -188,10 +188,10 @@ func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netl
|
||||||
reqPeerLock.Lock()
|
reqPeerLock.Lock()
|
||||||
reqPeer[i] = peerEndpointPtr{
|
reqPeer[i] = peerEndpointPtr{
|
||||||
peer: peer,
|
peer: peer,
|
||||||
endpoint: &peer.endpoint,
|
endpoint: &peer.endpoint.val,
|
||||||
}
|
}
|
||||||
reqPeerLock.Unlock()
|
reqPeerLock.Unlock()
|
||||||
peer.RUnlock()
|
peer.endpoint.Unlock()
|
||||||
i++
|
i++
|
||||||
_, err := netlinkCancel.Write((*[unsafe.Sizeof(nlmsg)]byte)(unsafe.Pointer(&nlmsg))[:])
|
_, err := netlinkCancel.Write((*[unsafe.Sizeof(nlmsg)]byte)(unsafe.Pointer(&nlmsg))[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* 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.
|
* 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)
|
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. */
|
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||||
peer.Lock()
|
peer.markEndpointSrcForClearing()
|
||||||
if peer.endpoint != nil {
|
|
||||||
peer.endpoint.ClearSrc()
|
|
||||||
}
|
|
||||||
peer.Unlock()
|
|
||||||
|
|
||||||
peer.SendHandshakeInitiation(true)
|
peer.SendHandshakeInitiation(true)
|
||||||
}
|
}
|
||||||
|
@ -123,11 +119,7 @@ func expiredSendKeepalive(peer *Peer) {
|
||||||
func expiredNewHandshake(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()))
|
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. */
|
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||||
peer.Lock()
|
peer.markEndpointSrcForClearing()
|
||||||
if peer.endpoint != nil {
|
|
||||||
peer.endpoint.ClearSrc()
|
|
||||||
}
|
|
||||||
peer.Unlock()
|
|
||||||
peer.SendHandshakeInitiation(false)
|
peer.SendHandshakeInitiation(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -8,7 +8,7 @@ package device
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tun"
|
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultMTU = 1420
|
const DefaultMTU = 1420
|
||||||
|
|
301
device/uapi.go
301
device/uapi.go
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package device
|
package device
|
||||||
|
@ -18,7 +18,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/ipc"
|
"github.com/amnezia-vpn/amneziawg-go/device/awg"
|
||||||
|
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IPCError struct {
|
type IPCError struct {
|
||||||
|
@ -97,68 +98,81 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
|
||||||
sendf("fwmark=%d", device.net.fwmark)
|
sendf("fwmark=%d", device.net.fwmark)
|
||||||
}
|
}
|
||||||
|
|
||||||
if device.isAdvancedSecurityOn() {
|
if device.isAWG() {
|
||||||
if device.aSecCfg.junkPacketCount != 0 {
|
if device.awg.ASecCfg.JunkPacketCount != 0 {
|
||||||
sendf("jc=%d", device.aSecCfg.junkPacketCount)
|
sendf("jc=%d", device.awg.ASecCfg.JunkPacketCount)
|
||||||
}
|
}
|
||||||
if device.aSecCfg.junkPacketMinSize != 0 {
|
if device.awg.ASecCfg.JunkPacketMinSize != 0 {
|
||||||
sendf("jmin=%d", device.aSecCfg.junkPacketMinSize)
|
sendf("jmin=%d", device.awg.ASecCfg.JunkPacketMinSize)
|
||||||
}
|
}
|
||||||
if device.aSecCfg.junkPacketMaxSize != 0 {
|
if device.awg.ASecCfg.JunkPacketMaxSize != 0 {
|
||||||
sendf("jmax=%d", device.aSecCfg.junkPacketMaxSize)
|
sendf("jmax=%d", device.awg.ASecCfg.JunkPacketMaxSize)
|
||||||
}
|
}
|
||||||
if device.aSecCfg.initPacketJunkSize != 0 {
|
if device.awg.ASecCfg.InitHeaderJunkSize != 0 {
|
||||||
sendf("s1=%d", device.aSecCfg.initPacketJunkSize)
|
sendf("s1=%d", device.awg.ASecCfg.InitHeaderJunkSize)
|
||||||
}
|
}
|
||||||
if device.aSecCfg.responsePacketJunkSize != 0 {
|
if device.awg.ASecCfg.ResponseHeaderJunkSize != 0 {
|
||||||
sendf("s2=%d", device.aSecCfg.responsePacketJunkSize)
|
sendf("s2=%d", device.awg.ASecCfg.ResponseHeaderJunkSize)
|
||||||
}
|
}
|
||||||
if device.aSecCfg.initPacketMagicHeader != 0 {
|
if device.awg.ASecCfg.CookieReplyHeaderJunkSize != 0 {
|
||||||
sendf("h1=%d", device.aSecCfg.initPacketMagicHeader)
|
sendf("s3=%d", device.awg.ASecCfg.CookieReplyHeaderJunkSize)
|
||||||
}
|
}
|
||||||
if device.aSecCfg.responsePacketMagicHeader != 0 {
|
if device.awg.ASecCfg.TransportHeaderJunkSize != 0 {
|
||||||
sendf("h2=%d", device.aSecCfg.responsePacketMagicHeader)
|
sendf("s4=%d", device.awg.ASecCfg.TransportHeaderJunkSize)
|
||||||
}
|
}
|
||||||
if device.aSecCfg.underloadPacketMagicHeader != 0 {
|
if device.awg.ASecCfg.InitPacketMagicHeader != 0 {
|
||||||
sendf("h3=%d", device.aSecCfg.underloadPacketMagicHeader)
|
sendf("h1=%d", device.awg.ASecCfg.InitPacketMagicHeader)
|
||||||
}
|
}
|
||||||
if device.aSecCfg.transportPacketMagicHeader != 0 {
|
if device.awg.ASecCfg.ResponsePacketMagicHeader != 0 {
|
||||||
sendf("h4=%d", device.aSecCfg.transportPacketMagicHeader)
|
sendf("h2=%d", device.awg.ASecCfg.ResponsePacketMagicHeader)
|
||||||
|
}
|
||||||
|
if device.awg.ASecCfg.UnderloadPacketMagicHeader != 0 {
|
||||||
|
sendf("h3=%d", device.awg.ASecCfg.UnderloadPacketMagicHeader)
|
||||||
|
}
|
||||||
|
if device.awg.ASecCfg.TransportPacketMagicHeader != 0 {
|
||||||
|
sendf("h4=%d", device.awg.ASecCfg.TransportPacketMagicHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
specialJunkIpcFields := device.awg.HandshakeHandler.SpecialJunk.IpcGetFields()
|
||||||
|
for _, field := range specialJunkIpcFields {
|
||||||
|
sendf("%s=%s", field.Key, field.Value)
|
||||||
|
}
|
||||||
|
controlledJunkIpcFields := device.awg.HandshakeHandler.ControlledJunk.IpcGetFields()
|
||||||
|
for _, field := range controlledJunkIpcFields {
|
||||||
|
sendf("%s=%s", field.Key, field.Value)
|
||||||
|
}
|
||||||
|
if device.awg.HandshakeHandler.ITimeout != 0 {
|
||||||
|
sendf("itime=%d", device.awg.HandshakeHandler.ITimeout/time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, peer := range device.peers.keyMap {
|
for _, peer := range device.peers.keyMap {
|
||||||
// Serialize peer state.
|
// Serialize peer state.
|
||||||
// Do the work in an anonymous function so that we can use defer.
|
peer.handshake.mutex.RLock()
|
||||||
func() {
|
keyf("public_key", (*[32]byte)(&peer.handshake.remoteStatic))
|
||||||
peer.RLock()
|
keyf("preshared_key", (*[32]byte)(&peer.handshake.presharedKey))
|
||||||
defer peer.RUnlock()
|
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))
|
nano := peer.lastHandshakeNano.Load()
|
||||||
keyf("preshared_key", (*[32]byte)(&peer.handshake.presharedKey))
|
secs := nano / time.Second.Nanoseconds()
|
||||||
sendf("protocol_version=1")
|
nano %= time.Second.Nanoseconds()
|
||||||
if peer.endpoint != nil {
|
|
||||||
sendf("endpoint=%s", peer.endpoint.DstToString())
|
|
||||||
}
|
|
||||||
|
|
||||||
nano := peer.lastHandshakeNano.Load()
|
sendf("last_handshake_time_sec=%d", secs)
|
||||||
secs := nano / time.Second.Nanoseconds()
|
sendf("last_handshake_time_nsec=%d", nano)
|
||||||
nano %= time.Second.Nanoseconds()
|
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)
|
device.allowedips.EntriesForPeer(peer, func(prefix netip.Prefix) bool {
|
||||||
sendf("last_handshake_time_nsec=%d", nano)
|
sendf("allowed_ip=%s", prefix.String())
|
||||||
sendf("tx_bytes=%d", peer.txBytes.Load())
|
return true
|
||||||
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
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -185,13 +199,16 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||||
peer := new(ipcSetPeer)
|
peer := new(ipcSetPeer)
|
||||||
deviceConfig := true
|
deviceConfig := true
|
||||||
|
|
||||||
tempASecCfg := aSecCfgType{}
|
tempAwg := awg.Protocol{}
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := bufio.NewScanner(r)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
if line == "" {
|
if line == "" {
|
||||||
// Blank line means terminate operation.
|
// Blank line means terminate operation.
|
||||||
device.handlePostConfig(&tempASecCfg)
|
err := device.handlePostConfig(&tempAwg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
peer.handlePostConfig()
|
peer.handlePostConfig()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -219,7 +236,7 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if deviceConfig {
|
if deviceConfig {
|
||||||
err = device.handleDeviceLine(key, value, &tempASecCfg)
|
err = device.handleDeviceLine(key, value, &tempAwg)
|
||||||
} else {
|
} else {
|
||||||
err = device.handlePeerLine(peer, key, value)
|
err = device.handlePeerLine(peer, key, value)
|
||||||
}
|
}
|
||||||
|
@ -227,7 +244,10 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
device.handlePostConfig(&tempASecCfg)
|
err = device.handlePostConfig(&tempAwg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
peer.handlePostConfig()
|
peer.handlePostConfig()
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
|
@ -236,13 +256,13 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) handleDeviceLine(key, value string, tempASecCfg *aSecCfgType) error {
|
func (device *Device) handleDeviceLine(key, value string, tempAwg *awg.Protocol) error {
|
||||||
switch key {
|
switch key {
|
||||||
case "private_key":
|
case "private_key":
|
||||||
var sk NoisePrivateKey
|
var sk NoisePrivateKey
|
||||||
err := sk.FromMaybeZeroHex(value)
|
err := sk.FromMaybeZeroHex(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"failed to set private_key: %w",err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set private_key: %w", err)
|
||||||
}
|
}
|
||||||
device.log.Verbosef("UAPI: Updating private key")
|
device.log.Verbosef("UAPI: Updating private key")
|
||||||
device.SetPrivateKey(sk)
|
device.SetPrivateKey(sk)
|
||||||
|
@ -250,7 +270,7 @@ func (device *Device) handleDeviceLine(key, value string, tempASecCfg *aSecCfgTy
|
||||||
case "listen_port":
|
case "listen_port":
|
||||||
port, err := strconv.ParseUint(value, 10, 16)
|
port, err := strconv.ParseUint(value, 10, 16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"failed to parse listen_port: %w",err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse listen_port: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update port and rebind
|
// update port and rebind
|
||||||
|
@ -261,7 +281,7 @@ func (device *Device) handleDeviceLine(key, value string, tempASecCfg *aSecCfgTy
|
||||||
device.net.Unlock()
|
device.net.Unlock()
|
||||||
|
|
||||||
if err := device.BindUpdate(); err != nil {
|
if err := device.BindUpdate(); err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorPortInUse,"failed to set listen_port: %w",err)
|
return ipcErrorf(ipc.IpcErrorPortInUse, "failed to set listen_port: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "fwmark":
|
case "fwmark":
|
||||||
|
@ -272,12 +292,16 @@ func (device *Device) handleDeviceLine(key, value string, tempASecCfg *aSecCfgTy
|
||||||
|
|
||||||
device.log.Verbosef("UAPI: Updating fwmark")
|
device.log.Verbosef("UAPI: Updating fwmark")
|
||||||
if err := device.BindSetMark(uint32(mark)); err != nil {
|
if err := device.BindSetMark(uint32(mark)); err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorPortInUse,"failed to update fwmark: %w", err)
|
return ipcErrorf(ipc.IpcErrorPortInUse, "failed to update fwmark: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "replace_peers":
|
case "replace_peers":
|
||||||
if value != "true" {
|
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.log.Verbosef("UAPI: Removing all peers")
|
||||||
device.RemoveAllPeers()
|
device.RemoveAllPeers()
|
||||||
|
@ -285,67 +309,140 @@ func (device *Device) handleDeviceLine(key, value string, tempASecCfg *aSecCfgTy
|
||||||
case "jc":
|
case "jc":
|
||||||
junkPacketCount, err := strconv.Atoi(value)
|
junkPacketCount, err := strconv.Atoi(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse junk_packet_count %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse junk_packet_count %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.junkPacketCount = junkPacketCount
|
device.log.Verbosef("UAPI: Updating junk_packet_count")
|
||||||
|
tempAwg.ASecCfg.JunkPacketCount = junkPacketCount
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
case "jmin":
|
case "jmin":
|
||||||
junkPacketMinSize, err := strconv.Atoi(value)
|
junkPacketMinSize, err := strconv.Atoi(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"faield to parse junk_packet_min_size %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse junk_packet_min_size %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.junkPacketMinSize = junkPacketMinSize
|
device.log.Verbosef("UAPI: Updating junk_packet_min_size")
|
||||||
|
tempAwg.ASecCfg.JunkPacketMinSize = junkPacketMinSize
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
case "jmax":
|
case "jmax":
|
||||||
junkPacketMaxSize, err := strconv.Atoi(value)
|
junkPacketMaxSize, err := strconv.Atoi(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"faield to parse junk_packet_max_size %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse junk_packet_max_size %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.junkPacketMaxSize = junkPacketMaxSize
|
device.log.Verbosef("UAPI: Updating junk_packet_max_size")
|
||||||
|
tempAwg.ASecCfg.JunkPacketMaxSize = junkPacketMaxSize
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
case "s1":
|
case "s1":
|
||||||
initPacketJunkSize, err := strconv.Atoi(value)
|
initPacketJunkSize, err := strconv.Atoi(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"faield to parse init_packet_junk_size %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse init_packet_junk_size %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.initPacketJunkSize = initPacketJunkSize
|
device.log.Verbosef("UAPI: Updating init_packet_junk_size")
|
||||||
|
tempAwg.ASecCfg.InitHeaderJunkSize = initPacketJunkSize
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
case "s2":
|
case "s2":
|
||||||
responsePacketJunkSize, err := strconv.Atoi(value)
|
responsePacketJunkSize, err := strconv.Atoi(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"faield to parse response_packet_junk_size %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse response_packet_junk_size %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.responsePacketJunkSize = responsePacketJunkSize
|
device.log.Verbosef("UAPI: Updating response_packet_junk_size")
|
||||||
|
tempAwg.ASecCfg.ResponseHeaderJunkSize = responsePacketJunkSize
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
|
case "s3":
|
||||||
|
cookieReplyPacketJunkSize, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse cookie_reply_packet_junk_size %w", err)
|
||||||
|
}
|
||||||
|
device.log.Verbosef("UAPI: Updating cookie_reply_packet_junk_size")
|
||||||
|
tempAwg.ASecCfg.CookieReplyHeaderJunkSize = cookieReplyPacketJunkSize
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
|
case "s4":
|
||||||
|
transportPacketJunkSize, err := strconv.Atoi(value)
|
||||||
|
if err != nil {
|
||||||
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse transport_packet_junk_size %w", err)
|
||||||
|
}
|
||||||
|
device.log.Verbosef("UAPI: Updating transport_packet_junk_size")
|
||||||
|
tempAwg.ASecCfg.TransportHeaderJunkSize = transportPacketJunkSize
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
case "h1":
|
case "h1":
|
||||||
initPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
initPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"faield to parse init_packet_magic_header %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse init_packet_magic_header %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.initPacketMagicHeader = uint32(initPacketMagicHeader)
|
tempAwg.ASecCfg.InitPacketMagicHeader = uint32(initPacketMagicHeader)
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
case "h2":
|
case "h2":
|
||||||
responsePacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
responsePacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"faield to parse response_packet_magic_header %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse response_packet_magic_header %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.responsePacketMagicHeader = uint32(responsePacketMagicHeader)
|
tempAwg.ASecCfg.ResponsePacketMagicHeader = uint32(responsePacketMagicHeader)
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
case "h3":
|
case "h3":
|
||||||
underloadPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
underloadPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"faield to parse underload_packet_magic_header %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse underload_packet_magic_header %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.underloadPacketMagicHeader = uint32(underloadPacketMagicHeader)
|
tempAwg.ASecCfg.UnderloadPacketMagicHeader = uint32(underloadPacketMagicHeader)
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
|
||||||
case "h4":
|
case "h4":
|
||||||
transportPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
transportPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"faield to parse transport_packet_magic_header %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse transport_packet_magic_header %w", err)
|
||||||
}
|
}
|
||||||
tempASecCfg.transportPacketMagicHeader = uint32(transportPacketMagicHeader)
|
tempAwg.ASecCfg.TransportPacketMagicHeader = uint32(transportPacketMagicHeader)
|
||||||
|
tempAwg.ASecCfg.IsSet = true
|
||||||
|
case "i1", "i2", "i3", "i4", "i5":
|
||||||
|
if len(value) == 0 {
|
||||||
|
device.log.Verbosef("UAPI: received empty %s", key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
generators, err := awg.Parse(key, value)
|
||||||
|
if err != nil {
|
||||||
|
return ipcErrorf(ipc.IpcErrorInvalid, "invalid %s: %w", key, err)
|
||||||
|
}
|
||||||
|
device.log.Verbosef("UAPI: Updating %s", key)
|
||||||
|
tempAwg.HandshakeHandler.SpecialJunk.AppendGenerator(generators)
|
||||||
|
tempAwg.HandshakeHandler.IsSet = true
|
||||||
|
case "j1", "j2", "j3":
|
||||||
|
if len(value) == 0 {
|
||||||
|
device.log.Verbosef("UAPI: received empty %s", key)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
generators, err := awg.Parse(key, value)
|
||||||
|
if err != nil {
|
||||||
|
return ipcErrorf(ipc.IpcErrorInvalid, "invalid %s: %w", key, err)
|
||||||
|
}
|
||||||
|
device.log.Verbosef("UAPI: Updating %s", key)
|
||||||
|
|
||||||
|
tempAwg.HandshakeHandler.ControlledJunk.AppendGenerator(generators)
|
||||||
|
tempAwg.HandshakeHandler.IsSet = true
|
||||||
|
case "itime":
|
||||||
|
if len(value) == 0 {
|
||||||
|
device.log.Verbosef("UAPI: received empty itime")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
itime, err := strconv.ParseInt(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return ipcErrorf(ipc.IpcErrorInvalid, "parse itime %w", err)
|
||||||
|
}
|
||||||
|
device.log.Verbosef("UAPI: Updating itime")
|
||||||
|
|
||||||
|
tempAwg.HandshakeHandler.ITimeout = time.Duration(itime) * time.Second
|
||||||
|
tempAwg.HandshakeHandler.IsSet = true
|
||||||
default:
|
default:
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid,"invalid UAPI device key: %v",key)
|
return ipcErrorf(ipc.IpcErrorInvalid, "invalid UAPI device key: %v", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -364,8 +461,7 @@ func (peer *ipcSetPeer) handlePostConfig() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if peer.created {
|
if peer.created {
|
||||||
peer.disableRoaming = peer.device.net.brokenRoaming &&
|
peer.endpoint.disableRoaming = peer.device.net.brokenRoaming && peer.endpoint.val != nil
|
||||||
peer.endpoint != nil
|
|
||||||
}
|
}
|
||||||
if peer.device.isUp() {
|
if peer.device.isUp() {
|
||||||
peer.Start()
|
peer.Start()
|
||||||
|
@ -417,7 +513,11 @@ func (device *Device) handlePeerLine(
|
||||||
case "update_only":
|
case "update_only":
|
||||||
// allow disabling of creation
|
// allow disabling of creation
|
||||||
if value != "true" {
|
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 {
|
if peer.created && !peer.dummy {
|
||||||
device.RemovePeer(peer.handshake.remoteStatic)
|
device.RemovePeer(peer.handshake.remoteStatic)
|
||||||
|
@ -452,18 +552,22 @@ func (device *Device) handlePeerLine(
|
||||||
device.log.Verbosef("%v - UAPI: Updating endpoint", peer.Peer)
|
device.log.Verbosef("%v - UAPI: Updating endpoint", peer.Peer)
|
||||||
endpoint, err := device.net.bind.ParseEndpoint(value)
|
endpoint, err := device.net.bind.ParseEndpoint(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set endpoint %v: %w", value, err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set endpoint %v: %w", value, err)
|
||||||
}
|
}
|
||||||
peer.Lock()
|
peer.endpoint.Lock()
|
||||||
defer peer.Unlock()
|
defer peer.endpoint.Unlock()
|
||||||
peer.endpoint = endpoint
|
peer.endpoint.val = endpoint
|
||||||
|
|
||||||
case "persistent_keepalive_interval":
|
case "persistent_keepalive_interval":
|
||||||
device.log.Verbosef("%v - UAPI: Updating persistent keepalive interval", peer.Peer)
|
device.log.Verbosef("%v - UAPI: Updating persistent keepalive interval", peer.Peer)
|
||||||
|
|
||||||
secs, err := strconv.ParseUint(value, 10, 16)
|
secs, err := strconv.ParseUint(value, 10, 16)
|
||||||
if err != nil {
|
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))
|
old := peer.persistentKeepaliveInterval.Swap(uint32(secs))
|
||||||
|
@ -474,7 +578,11 @@ func (device *Device) handlePeerLine(
|
||||||
case "replace_allowed_ips":
|
case "replace_allowed_ips":
|
||||||
device.log.Verbosef("%v - UAPI: Removing all allowedips", peer.Peer)
|
device.log.Verbosef("%v - UAPI: Removing all allowedips", peer.Peer)
|
||||||
if value != "true" {
|
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 {
|
if peer.dummy {
|
||||||
return nil
|
return nil
|
||||||
|
@ -482,7 +590,14 @@ func (device *Device) handlePeerLine(
|
||||||
device.allowedips.RemoveByPeer(peer.Peer)
|
device.allowedips.RemoveByPeer(peer.Peer)
|
||||||
|
|
||||||
case "allowed_ip":
|
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)
|
prefix, err := netip.ParsePrefix(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set allowed ip: %w", err)
|
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set allowed ip: %w", err)
|
||||||
|
@ -490,7 +605,11 @@ func (device *Device) handlePeerLine(
|
||||||
if peer.dummy {
|
if peer.dummy {
|
||||||
return nil
|
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":
|
case "protocol_version":
|
||||||
if value != "1" {
|
if value != "1" {
|
||||||
|
@ -542,7 +661,11 @@ func (device *Device) IpcHandle(socket net.Conn) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if nextByte != '\n' {
|
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
|
break
|
||||||
}
|
}
|
||||||
err = device.IpcGetOperation(buffered.Writer)
|
err = device.IpcGetOperation(buffered.Writer)
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package device
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
crand "crypto/rand"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func appendJunk(writer *bytes.Buffer, size int) error {
|
|
||||||
headerJunk, err := randomJunkWithSize(size)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create header junk: %v", err)
|
|
||||||
}
|
|
||||||
_, err = writer.Write(headerJunk)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to write header junk: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomJunkWithSize(size int) ([]byte, error) {
|
|
||||||
junk := make([]byte, size)
|
|
||||||
_, err := crand.Read(junk)
|
|
||||||
return junk, err
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package device
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_randomJunktWithSize(t *testing.T) {
|
|
||||||
junk, err := randomJunkWithSize(30)
|
|
||||||
fmt.Println(string(junk), len(junk), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_appendJunk(t *testing.T) {
|
|
||||||
t.Run("", func(t *testing.T) {
|
|
||||||
s := "apple"
|
|
||||||
buffer := bytes.NewBuffer([]byte(s))
|
|
||||||
err := appendJunk(buffer, 30)
|
|
||||||
if err != nil &&
|
|
||||||
buffer.Len() != len(s)+30 {
|
|
||||||
t.Errorf("appendWithJunk() size don't match")
|
|
||||||
}
|
|
||||||
read := make([]byte, 50)
|
|
||||||
buffer.Read(read)
|
|
||||||
fmt.Println(string(read))
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
22
go.mod
22
go.mod
|
@ -1,17 +1,23 @@
|
||||||
module github.com/amnezia-vpn/amnezia-wg
|
module github.com/amnezia-vpn/amneziawg-go
|
||||||
|
|
||||||
go 1.20
|
go 1.24.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/stretchr/testify v1.10.0
|
||||||
|
github.com/tevino/abool v1.2.0
|
||||||
github.com/tevino/abool/v2 v2.1.0
|
github.com/tevino/abool/v2 v2.1.0
|
||||||
golang.org/x/crypto v0.6.0
|
go.uber.org/atomic v1.11.0
|
||||||
golang.org/x/net v0.7.0
|
golang.org/x/crypto v0.39.0
|
||||||
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89
|
golang.org/x/net v0.41.0
|
||||||
|
golang.org/x/sys v0.33.0
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
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 (
|
require (
|
||||||
github.com/google/btree v1.0.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // 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
|
||||||
)
|
)
|
||||||
|
|
48
go.sum
48
go.sum
|
@ -1,16 +1,40 @@
|
||||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
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/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
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=
|
||||||
github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c=
|
github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c=
|
||||||
github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY=
|
github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY=
|
||||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||||
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89 h1:260HNjMTPDya+jq5AM1zZLgG9pv9GASPAGiEEJUbRg4=
|
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||||
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
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=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U6A2rpebLs/MtTwFacjKb8jLdQA=
|
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=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 h1:sfK5nHuG7lRFZ2FdTT3RimOqWBg8IrVm+/Vko1FVOsk=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20250606233247-e3c4c4cad86f h1:zmc4cHEcCudRt2O8VsCW7nYLfAsbVY2i910/DAop1TM=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20250606233247-e3c4c4cad86f/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/ipc/namedpipe"
|
"github.com/amnezia-vpn/amneziawg-go/ipc/namedpipe"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ipc
|
package ipc
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ipc
|
package ipc
|
||||||
|
@ -9,7 +9,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/rwcancel"
|
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ipc
|
package ipc
|
||||||
|
@ -26,7 +26,7 @@ const (
|
||||||
|
|
||||||
// socketDirectory is variable because it is modified by a linker
|
// socketDirectory is variable because it is modified by a linker
|
||||||
// flag in wireguard-android.
|
// flag in wireguard-android.
|
||||||
var socketDirectory = "/var/run/wireguard"
|
var socketDirectory = "/var/run/amneziawg"
|
||||||
|
|
||||||
func sockPath(iface string) string {
|
func sockPath(iface string) string {
|
||||||
return fmt.Sprintf("%s/%s.sock", socketDirectory, iface)
|
return fmt.Sprintf("%s/%s.sock", socketDirectory, iface)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ipc
|
package ipc
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ipc
|
package ipc
|
||||||
|
@ -8,7 +8,7 @@ package ipc
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/ipc/namedpipe"
|
"github.com/amnezia-vpn/amneziawg-go/ipc/namedpipe"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ func init() {
|
||||||
func UAPIListen(name string) (net.Listener, error) {
|
func UAPIListen(name string) (net.Listener, error) {
|
||||||
listener, err := (&namedpipe.ListenConfig{
|
listener, err := (&namedpipe.ListenConfig{
|
||||||
SecurityDescriptor: UAPISecurityDescriptor,
|
SecurityDescriptor: UAPISecurityDescriptor,
|
||||||
}).Listen(`\\.\pipe\ProtectedPrefix\Administrators\WireGuard\` + name)
|
}).Listen(`\\.\pipe\ProtectedPrefix\Administrators\AmneziaWG\` + name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
32
main.go
32
main.go
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
@ -14,10 +14,10 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/device"
|
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/ipc"
|
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tun"
|
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,20 +46,20 @@ func warning() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────┐")
|
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────────────┐")
|
||||||
fmt.Fprintln(os.Stderr, "│ │")
|
fmt.Fprintln(os.Stderr, "│ │")
|
||||||
fmt.Fprintln(os.Stderr, "│ Running wireguard-go is not required because this │")
|
fmt.Fprintln(os.Stderr, "│ Running amneziawg-go is not required because this │")
|
||||||
fmt.Fprintln(os.Stderr, "│ kernel has first class support for WireGuard. For │")
|
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, "│ information on installing the kernel module, │")
|
||||||
fmt.Fprintln(os.Stderr, "│ please visit: │")
|
fmt.Fprintln(os.Stderr, "│ please visit: │")
|
||||||
fmt.Fprintln(os.Stderr, "│ https://www.wireguard.com/install/ │")
|
fmt.Fprintln(os.Stderr, "| https://github.com/amnezia-vpn/amneziawg-linux-kernel-module │")
|
||||||
fmt.Fprintln(os.Stderr, "│ │")
|
fmt.Fprintln(os.Stderr, "│ │")
|
||||||
fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────┘")
|
fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────────────┘")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) == 2 && os.Args[1] == "--version" {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ func main() {
|
||||||
fmt.Sprintf("(%s) ", interfaceName),
|
fmt.Sprintf("(%s) ", interfaceName),
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.Verbosef("Starting wireguard-go version %s", Version)
|
logger.Verbosef("Starting amneziawg-go version %s", Version)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Failed to create TUN device: %v", err)
|
logger.Errorf("Failed to create TUN device: %v", err)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
@ -12,11 +12,11 @@ import (
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/conn"
|
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/device"
|
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||||
"github.com/amnezia-vpn/amnezia-wg/ipc"
|
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||||
|
|
||||||
"github.com/amnezia-vpn/amnezia-wg/tun"
|
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -30,13 +30,13 @@ func main() {
|
||||||
}
|
}
|
||||||
interfaceName := os.Args[1]
|
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(
|
logger := device.NewLogger(
|
||||||
device.LogLevelVerbose,
|
device.LogLevelVerbose,
|
||||||
fmt.Sprintf("(%s) ", interfaceName),
|
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)
|
tun, err := tun.CreateTUN(interfaceName, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ratelimiter
|
package ratelimiter
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ratelimiter
|
package ratelimiter
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* 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.
|
// Package replay implements an efficient anti-replay algorithm as specified in RFC 6479.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package replay
|
package replay
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/* SPDX-License-Identifier: MIT
|
/* 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
|
// Package rwcancel implements cancelable read/write operations on
|
||||||
|
@ -64,7 +64,7 @@ func (rw *RWCancel) ReadyRead() bool {
|
||||||
|
|
||||||
func (rw *RWCancel) ReadyWrite() bool {
|
func (rw *RWCancel) ReadyWrite() bool {
|
||||||
closeFd := int32(rw.closingReader.Fd())
|
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
|
var err error
|
||||||
for {
|
for {
|
||||||
_, err = unix.Poll(pollFds, -1)
|
_, err = unix.Poll(pollFds, -1)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package tai64n
|
package tai64n
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package tai64n
|
package tai64n
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package tun
|
package tun
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue