mirror of
https://github.com/amnezia-vpn/amneziawg-go.git
synced 2026-10-08 22:04:16 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5928efb6c | ||
|
|
da11c9fadc | ||
|
|
1b86b2ae0e | ||
|
|
08271d00b3 | ||
|
|
75ea550a64 | ||
|
|
1f50ad736e | ||
|
|
08d68cdae2 | ||
|
|
cf9d2dd202 | ||
|
|
7860d60a52 | ||
|
|
ce7cf1034d | ||
|
|
d57d98d55b | ||
|
|
0527dfa476 | ||
|
|
9f5d948bc7 | ||
|
|
457d920a1a | ||
|
|
c1e9bb3758 | ||
|
|
1cc94272ca | ||
|
|
3610f21b75 | ||
|
|
f4f4c99926 | ||
|
|
12a012205e | ||
|
|
e7ef4339e7 | ||
|
|
449d7cffd4 | ||
|
|
e796d477d8 | ||
|
|
730d6c39d0 | ||
|
|
0361c54dca | ||
|
|
f6542209f4 | ||
|
|
1abd24b5b9 | ||
|
|
3f19f1c657 | ||
|
|
c207898480 | ||
|
|
fe75b639fa | ||
|
|
169ed49a46 | ||
|
|
eeb8aae13e | ||
|
|
99f2e6d66f | ||
|
|
d5359f52f0 | ||
|
|
6768090667 | ||
|
|
2cad62c40b | ||
|
|
8051f17147 | ||
|
|
ace3e11ef2 | ||
|
|
8a2b2bf4f4 | ||
|
|
75d6c67a67 | ||
|
|
ac8a885a03 | ||
|
|
6a7c878409 | ||
|
|
704d57c27a | ||
|
|
c0b6e6a200 | ||
|
|
c803ce1e5b | ||
|
|
deedce495a | ||
|
|
27e661d68e | ||
|
|
71be0eb3a6 | ||
|
|
e3f1273f8a | ||
|
|
c97b5b7615 | ||
|
|
668ddfd455 | ||
|
|
b8da08c106 | ||
|
|
2e3f7d122c | ||
|
|
2e7780471a | ||
|
|
87d8c00f86 | ||
|
|
c00bda9200 | ||
|
|
d2b0fc9789 | ||
|
|
77d39ff3b9 | ||
|
|
e433d13df6 | ||
|
|
3ddf952973 | ||
|
|
3f0a3bcfa0 |
No files matched your search
+14
-7
@@ -1,17 +1,24 @@
|
||||
FROM golang:1.20 as awg
|
||||
FROM golang:1.25.12 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 as tools
|
||||
ARG AWGTOOLS_COMMIT="v3.1.20260812"
|
||||
|
||||
RUN apk add --no-cache git build-base linux-headers && \
|
||||
git clone https://github.com/amnezia-vpn/amneziawg-tools.git /amneziawg-tools && \
|
||||
cd /amneziawg-tools && git checkout ${AWGTOOLS_COMMIT} && \
|
||||
cd src && make
|
||||
|
||||
FROM alpine:3.19
|
||||
ARG AWGTOOLS_RELEASE="1.0.20240213"
|
||||
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 && \
|
||||
|
||||
RUN apk --no-cache add iproute2 iptables bash
|
||||
COPY --from=tools /amneziawg-tools/src/wg /usr/bin/awg
|
||||
COPY --from=tools /amneziawg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
|
||||
RUN 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
|
||||
@@ -1,3 +1,5 @@
|
||||
Copyright (C) 2017-2025 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
|
||||
|
||||
@@ -9,7 +9,7 @@ MAKEFLAGS += --no-print-directory
|
||||
|
||||
generate-version-and-build:
|
||||
@export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \
|
||||
tag="$$(git describe --dirty 2>/dev/null)" && \
|
||||
tag="$$(git describe --tags --dirty 2>/dev/null)" && \
|
||||
ver="$$(printf 'package main\n\nconst Version = "%s"\n' "$$tag")" && \
|
||||
[ "$$(cat version.go 2>/dev/null)" != "$$ver" ] && \
|
||||
echo "$$ver" > version.go && \
|
||||
|
||||
@@ -21,7 +21,7 @@ To run amneziawg-go without forking to the background, pass `-f` or `--foregroun
|
||||
```
|
||||
$ amneziawg-go -f wg0
|
||||
```
|
||||
When an interface is running, you may use [`amnezia-wg-tools `](https://github.com/amnezia-vpn/amneziawg-go-tools) to configure it, as well as the usual `ip(8)` and `ifconfig(8)` commands.
|
||||
When an interface is running, you may use [`amneziawg-tools `](https://github.com/amnezia-vpn/amneziawg-tools) to configure it, as well as the usual `ip(8)` and `ifconfig(8)` commands.
|
||||
|
||||
To run with more logging you may set the environment variable `LOG_LEVEL=debug`.
|
||||
|
||||
@@ -34,11 +34,11 @@ This will run on Linux; you should run amnezia-wg instead of using default linux
|
||||
### macOS
|
||||
|
||||
This runs on macOS using the utun driver. It does not yet support sticky sockets, and won't support fwmarks because of Darwin limitations. Since the utun driver cannot have arbitrary interface names, you must either use `utun[0-9]+` for an explicit interface name or `utun` to have the kernel select one for you. If you choose `utun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
|
||||
This runs on MacOS, you should use it from [awg-apple](https://github.com/amnezia-vpn/awg-apple)
|
||||
This runs on MacOS, you should use it from [amneziawg-apple](https://github.com/amnezia-vpn/amneziawg-apple)
|
||||
|
||||
### Windows
|
||||
|
||||
This runs on Windows, you should use it from [awg-windows](https://github.com/amnezia-vpn/awg-windows), 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.
|
||||
|
||||
|
||||
## Building
|
||||
@@ -50,3 +50,114 @@ $ git clone https://github.com/amnezia-vpn/amneziawg-go
|
||||
$ cd amneziawg-go
|
||||
$ make
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Data types and definitions
|
||||
|
||||
`client-side` means the param is not required to be the same on both server and client, while
|
||||
`server-side` means this is mandatory the values are the same on server and on client
|
||||
|
||||
`range,x`, where x is the underlying type of left and right
|
||||
* Format: "a-b", or "a", or "(off)"
|
||||
* Example: PersistentKeepalive = 22-30
|
||||
|
||||
> [!NOTE]
|
||||
> If there is no value specified (for any param), AWG treats it as 0
|
||||
|
||||
### Header protection [AWG 3+]
|
||||
|
||||
Header protection is the mechanism of protecting low-entropy values of packets' headers. The idea is to apply fast encryption to the specific fields which WireGuard does use for authentication and its own encryption. The cipher uses `S1-S4` crypto padding as nonce for each incoming packet.
|
||||
|
||||
```
|
||||
[Device]
|
||||
+ HeaderProtectionKey: key,string - server-side # the key to be used in header protection
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Use `awg genkey` to generate header protection key
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Header protection requires `S1-S4` value to be 12 at least
|
||||
|
||||
### Content padding [AWG 3+]
|
||||
|
||||
```
|
||||
[Device]
|
||||
+ ContentPaddingAddition: uint32,range - client-side # the range to be used as a custom padding
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> It's important to specify content padding on both sides. However, this is not strictly required and could be omitted.
|
||||
|
||||
### Timings [AWG 3+]
|
||||
|
||||
This param could be used to customize default Wireguard's timings
|
||||
|
||||
```
|
||||
[Device]
|
||||
+ RekeyAfterTime = "uint32,range - client-side - seconds" # time, after which client tries to handshake
|
||||
+ RekeyTimeout = "uint32,range - client-side - seconds" # timeout, after which handshake is repeated
|
||||
+ RejectAfterTime = "uint32,range - client-side - seconds" # time, after which client forces handshake, and declines all incoming data
|
||||
+ KeepaliveTimeout = "uint32,range - client-side - seconds" # time from last data sending, after which keepalive is sent
|
||||
+ MaxHandshakeAttempts = "uint32,range - client-side - amount" # maximum attempts of handshake repetition
|
||||
|
||||
[Peer]
|
||||
M PersistentKeepalive = "uint32,range - client-side - seconds" # interval of persistent keepalive
|
||||
```
|
||||
|
||||
### Junk packets
|
||||
|
||||
The amount of junk packets specified in `Jc` with a random size between `Jmin` and `Jmax` would be generated and sent prior every handshake
|
||||
|
||||
- `Jc: int`, recommended range is 4-12
|
||||
- `Jmin: int` <= `Jmax:int`
|
||||
|
||||
> [!TIP]
|
||||
> Junk packets do not carry any actual data, so there is no need to specify it on both sides. General recommendation is to use it on the client side only
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If Jmax >= system MTU (not the one specified in AWG), then the system can fracture this packet into fragments, which looks suspicious from the censor side
|
||||
|
||||
### Message paddings
|
||||
|
||||
- `S1: int` - padding of handshake initial message
|
||||
- `S2: int` - padding of handshake response message
|
||||
- `S3: int` - padding of handshake cookie message
|
||||
- `S4: int` - padding of transport messages
|
||||
|
||||
### Message headers
|
||||
|
||||
Every message in wireguard has `uint32` type at the beginning of the packet. This field could be controlled by specifying the params below:
|
||||
|
||||
- `H1: string` - header range of handshake initial message
|
||||
- `H2: string` - header range of handshake response message
|
||||
- `H3: string` - header range of handshake cookie message
|
||||
- `H4: string` - header range of transport message
|
||||
|
||||
Values could be specified as:
|
||||
- range: `x-y`, x <= y; e.g. `123-456`
|
||||
- single value `1234`
|
||||
|
||||
### Custom signature packets
|
||||
|
||||
These packets are being send prior to every handshake, in the same way as Junk packets do. The sending order is `I1`, `I2`, `I3`, `I4`, `I5`. If there is no value specified, the packet is skipped.
|
||||
|
||||
- `I1: string`
|
||||
- `I2: string`
|
||||
- `I3: string`
|
||||
- `I4: string`
|
||||
- `I5: string`
|
||||
|
||||
Value is a sequence of tags specified below:
|
||||
- `<b 0x[seq]>` - static bytes tag. Dumps `[seq]` as-is to the packet. `[seq]` is hex-encoded sequence which represents bytes sequence (2 hex numbers per byte) and is always even-sized
|
||||
- `<r [size]>` - random bytes tag. Dumps `[size]` amount of randomly-generated bytes to the packet
|
||||
- `<rd [size]>` - random digits tag. Dumps `[size]` amount of randomly-generated bytes from `[0-9]` set to the packet
|
||||
- `<rc [size]>` - random chars tag. Dumps `[size]` amount of randomly-generated bytes from `[a-zA-Z] set to the packet
|
||||
- `<t>` - timestamp tag. Dumps 4-bytes long current system time in UNIX format
|
||||
|
||||
> [!TIP]
|
||||
> Custom signature packets does not carry any actual data, so there is no need to specify it on both sides. General recommendation is to use it on the client side only
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If the final size of any packet exceeds system MTU, it would be fractured into fragments, which looks suspicious
|
||||
+2
-7
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -298,11 +298,6 @@ func (s *StdNetBind) BatchSize() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (s *StdNetBind) GetOffloadInfo() string {
|
||||
return fmt.Sprintf("ipv4TxOffload: %v, ipv4RxOffload: %v\nipv6TxOffload: %v, ipv6RxOffload: %v",
|
||||
s.ipv4TxOffload, s.ipv4RxOffload, s.ipv6TxOffload, s.ipv6RxOffload)
|
||||
}
|
||||
|
||||
func (s *StdNetBind) Close() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@@ -336,7 +331,7 @@ type ErrUDPGSODisabled struct {
|
||||
}
|
||||
|
||||
func (e ErrUDPGSODisabled) Error() string {
|
||||
return fmt.Sprintf("disabled UDP GSO on %s, NIC(s) may not support checksum offload", e.onLaddr)
|
||||
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 {
|
||||
|
||||
+20
-6
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn/winrio"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn/winrio"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -328,10 +328,6 @@ func (bind *WinRingBind) BatchSize() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (bind *WinRingBind) GetOffloadInfo() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (bind *WinRingBind) SetMark(mark uint32) error {
|
||||
return nil
|
||||
}
|
||||
@@ -586,6 +582,24 @@ func (bind *WinRingBind) BindSocketToInterface6(interfaceIndex uint32, blackhole
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bind *WinRingBind) PeekLookAtSocketFd4() (fd int, err error) {
|
||||
bind.mu.RLock()
|
||||
defer bind.mu.RUnlock()
|
||||
if bind.isOpen.Load() != 1 {
|
||||
return -1, net.ErrClosed
|
||||
}
|
||||
return int(bind.v4.sock), nil
|
||||
}
|
||||
|
||||
func (bind *WinRingBind) PeekLookAtSocketFd6() (fd int, err error) {
|
||||
bind.mu.RLock()
|
||||
defer bind.mu.RUnlock()
|
||||
if bind.isOpen.Load() != 1 {
|
||||
return -1, net.ErrClosed
|
||||
}
|
||||
return int(bind.v6.sock), nil
|
||||
}
|
||||
|
||||
func bindSocketToInterface4(handle windows.Handle, interfaceIndex uint32) error {
|
||||
const IP_UNICAST_IF = 31
|
||||
/* MSDN says for IPv4 this needs to be in net byte order, so that it's like an IP address with leading zeros. */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package bindtest
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"net/netip"
|
||||
"os"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
)
|
||||
|
||||
type ChannelBind struct {
|
||||
@@ -91,8 +91,6 @@ func (c *ChannelBind) Close() error {
|
||||
|
||||
func (c *ChannelBind) BatchSize() int { return 1 }
|
||||
|
||||
func (c *ChannelBind) GetOffloadInfo() string { return "" }
|
||||
|
||||
func (c *ChannelBind) SetMark(mark uint32) error { return nil }
|
||||
|
||||
func (c *ChannelBind) makeReceiveFunc(ch chan []byte) conn.ReceiveFunc {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
+1
-3
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
// Package conn implements WireGuard's network connections.
|
||||
@@ -55,8 +55,6 @@ type Bind interface {
|
||||
// BatchSize is the number of buffers expected to be passed to
|
||||
// the ReceiveFuncs, and the maximum expected to be passed to SendBatch.
|
||||
BatchSize() int
|
||||
|
||||
GetOffloadInfo() string
|
||||
}
|
||||
|
||||
// BindSocketToInterface is implemented by Bind objects that support being
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -13,6 +13,35 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Taken from go/src/internal/syscall/unix/kernel_version_linux.go
|
||||
func kernelVersion() (major, minor int) {
|
||||
var uname unix.Utsname
|
||||
if err := unix.Uname(&uname); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
values [2]int
|
||||
value, vi int
|
||||
)
|
||||
for _, c := range uname.Release {
|
||||
if '0' <= c && c <= '9' {
|
||||
value = (value * 10) + int(c-'0')
|
||||
} else {
|
||||
// Note that we're assuming N.N.N here.
|
||||
// If we see anything else, we are likely to mis-parse it.
|
||||
values[vi] = value
|
||||
vi++
|
||||
if vi >= len(values) {
|
||||
break
|
||||
}
|
||||
value = 0
|
||||
}
|
||||
}
|
||||
|
||||
return values[0], values[1]
|
||||
}
|
||||
|
||||
func init() {
|
||||
controlFns = append(controlFns,
|
||||
|
||||
@@ -57,5 +86,24 @@ func init() {
|
||||
}
|
||||
return err
|
||||
},
|
||||
|
||||
// Attempt to enable UDP_GRO
|
||||
func(network, address string, c syscall.RawConn) error {
|
||||
// Kernels below 5.12 are missing 98184612aca0 ("net:
|
||||
// udp: Add support for getsockopt(..., ..., UDP_GRO,
|
||||
// ..., ...);"), which means we can't read this back
|
||||
// later. We could pipe the return value through to
|
||||
// the rest of the code, but UDP_GRO is kind of buggy
|
||||
// anyway, so just gate this here.
|
||||
major, minor := kernelVersion()
|
||||
if major < 5 || (major == 5 && minor < 12) {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.Control(func(fd uintptr) {
|
||||
_ = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
|
||||
})
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
func errShouldDisableUDPGSO(err error) bool {
|
||||
func errShouldDisableUDPGSO(_ error) bool {
|
||||
return false
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
@@ -20,7 +20,9 @@ func errShouldDisableUDPGSO(err error) bool {
|
||||
// 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
|
||||
return serr.Err == unix.EIO
|
||||
// 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
|
||||
}
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
import "net"
|
||||
|
||||
func supportsUDPOffload(conn *net.UDPConn) (txOffload, rxOffload bool) {
|
||||
func supportsUDPOffload(_ *net.UDPConn) (txOffload, rxOffload bool) {
|
||||
return
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package conn
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package winrio
|
||||
|
||||
+56
-33
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -223,6 +223,60 @@ func (table *AllowedIPs) EntriesForPeer(peer *Peer, cb func(prefix netip.Prefix)
|
||||
}
|
||||
}
|
||||
|
||||
func (node *trieEntry) remove() {
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = nil
|
||||
if node.child[0] != nil && node.child[1] != nil {
|
||||
return
|
||||
}
|
||||
bit := 0
|
||||
if node.child[0] == nil {
|
||||
bit = 1
|
||||
}
|
||||
child := node.child[bit]
|
||||
if child != nil {
|
||||
child.parent = node.parent
|
||||
}
|
||||
*node.parent.parentBit = child
|
||||
if node.child[0] != nil || node.child[1] != nil || node.parent.parentBitType > 1 {
|
||||
node.zeroizePointers()
|
||||
return
|
||||
}
|
||||
parent := (*trieEntry)(unsafe.Pointer(uintptr(unsafe.Pointer(node.parent.parentBit)) - unsafe.Offsetof(node.child) - unsafe.Sizeof(node.child[0])*uintptr(node.parent.parentBitType)))
|
||||
if parent.peer != nil {
|
||||
node.zeroizePointers()
|
||||
return
|
||||
}
|
||||
child = parent.child[node.parent.parentBitType^1]
|
||||
if child != nil {
|
||||
child.parent = parent.parent
|
||||
}
|
||||
*parent.parent.parentBit = child
|
||||
node.zeroizePointers()
|
||||
parent.zeroizePointers()
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) Remove(prefix netip.Prefix, peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
var node *trieEntry
|
||||
var exact bool
|
||||
|
||||
if prefix.Addr().Is6() {
|
||||
ip := prefix.Addr().As16()
|
||||
node, exact = table.IPv6.nodePlacement(ip[:], uint8(prefix.Bits()))
|
||||
} else if prefix.Addr().Is4() {
|
||||
ip := prefix.Addr().As4()
|
||||
node, exact = table.IPv4.nodePlacement(ip[:], uint8(prefix.Bits()))
|
||||
} else {
|
||||
panic(errors.New("removing unknown address type"))
|
||||
}
|
||||
if !exact || node == nil || peer != node.peer {
|
||||
return
|
||||
}
|
||||
node.remove()
|
||||
}
|
||||
|
||||
func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
@@ -230,38 +284,7 @@ func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
|
||||
var next *list.Element
|
||||
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
|
||||
next = elem.Next()
|
||||
node := elem.Value.(*trieEntry)
|
||||
|
||||
node.removeFromPeerEntries()
|
||||
node.peer = nil
|
||||
if node.child[0] != nil && node.child[1] != nil {
|
||||
continue
|
||||
}
|
||||
bit := 0
|
||||
if node.child[0] == nil {
|
||||
bit = 1
|
||||
}
|
||||
child := node.child[bit]
|
||||
if child != nil {
|
||||
child.parent = node.parent
|
||||
}
|
||||
*node.parent.parentBit = child
|
||||
if node.child[0] != nil || node.child[1] != nil || node.parent.parentBitType > 1 {
|
||||
node.zeroizePointers()
|
||||
continue
|
||||
}
|
||||
parent := (*trieEntry)(unsafe.Pointer(uintptr(unsafe.Pointer(node.parent.parentBit)) - unsafe.Offsetof(node.child) - unsafe.Sizeof(node.child[0])*uintptr(node.parent.parentBitType)))
|
||||
if parent.peer != nil {
|
||||
node.zeroizePointers()
|
||||
continue
|
||||
}
|
||||
child = parent.child[node.parent.parentBitType^1]
|
||||
if child != nil {
|
||||
child.parent = parent.parent
|
||||
}
|
||||
*parent.parent.parentBit = child
|
||||
node.zeroizePointers()
|
||||
parent.zeroizePointers()
|
||||
elem.Value.(*trieEntry).remove()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -83,7 +83,7 @@ func TestTrieRandom(t *testing.T) {
|
||||
var peers []*Peer
|
||||
var allowedIPs AllowedIPs
|
||||
|
||||
rand.Seed(1)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
|
||||
for n := 0; n < NumberOfPeers; n++ {
|
||||
peers = append(peers, &Peer{})
|
||||
@@ -91,14 +91,14 @@ func TestTrieRandom(t *testing.T) {
|
||||
|
||||
for n := 0; n < NumberOfAddresses; n++ {
|
||||
var addr4 [4]byte
|
||||
rand.Read(addr4[:])
|
||||
rng.Read(addr4[:])
|
||||
cidr := uint8(rand.Intn(32) + 1)
|
||||
index := rand.Intn(NumberOfPeers)
|
||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4(addr4), int(cidr)), peers[index])
|
||||
slow4 = slow4.Insert(addr4[:], cidr, peers[index])
|
||||
|
||||
var addr6 [16]byte
|
||||
rand.Read(addr6[:])
|
||||
rng.Read(addr6[:])
|
||||
cidr = uint8(rand.Intn(128) + 1)
|
||||
index = rand.Intn(NumberOfPeers)
|
||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(addr6), int(cidr)), peers[index])
|
||||
@@ -109,7 +109,7 @@ func TestTrieRandom(t *testing.T) {
|
||||
for p = 0; ; p++ {
|
||||
for n := 0; n < NumberOfTests; n++ {
|
||||
var addr4 [4]byte
|
||||
rand.Read(addr4[:])
|
||||
rng.Read(addr4[:])
|
||||
peer1 := slow4.Lookup(addr4[:])
|
||||
peer2 := allowedIPs.Lookup(addr4[:])
|
||||
if peer1 != peer2 {
|
||||
@@ -117,7 +117,7 @@ func TestTrieRandom(t *testing.T) {
|
||||
}
|
||||
|
||||
var addr6 [16]byte
|
||||
rand.Read(addr6[:])
|
||||
rng.Read(addr6[:])
|
||||
peer1 = slow6.Lookup(addr6[:])
|
||||
peer2 = allowedIPs.Lookup(addr6[:])
|
||||
if peer1 != peer2 {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -39,12 +39,12 @@ func TestCommonBits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkTrie(peerNumber, addressNumber, addressLength int, b *testing.B) {
|
||||
func benchmarkTrie(peerNumber, addressNumber, _ int, b *testing.B) {
|
||||
var trie *trieEntry
|
||||
var peers []*Peer
|
||||
root := parentIndirection{&trie, 2}
|
||||
|
||||
rand.Seed(1)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
|
||||
const AddressLength = 4
|
||||
|
||||
@@ -54,15 +54,15 @@ func benchmarkTrie(peerNumber, addressNumber, addressLength int, b *testing.B) {
|
||||
|
||||
for n := 0; n < addressNumber; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
cidr := uint8(rand.Uint32() % (AddressLength * 8))
|
||||
index := rand.Int() % peerNumber
|
||||
rng.Read(addr[:])
|
||||
cidr := uint8(rng.Uint32() % (AddressLength * 8))
|
||||
index := rng.Int() % peerNumber
|
||||
root.insert(addr[:], cidr, peers[index])
|
||||
}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
var addr [AddressLength]byte
|
||||
rand.Read(addr[:])
|
||||
rng.Read(addr[:])
|
||||
trie.lookup(addr[:])
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,10 @@ func TestTrieIPv4(t *testing.T) {
|
||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4([4]byte{a, b, c, d}), int(cidr)), peer)
|
||||
}
|
||||
|
||||
remove := func(peer *Peer, a, b, c, d byte, cidr uint8) {
|
||||
allowedIPs.Remove(netip.PrefixFrom(netip.AddrFrom4([4]byte{a, b, c, d}), int(cidr)), peer)
|
||||
}
|
||||
|
||||
assertEQ := func(peer *Peer, a, b, c, d byte) {
|
||||
p := allowedIPs.Lookup([]byte{a, b, c, d})
|
||||
if p != peer {
|
||||
@@ -176,6 +180,21 @@ func TestTrieIPv4(t *testing.T) {
|
||||
allowedIPs.RemoveByPeer(a)
|
||||
|
||||
assertNEQ(a, 192, 168, 0, 1)
|
||||
|
||||
insert(a, 1, 0, 0, 0, 32)
|
||||
insert(a, 192, 0, 0, 0, 24)
|
||||
assertEQ(a, 1, 0, 0, 0)
|
||||
assertEQ(a, 192, 0, 0, 1)
|
||||
remove(a, 192, 0, 0, 0, 32)
|
||||
assertEQ(a, 192, 0, 0, 1)
|
||||
remove(nil, 192, 0, 0, 0, 24)
|
||||
assertEQ(a, 192, 0, 0, 1)
|
||||
remove(b, 192, 0, 0, 0, 24)
|
||||
assertEQ(a, 192, 0, 0, 1)
|
||||
remove(a, 192, 0, 0, 0, 24)
|
||||
assertNEQ(a, 192, 0, 0, 1)
|
||||
remove(a, 1, 0, 0, 0, 32)
|
||||
assertNEQ(a, 1, 0, 0, 0)
|
||||
}
|
||||
|
||||
/* Test ported from kernel implementation:
|
||||
@@ -211,6 +230,15 @@ func TestTrieIPv6(t *testing.T) {
|
||||
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(*(*[16]byte)(addr)), int(cidr)), peer)
|
||||
}
|
||||
|
||||
remove := func(peer *Peer, a, b, c, d uint32, cidr uint8) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
addr = append(addr, expand(b)...)
|
||||
addr = append(addr, expand(c)...)
|
||||
addr = append(addr, expand(d)...)
|
||||
allowedIPs.Remove(netip.PrefixFrom(netip.AddrFrom16(*(*[16]byte)(addr)), int(cidr)), peer)
|
||||
}
|
||||
|
||||
assertEQ := func(peer *Peer, a, b, c, d uint32) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
@@ -223,6 +251,18 @@ func TestTrieIPv6(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
assertNEQ := func(peer *Peer, a, b, c, d uint32) {
|
||||
var addr []byte
|
||||
addr = append(addr, expand(a)...)
|
||||
addr = append(addr, expand(b)...)
|
||||
addr = append(addr, expand(c)...)
|
||||
addr = append(addr, expand(d)...)
|
||||
p := allowedIPs.Lookup(addr)
|
||||
if p == peer {
|
||||
t.Error("Assert NEQ failed")
|
||||
}
|
||||
}
|
||||
|
||||
insert(d, 0x26075300, 0x60006b00, 0, 0xc05f0543, 128)
|
||||
insert(c, 0x26075300, 0x60006b00, 0, 0, 64)
|
||||
insert(e, 0, 0, 0, 0, 0)
|
||||
@@ -244,4 +284,21 @@ func TestTrieIPv6(t *testing.T) {
|
||||
assertEQ(h, 0x24046800, 0x40040800, 0, 0)
|
||||
assertEQ(h, 0x24046800, 0x40040800, 0x10101010, 0x10101010)
|
||||
assertEQ(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef)
|
||||
|
||||
insert(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
insert(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
|
||||
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
assertEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
|
||||
remove(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 96)
|
||||
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
remove(nil, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
remove(b, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
remove(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
|
||||
assertNEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
|
||||
remove(b, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
|
||||
assertEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
|
||||
remove(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
|
||||
assertNEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -8,7 +8,7 @@ package device
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
)
|
||||
|
||||
type DummyDatagram struct {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -24,6 +24,7 @@ const (
|
||||
CookieRefreshTime = time.Second * 120
|
||||
HandshakeInitationRate = time.Second / 50
|
||||
PaddingMultiple = 16
|
||||
DefaultUdpWindow = 500
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -118,6 +118,7 @@ func (st *CookieChecker) CreateReply(
|
||||
msg []byte,
|
||||
recv uint32,
|
||||
src []byte,
|
||||
msgType uint32,
|
||||
) (*MessageCookieReply, error) {
|
||||
st.RLock()
|
||||
|
||||
@@ -153,7 +154,7 @@ func (st *CookieChecker) CreateReply(
|
||||
smac1 := smac2 - blake2s.Size128
|
||||
|
||||
reply := new(MessageCookieReply)
|
||||
reply.Type = MessageCookieReplyType
|
||||
reply.Type = msgType
|
||||
reply.Receiver = recv
|
||||
|
||||
_, err := rand.Read(reply.Nonce[:])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -99,7 +99,7 @@ func TestCookieMAC1(t *testing.T) {
|
||||
0x8c, 0xe1, 0xe8, 0xfa, 0x67, 0x20, 0x80, 0x6d,
|
||||
}
|
||||
generator.AddMacs(msg)
|
||||
reply, err := checker.CreateReply(msg, 1377, src)
|
||||
reply, err := checker.CreateReply(msg, 1377, src, MessageCookieReplyType)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create cookie reply:", err)
|
||||
}
|
||||
|
||||
+58
-264
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -11,12 +11,10 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||
"github.com/amnezia-vpn/amneziawg-go/ratelimiter"
|
||||
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"github.com/tevino/abool/v2"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/ratelimiter"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/rwcancel"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
@@ -92,22 +90,45 @@ type Device struct {
|
||||
closed chan struct{}
|
||||
log *Logger
|
||||
|
||||
isASecOn abool.AtomicBool
|
||||
aSecMux sync.RWMutex
|
||||
aSecCfg aSecCfgType
|
||||
}
|
||||
junk struct {
|
||||
min atomic.Uint32
|
||||
max atomic.Uint32
|
||||
count atomic.Uint32
|
||||
}
|
||||
|
||||
type aSecCfgType struct {
|
||||
isSet bool
|
||||
junkPacketCount int
|
||||
junkPacketMinSize int
|
||||
junkPacketMaxSize int
|
||||
initPacketJunkSize int
|
||||
responsePacketJunkSize int
|
||||
initPacketMagicHeader uint32
|
||||
responsePacketMagicHeader uint32
|
||||
underloadPacketMagicHeader uint32
|
||||
transportPacketMagicHeader uint32
|
||||
headers struct {
|
||||
init AtomicUintRange
|
||||
cookie AtomicUintRange
|
||||
response AtomicUintRange
|
||||
transport AtomicUintRange
|
||||
}
|
||||
|
||||
paddings struct {
|
||||
init atomic.Uint32
|
||||
response atomic.Uint32
|
||||
cookie atomic.Uint32
|
||||
transport atomic.Uint32
|
||||
}
|
||||
|
||||
ipackets [5]*obfChain
|
||||
|
||||
headerProtection struct {
|
||||
sync.RWMutex
|
||||
key HeaderCipherKey
|
||||
}
|
||||
|
||||
contentPaddingAddition AtomicUintRange
|
||||
|
||||
timings struct {
|
||||
rekeyAfterTimeSec AtomicUintRange
|
||||
rekeyTimeoutSec AtomicUintRange
|
||||
rejectAfterTimeSec AtomicUintRange
|
||||
keepaliveTimeoutSec AtomicUintRange
|
||||
maxHandshakeAttemps AtomicUintRange
|
||||
}
|
||||
|
||||
randomTrailers atomic.Bool
|
||||
disableCookies atomic.Bool
|
||||
}
|
||||
|
||||
// deviceState represents the state of a Device.
|
||||
@@ -202,7 +223,7 @@ func (device *Device) upLocked() error {
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Start()
|
||||
if peer.persistentKeepaliveInterval.Load() > 0 {
|
||||
if !peer.persistentKeepaliveInterval.Load().IsZero() {
|
||||
peer.SendKeepalive()
|
||||
}
|
||||
}
|
||||
@@ -302,6 +323,8 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
|
||||
}
|
||||
|
||||
func NewDevice(tunDevice tun.Device, bind conn.Bind, logger *Logger) *Device {
|
||||
var rang UintRange
|
||||
|
||||
device := new(Device)
|
||||
device.state.state.Store(uint32(deviceStateDown))
|
||||
device.closed = make(chan struct{})
|
||||
@@ -318,6 +341,15 @@ func NewDevice(tunDevice tun.Device, bind conn.Bind, logger *Logger) *Device {
|
||||
device.rate.limiter.Init()
|
||||
device.indexTable.Init()
|
||||
|
||||
rang.FromUint32(MessageInitiationType, MessageInitiationType)
|
||||
device.headers.init.Store(rang)
|
||||
rang.FromUint32(MessageResponseType, MessageResponseType)
|
||||
device.headers.response.Store(rang)
|
||||
rang.FromUint32(MessageCookieReplyType, MessageCookieReplyType)
|
||||
device.headers.cookie.Store(rang)
|
||||
rang.FromUint32(MessageTransportType, MessageTransportType)
|
||||
device.headers.transport.Store(rang)
|
||||
|
||||
device.PopulatePools()
|
||||
|
||||
// create queues
|
||||
@@ -428,10 +460,12 @@ func (device *Device) SendKeepalivesToPeersWithCurrentKeypair() {
|
||||
return
|
||||
}
|
||||
|
||||
timeout := device.keychainExpireTime()
|
||||
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.keypairs.RLock()
|
||||
sendKeepalive := peer.keypairs.current != nil && !peer.keypairs.current.created.Add(RejectAfterTime).Before(time.Now())
|
||||
sendKeepalive := peer.keypairs.current != nil && !peer.keypairs.current.created.Add(timeout).Before(time.Now())
|
||||
peer.keypairs.RUnlock()
|
||||
if sendKeepalive {
|
||||
peer.SendKeepalive()
|
||||
@@ -545,7 +579,6 @@ func (device *Device) BindUpdate() error {
|
||||
}
|
||||
|
||||
device.log.Verbosef("UDP bind has been updated")
|
||||
device.log.Verbosef(netc.bind.GetOffloadInfo())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -555,242 +588,3 @@ func (device *Device) BindClose() error {
|
||||
device.net.Unlock()
|
||||
return err
|
||||
}
|
||||
func (device *Device) isAdvancedSecurityOn() bool {
|
||||
return device.isASecOn.IsSet()
|
||||
}
|
||||
|
||||
func (device *Device) handlePostConfig(tempASecCfg *aSecCfgType) (err error) {
|
||||
|
||||
if !tempASecCfg.isSet {
|
||||
return err
|
||||
}
|
||||
|
||||
isASecOn := false
|
||||
device.aSecMux.Lock()
|
||||
if tempASecCfg.junkPacketCount < 0 {
|
||||
err = ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"JunkPacketCount should be non negative",
|
||||
)
|
||||
}
|
||||
device.aSecCfg.junkPacketCount = tempASecCfg.junkPacketCount
|
||||
if tempASecCfg.junkPacketCount != 0 {
|
||||
isASecOn = true
|
||||
}
|
||||
|
||||
device.aSecCfg.junkPacketMinSize = tempASecCfg.junkPacketMinSize
|
||||
if tempASecCfg.junkPacketMinSize != 0 {
|
||||
isASecOn = true
|
||||
}
|
||||
|
||||
if device.aSecCfg.junkPacketCount > 0 &&
|
||||
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.aSecCfg.junkPacketMaxSize = tempASecCfg.junkPacketMaxSize
|
||||
}
|
||||
|
||||
if tempASecCfg.junkPacketMaxSize != 0 {
|
||||
isASecOn = true
|
||||
}
|
||||
|
||||
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.aSecCfg.initPacketJunkSize = tempASecCfg.initPacketJunkSize
|
||||
}
|
||||
|
||||
if tempASecCfg.initPacketJunkSize != 0 {
|
||||
isASecOn = true
|
||||
}
|
||||
|
||||
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.aSecCfg.responsePacketJunkSize = tempASecCfg.responsePacketJunkSize
|
||||
}
|
||||
|
||||
if tempASecCfg.responsePacketJunkSize != 0 {
|
||||
isASecOn = true
|
||||
}
|
||||
|
||||
if tempASecCfg.initPacketMagicHeader > 4 {
|
||||
isASecOn = true
|
||||
device.log.Verbosef("UAPI: Updating init_packet_magic_header")
|
||||
device.aSecCfg.initPacketMagicHeader = tempASecCfg.initPacketMagicHeader
|
||||
MessageInitiationType = device.aSecCfg.initPacketMagicHeader
|
||||
} else {
|
||||
device.log.Verbosef("UAPI: Using default init type")
|
||||
MessageInitiationType = 1
|
||||
}
|
||||
|
||||
if tempASecCfg.responsePacketMagicHeader > 4 {
|
||||
isASecOn = true
|
||||
device.log.Verbosef("UAPI: Updating response_packet_magic_header")
|
||||
device.aSecCfg.responsePacketMagicHeader = tempASecCfg.responsePacketMagicHeader
|
||||
MessageResponseType = device.aSecCfg.responsePacketMagicHeader
|
||||
} else {
|
||||
device.log.Verbosef("UAPI: Using default response type")
|
||||
MessageResponseType = 2
|
||||
}
|
||||
|
||||
if tempASecCfg.underloadPacketMagicHeader > 4 {
|
||||
isASecOn = true
|
||||
device.log.Verbosef("UAPI: Updating underload_packet_magic_header")
|
||||
device.aSecCfg.underloadPacketMagicHeader = tempASecCfg.underloadPacketMagicHeader
|
||||
MessageCookieReplyType = device.aSecCfg.underloadPacketMagicHeader
|
||||
} else {
|
||||
device.log.Verbosef("UAPI: Using default underload type")
|
||||
MessageCookieReplyType = 3
|
||||
}
|
||||
|
||||
if tempASecCfg.transportPacketMagicHeader > 4 {
|
||||
isASecOn = true
|
||||
device.log.Verbosef("UAPI: Updating transport_packet_magic_header")
|
||||
device.aSecCfg.transportPacketMagicHeader = tempASecCfg.transportPacketMagicHeader
|
||||
MessageTransportType = device.aSecCfg.transportPacketMagicHeader
|
||||
} else {
|
||||
device.log.Verbosef("UAPI: Using default transport type")
|
||||
MessageTransportType = 4
|
||||
}
|
||||
|
||||
isSameMap := map[uint32]bool{}
|
||||
isSameMap[MessageInitiationType] = true
|
||||
isSameMap[MessageResponseType] = true
|
||||
isSameMap[MessageCookieReplyType] = true
|
||||
isSameMap[MessageTransportType] = true
|
||||
|
||||
// size will be different if same values
|
||||
if len(isSameMap) != 4 {
|
||||
if err != nil {
|
||||
err = ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
`magic headers should differ; got: init:%d; recv:%d; unde:%d; tran:%d; %w`,
|
||||
MessageInitiationType,
|
||||
MessageResponseType,
|
||||
MessageCookieReplyType,
|
||||
MessageTransportType,
|
||||
err,
|
||||
)
|
||||
} else {
|
||||
err = ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
`magic headers should differ; got: init:%d; recv:%d; unde:%d; tran:%d`,
|
||||
MessageInitiationType,
|
||||
MessageResponseType,
|
||||
MessageCookieReplyType,
|
||||
MessageTransportType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
newInitSize := MessageInitiationSize + device.aSecCfg.initPacketJunkSize
|
||||
newResponseSize := MessageResponseSize + device.aSecCfg.responsePacketJunkSize
|
||||
|
||||
if newInitSize == newResponseSize {
|
||||
if err != nil {
|
||||
err = ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
`new init size:%d; and new response size:%d; should differ; %w`,
|
||||
newInitSize,
|
||||
newResponseSize,
|
||||
err,
|
||||
)
|
||||
} else {
|
||||
err = ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
`new init size:%d; and new response size:%d; should differ`,
|
||||
newInitSize,
|
||||
newResponseSize,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
packetSizeToMsgType = map[int]uint32{
|
||||
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.aSecMux.Unlock()
|
||||
|
||||
return err
|
||||
}
|
||||
+91
-84
@@ -1,29 +1,32 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn/bindtest"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/tuntest"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn/bindtest"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun/tuntest"
|
||||
)
|
||||
|
||||
// uapiCfg returns a string that contains cfg formatted use with IpcSet.
|
||||
@@ -50,7 +53,7 @@ func uapiCfg(cfg ...string) string {
|
||||
|
||||
// genConfigs generates a pair of configs that connect to each other.
|
||||
// The configs use distinct, probably-usable ports.
|
||||
func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
func genConfigs(tb testing.TB, cfg ...string) (cfgs, endpointCfgs [2]string) {
|
||||
var key1, key2 NoisePrivateKey
|
||||
_, err := rand.Read(key1[:])
|
||||
if err != nil {
|
||||
@@ -62,7 +65,8 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
}
|
||||
pub1, pub2 := key1.publicKey(), key2.publicKey()
|
||||
|
||||
cfgs[0] = uapiCfg(
|
||||
args0 := append([]string(nil), cfg...)
|
||||
args0 = append(args0, []string{
|
||||
"private_key", hex.EncodeToString(key1[:]),
|
||||
"listen_port", "0",
|
||||
"replace_peers", "true",
|
||||
@@ -70,12 +74,16 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
"protocol_version", "1",
|
||||
"replace_allowed_ips", "true",
|
||||
"allowed_ip", "1.0.0.2/32",
|
||||
)
|
||||
}...)
|
||||
cfgs[0] = uapiCfg(args0...)
|
||||
|
||||
endpointCfgs[0] = uapiCfg(
|
||||
"public_key", hex.EncodeToString(pub2[:]),
|
||||
"endpoint", "127.0.0.1:%d",
|
||||
)
|
||||
cfgs[1] = uapiCfg(
|
||||
|
||||
args1 := append([]string(nil), cfg...)
|
||||
args1 = append(args1, []string{
|
||||
"private_key", hex.EncodeToString(key2[:]),
|
||||
"listen_port", "0",
|
||||
"replace_peers", "true",
|
||||
@@ -83,66 +91,9 @@ func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
"protocol_version", "1",
|
||||
"replace_allowed_ips", "true",
|
||||
"allowed_ip", "1.0.0.1/32",
|
||||
)
|
||||
endpointCfgs[1] = uapiCfg(
|
||||
"public_key", hex.EncodeToString(pub1[:]),
|
||||
"endpoint", "127.0.0.1:%d",
|
||||
)
|
||||
return
|
||||
}
|
||||
}...)
|
||||
|
||||
func genASecurityConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
|
||||
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",
|
||||
)
|
||||
cfgs[1] = uapiCfg(args1...)
|
||||
endpointCfgs[1] = uapiCfg(
|
||||
"public_key", hex.EncodeToString(pub1[:]),
|
||||
"endpoint", "127.0.0.1:%d",
|
||||
@@ -185,9 +136,10 @@ func (pair *testPair) Send(
|
||||
// pong is the new ping
|
||||
p0, p1 = p1, p0
|
||||
}
|
||||
|
||||
msg := tuntest.Ping(p0.ip, p1.ip)
|
||||
p1.tun.Outbound <- msg
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
timer := time.NewTimer(6 * time.Second)
|
||||
defer timer.Stop()
|
||||
var err error
|
||||
select {
|
||||
@@ -214,14 +166,12 @@ func (pair *testPair) Send(
|
||||
// genTestPair creates a testPair.
|
||||
func genTestPair(
|
||||
tb testing.TB,
|
||||
realSocket, withASecurity bool,
|
||||
realSocket bool,
|
||||
extraCfg ...string,
|
||||
) (pair testPair) {
|
||||
var cfg, endpointCfg [2]string
|
||||
if withASecurity {
|
||||
cfg, endpointCfg = genASecurityConfigs(tb)
|
||||
} else {
|
||||
cfg, endpointCfg = genConfigs(tb)
|
||||
}
|
||||
cfg, endpointCfg = genConfigs(tb, extraCfg...)
|
||||
|
||||
var binds [2]conn.Bind
|
||||
if realSocket {
|
||||
binds[0], binds[1] = conn.NewDefaultBind(), conn.NewDefaultBind()
|
||||
@@ -265,7 +215,7 @@ func genTestPair(
|
||||
|
||||
func TestTwoDevicePing(t *testing.T) {
|
||||
goroutineLeakCheck(t)
|
||||
pair := genTestPair(t, true, false)
|
||||
pair := genTestPair(t, true)
|
||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||
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)
|
||||
pair := genTestPair(t, true, true)
|
||||
|
||||
pair := genTestPair(t, true,
|
||||
"jc", "5",
|
||||
"jmin", "500",
|
||||
"jmax", "1000",
|
||||
"s1", "15",
|
||||
"s2", "18",
|
||||
"s3", "20",
|
||||
"s4", "25",
|
||||
"h1", "123456-123500",
|
||||
"h2", "67543-67550",
|
||||
"h3", "123123-123200",
|
||||
"h4", "32345-32350",
|
||||
)
|
||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||
pair.Send(t, Ping, nil)
|
||||
})
|
||||
@@ -285,13 +249,56 @@ 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>",
|
||||
"i2", "<b 0xf6ab3267fa><c><b 0xf6ab><t><rc 10>",
|
||||
"i3", "<b 0xf6ab3267fa><c><b 0xf6ab><t><rd 10>",
|
||||
"i4", "<b 0xf6ab3267fa><r 100>",
|
||||
// "jc", "1",
|
||||
// "jmin", "500",
|
||||
// "jmax", "1000",
|
||||
// "s1", "30",
|
||||
// "s2", "40",
|
||||
// "h1", "123456",
|
||||
// "h2", "67543",
|
||||
// "h4", "32345",
|
||||
// "h3", "123123",
|
||||
)
|
||||
t.Run("ping 1.0.0.1", func(t *testing.T) {
|
||||
for isRunning.Load() {
|
||||
pair.Send(t, Ping, nil)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
})
|
||||
t.Run("ping 1.0.0.2", func(t *testing.T) {
|
||||
for isRunning.Load() {
|
||||
pair.Send(t, Pong, nil)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpDown(t *testing.T) {
|
||||
goroutineLeakCheck(t)
|
||||
const itrials = 50
|
||||
const otrials = 10
|
||||
|
||||
for n := 0; n < otrials; n++ {
|
||||
pair := genTestPair(t, false, false)
|
||||
pair := genTestPair(t, false)
|
||||
for i := range pair {
|
||||
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[:])))
|
||||
@@ -325,7 +332,7 @@ func TestUpDown(t *testing.T) {
|
||||
// TestConcurrencySafety does other things concurrently with tunnel use.
|
||||
// It is intended to be used with the race detector to catch data races.
|
||||
func TestConcurrencySafety(t *testing.T) {
|
||||
pair := genTestPair(t, true, false)
|
||||
pair := genTestPair(t, true)
|
||||
done := make(chan struct{})
|
||||
|
||||
const warmupIters = 10
|
||||
@@ -406,7 +413,7 @@ func TestConcurrencySafety(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkLatency(b *testing.B) {
|
||||
pair := genTestPair(b, true, false)
|
||||
pair := genTestPair(b, true)
|
||||
|
||||
// Establish a connection.
|
||||
pair.Send(b, Ping, nil)
|
||||
@@ -420,7 +427,7 @@ func BenchmarkLatency(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkThroughput(b *testing.B) {
|
||||
pair := genTestPair(b, true, false)
|
||||
pair := genTestPair(b, true)
|
||||
|
||||
// Establish a connection.
|
||||
pair.Send(b, Ping, nil)
|
||||
@@ -464,7 +471,7 @@ func BenchmarkThroughput(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, Pong, nil)
|
||||
b.ReportAllocs()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/replay"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/replay"
|
||||
)
|
||||
|
||||
/* Due to limitations in Go and /x/crypto there is currently
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+36
-19
@@ -1,21 +1,23 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/blake2s"
|
||||
"golang.org/x/crypto/chacha20"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/poly1305"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/tai64n"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tai64n"
|
||||
)
|
||||
|
||||
type handshakeState int
|
||||
@@ -52,7 +54,8 @@ const (
|
||||
WGLabelCookie = "cookie--"
|
||||
)
|
||||
|
||||
var (
|
||||
const (
|
||||
MessageUnknownType uint32 = 0
|
||||
MessageInitiationType uint32 = 1
|
||||
MessageResponseType uint32 = 2
|
||||
MessageCookieReplyType uint32 = 3
|
||||
@@ -75,10 +78,6 @@ const (
|
||||
MessageTransportOffsetContent = 16
|
||||
)
|
||||
|
||||
var packetSizeToMsgType map[int]uint32
|
||||
|
||||
var msgTypeToJunkSize map[uint32]int
|
||||
|
||||
/* Type is an 8-bit field, followed by 3 nul bytes,
|
||||
* by marshalling the messages in little-endian byteorder
|
||||
* we can treat these as a 32-bit unsigned int (for now)
|
||||
@@ -197,12 +196,12 @@ func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, e
|
||||
|
||||
handshake.mixHash(handshake.remoteStatic[:])
|
||||
|
||||
device.aSecMux.RLock()
|
||||
msgType := device.headers.init.Load().PickOne()
|
||||
|
||||
msg := MessageInitiation{
|
||||
Type: MessageInitiationType,
|
||||
Type: msgType,
|
||||
Ephemeral: handshake.localEphemeral.publicKey(),
|
||||
}
|
||||
device.aSecMux.RUnlock()
|
||||
|
||||
handshake.mixKey(msg.Ephemeral[:])
|
||||
handshake.mixHash(msg.Ephemeral[:])
|
||||
@@ -256,12 +255,9 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
|
||||
chainKey [blake2s.Size]byte
|
||||
)
|
||||
|
||||
device.aSecMux.RLock()
|
||||
if msg.Type != MessageInitiationType {
|
||||
device.aSecMux.RUnlock()
|
||||
return nil
|
||||
}
|
||||
device.aSecMux.RUnlock()
|
||||
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
@@ -376,9 +372,7 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error
|
||||
}
|
||||
|
||||
var msg MessageResponse
|
||||
device.aSecMux.RLock()
|
||||
msg.Type = MessageResponseType
|
||||
device.aSecMux.RUnlock()
|
||||
msg.Type = device.headers.response.Load().PickOne()
|
||||
msg.Sender = handshake.localIndex
|
||||
msg.Receiver = handshake.remoteIndex
|
||||
|
||||
@@ -428,12 +422,9 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error
|
||||
}
|
||||
|
||||
func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
|
||||
device.aSecMux.RLock()
|
||||
if msg.Type != MessageResponseType {
|
||||
device.aSecMux.RUnlock()
|
||||
return nil
|
||||
}
|
||||
device.aSecMux.RUnlock()
|
||||
|
||||
// lookup handshake by receiver
|
||||
|
||||
@@ -637,3 +628,29 @@ func (peer *Peer) ReceivedWithKeypair(receivedKeypair *Keypair) bool {
|
||||
keypairs.next.Store(nil)
|
||||
return true
|
||||
}
|
||||
|
||||
func (device *Device) JunkPackets() [][]byte {
|
||||
var bufs [][]byte
|
||||
|
||||
min := device.junk.min.Load()
|
||||
max := device.junk.max.Load()
|
||||
|
||||
for range device.junk.count.Load() {
|
||||
buf := make([]byte, min+fastrandn(max-min))
|
||||
rand.Read(buf)
|
||||
bufs = append(bufs, buf)
|
||||
}
|
||||
|
||||
return bufs
|
||||
}
|
||||
|
||||
func (device *Device) HeaderProtectionCipher(salt []byte) (*chacha20.Cipher, error) {
|
||||
device.headerProtection.RLock()
|
||||
defer device.headerProtection.RUnlock()
|
||||
|
||||
if device.headerProtection.key.IsZero() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return chacha20.NewUnauthenticatedCipher(device.headerProtection.key[:], salt)
|
||||
}
|
||||
+109
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -9,12 +9,18 @@ import (
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
NoisePublicKeySize = 32
|
||||
NoisePrivateKeySize = 32
|
||||
NoisePresharedKeySize = 32
|
||||
HeaderCipherKeySize = 32
|
||||
HeaderCipherNonceSize = 12
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -22,6 +28,7 @@ type (
|
||||
NoisePrivateKey [NoisePrivateKeySize]byte
|
||||
NoisePresharedKey [NoisePresharedKeySize]byte
|
||||
NoiseNonce uint64 // padded to 12-bytes
|
||||
HeaderCipherKey [HeaderCipherKeySize]byte
|
||||
)
|
||||
|
||||
func loadExactHex(dst []byte, src string) error {
|
||||
@@ -76,3 +83,104 @@ func (key NoisePublicKey) Equals(tar NoisePublicKey) bool {
|
||||
func (key *NoisePresharedKey) FromHex(src string) error {
|
||||
return loadExactHex(key[:], src)
|
||||
}
|
||||
|
||||
func (key HeaderCipherKey) IsZero() bool {
|
||||
var zero HeaderCipherKey
|
||||
return key.Equals(zero)
|
||||
}
|
||||
|
||||
func (key HeaderCipherKey) Equals(tar HeaderCipherKey) bool {
|
||||
return subtle.ConstantTimeCompare(key[:], tar[:]) == 1
|
||||
}
|
||||
|
||||
func (key *HeaderCipherKey) FromHex(src string) error {
|
||||
return loadExactHex(key[:], src)
|
||||
}
|
||||
|
||||
type UintRange uint64
|
||||
|
||||
func (r *UintRange) FromUint32(lo, hi uint32) {
|
||||
*r = UintRange(uint64(hi)<<32 | uint64(lo))
|
||||
}
|
||||
|
||||
func (r *UintRange) FromString(str string) error {
|
||||
parts := strings.Split(str, "-")
|
||||
if len(parts) < 1 || len(parts) > 2 {
|
||||
return errors.New("wrong format")
|
||||
}
|
||||
|
||||
lo, err := strconv.ParseUint(parts[0], 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hi := lo
|
||||
if len(parts) > 1 {
|
||||
hi, err = strconv.ParseUint(parts[1], 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if hi < lo {
|
||||
return errors.New("wrong range specified")
|
||||
}
|
||||
|
||||
r.FromUint32(uint32(lo), uint32(hi))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r UintRange) Contains(num uint32) bool {
|
||||
lo, hi := uint32(r), uint32(r>>32)
|
||||
return lo <= num && num <= hi
|
||||
}
|
||||
|
||||
func (r UintRange) IsZero() bool {
|
||||
return r == 0
|
||||
}
|
||||
|
||||
func (r UintRange) PickOne() uint32 {
|
||||
lo, hi := uint32(r), uint32(r>>32)
|
||||
return lo + fastrandn(hi-lo+1)
|
||||
}
|
||||
|
||||
func (r UintRange) ToString() string {
|
||||
lo, hi := uint32(r), uint32(r>>32)
|
||||
|
||||
if lo == hi {
|
||||
return fmt.Sprintf("%d", lo)
|
||||
} else {
|
||||
return fmt.Sprintf("%d-%d", lo, hi)
|
||||
}
|
||||
}
|
||||
|
||||
func (r UintRange) Overlap(right UintRange) bool {
|
||||
l_lo, l_hi := uint32(r), uint32(r>>32)
|
||||
r_lo, r_hi := uint32(right), uint32(right>>32)
|
||||
|
||||
return l_lo <= r_hi && r_lo <= l_hi
|
||||
}
|
||||
|
||||
func (r UintRange) Lo() uint32 {
|
||||
return uint32(r)
|
||||
}
|
||||
|
||||
func (r UintRange) Hi() uint32 {
|
||||
return uint32(r >> 32)
|
||||
}
|
||||
|
||||
type AtomicUintRange struct {
|
||||
v atomic.Uint64
|
||||
}
|
||||
|
||||
func (a *AtomicUintRange) Load() UintRange {
|
||||
return UintRange(a.v.Load())
|
||||
}
|
||||
|
||||
func (a *AtomicUintRange) Store(r UintRange) {
|
||||
a.v.Store(uint64(r))
|
||||
}
|
||||
|
||||
func (a *AtomicUintRange) Swap(r UintRange) UintRange {
|
||||
return UintRange(a.v.Swap(uint64(r)))
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/tuntest"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun/tuntest"
|
||||
)
|
||||
|
||||
func TestCurveWrappers(t *testing.T) {
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type obfBuilder func(val string) (obf, error)
|
||||
|
||||
var obfBuilders = map[string]obfBuilder{
|
||||
"b": newBytesObf,
|
||||
"t": newTimestampObf,
|
||||
"r": newRandObf,
|
||||
"rc": newRandCharObf,
|
||||
"rd": newRandDigitsObf,
|
||||
"d": newDataObf,
|
||||
"ds": newDataStringObf,
|
||||
"dz": newDataSizeObf,
|
||||
}
|
||||
|
||||
type obf interface {
|
||||
Obfuscate(dst, src []byte)
|
||||
Deobfuscate(dst, src []byte) bool
|
||||
ObfuscatedLen(srcLen int) int
|
||||
DeobfuscatedLen(srcLen int) int
|
||||
}
|
||||
|
||||
type obfChain struct {
|
||||
Spec string
|
||||
obfs []obf
|
||||
}
|
||||
|
||||
func newObfChain(spec string) (*obfChain, error) {
|
||||
var (
|
||||
obfs []obf
|
||||
errs []error
|
||||
)
|
||||
|
||||
remaining := spec[:]
|
||||
for {
|
||||
start := strings.IndexByte(remaining, '<')
|
||||
if start == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
end := strings.IndexByte(remaining[start:], '>')
|
||||
if end == -1 {
|
||||
return nil, errors.New("missing enclosing >")
|
||||
}
|
||||
end += start
|
||||
|
||||
tag := remaining[start+1 : end]
|
||||
parts := strings.Fields(tag)
|
||||
if len(parts) == 0 {
|
||||
errs = append(errs, errors.New("empty tag"))
|
||||
remaining = remaining[end+1:]
|
||||
continue
|
||||
}
|
||||
|
||||
key := parts[0]
|
||||
builder, ok := obfBuilders[key]
|
||||
if !ok {
|
||||
errs = append(errs, fmt.Errorf("unknown tag <%s>", key))
|
||||
remaining = remaining[end+1:]
|
||||
continue
|
||||
}
|
||||
|
||||
val := ""
|
||||
if len(parts) > 1 {
|
||||
val = parts[1]
|
||||
}
|
||||
|
||||
o, err := builder(val)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to build <%s>: %w", key, err))
|
||||
remaining = remaining[end+1:]
|
||||
continue
|
||||
}
|
||||
|
||||
obfs = append(obfs, o)
|
||||
remaining = remaining[end+1:]
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
if len(obfs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &obfChain{
|
||||
Spec: spec,
|
||||
obfs: obfs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *obfChain) Obfuscate(dst, src []byte) {
|
||||
written := 0
|
||||
for _, o := range c.obfs {
|
||||
obfLen := o.ObfuscatedLen(len(src))
|
||||
o.Obfuscate(dst[written:written+obfLen], src)
|
||||
written += obfLen
|
||||
}
|
||||
}
|
||||
|
||||
func (c *obfChain) Deobfuscate(dst, src []byte) bool {
|
||||
dynamicLen := len(src) - c.ObfuscatedLen(0)
|
||||
|
||||
written, read := 0, 0
|
||||
|
||||
for _, o := range c.obfs {
|
||||
deobfLen := o.DeobfuscatedLen(dynamicLen)
|
||||
obfLen := o.ObfuscatedLen(deobfLen)
|
||||
|
||||
if !o.Deobfuscate(dst[written:written+deobfLen], src[read:read+obfLen]) {
|
||||
return false
|
||||
}
|
||||
|
||||
written += deobfLen
|
||||
read += obfLen
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *obfChain) ObfuscatedLen(n int) int {
|
||||
total := 0
|
||||
for _, o := range c.obfs {
|
||||
total += o.ObfuscatedLen(n)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func (c *obfChain) DeobfuscatedLen(n int) int {
|
||||
dynamicLen := n - c.ObfuscatedLen(0)
|
||||
|
||||
total := 0
|
||||
for _, o := range c.obfs {
|
||||
total += o.DeobfuscatedLen(dynamicLen)
|
||||
}
|
||||
return total
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func newBytesObf(val string) (obf, error) {
|
||||
val = strings.TrimPrefix(val, "0x")
|
||||
|
||||
if len(val) == 0 {
|
||||
return nil, errors.New("empty argument")
|
||||
}
|
||||
|
||||
if len(val)%2 != 0 {
|
||||
return nil, errors.New("odd amount of symbols")
|
||||
}
|
||||
|
||||
bytes, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &bytesObf{data: bytes}, nil
|
||||
}
|
||||
|
||||
type bytesObf struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (o *bytesObf) Obfuscate(dst, src []byte) {
|
||||
copy(dst, o.data)
|
||||
}
|
||||
|
||||
func (o *bytesObf) Deobfuscate(dst, src []byte) bool {
|
||||
return bytes.Equal(o.data, src[:o.ObfuscatedLen(0)])
|
||||
}
|
||||
|
||||
func (o *bytesObf) ObfuscatedLen(srcLen int) int {
|
||||
return len(o.data)
|
||||
}
|
||||
|
||||
func (o *bytesObf) DeobfuscatedLen(srcLen int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package device
|
||||
|
||||
func newDataObf(val string) (obf, error) {
|
||||
return &dataObf{}, nil
|
||||
}
|
||||
|
||||
type dataObf struct {
|
||||
}
|
||||
|
||||
func (obf *dataObf) Obfuscate(dst, src []byte) {
|
||||
copy(dst, src)
|
||||
}
|
||||
|
||||
func (obf *dataObf) Deobfuscate(dst, src []byte) bool {
|
||||
copy(dst, src)
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *dataObf) ObfuscatedLen(n int) int {
|
||||
return n
|
||||
}
|
||||
|
||||
func (o *dataObf) DeobfuscatedLen(n int) int {
|
||||
return n
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package device
|
||||
|
||||
import "strconv"
|
||||
|
||||
func newDataSizeObf(val string) (obf, error) {
|
||||
length, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dataSizeObf{
|
||||
length: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type dataSizeObf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
func (o *dataSizeObf) Obfuscate(dst, src []byte) {
|
||||
srcLen := len(src)
|
||||
for i := o.length - 1; i >= 0; i-- {
|
||||
dst[i] = byte(srcLen & 0xFF)
|
||||
srcLen >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
func (o *dataSizeObf) Deobfuscate(dst, src []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *dataSizeObf) ObfuscatedLen(srcLen int) int {
|
||||
return o.length
|
||||
}
|
||||
|
||||
func (o *dataSizeObf) DeobfuscatedLen(srcLen int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
)
|
||||
|
||||
func newDataStringObf(val string) (obf, error) {
|
||||
return &dataStringObf{}, nil
|
||||
}
|
||||
|
||||
type dataStringObf struct {
|
||||
}
|
||||
|
||||
func (o *dataStringObf) Obfuscate(dst, src []byte) {
|
||||
base64.RawStdEncoding.Encode(dst, src)
|
||||
}
|
||||
|
||||
func (o *dataStringObf) Deobfuscate(dst, src []byte) bool {
|
||||
base64.RawStdEncoding.Decode(dst, src)
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *dataStringObf) ObfuscatedLen(n int) int {
|
||||
return base64.RawStdEncoding.EncodedLen(n)
|
||||
}
|
||||
|
||||
func (o *dataStringObf) DeobfuscatedLen(n int) int {
|
||||
return base64.RawStdEncoding.DecodedLen(n)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func newRandObf(val string) (obf, error) {
|
||||
length, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &randObf{
|
||||
length: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type randObf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
func (o *randObf) Obfuscate(dst, src []byte) {
|
||||
rand.Read(dst[:o.length])
|
||||
}
|
||||
|
||||
func (o *randObf) Deobfuscate(dst, src []byte) bool {
|
||||
// there is no way to validate randomness :)
|
||||
// assume that it is always true
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *randObf) ObfuscatedLen(n int) int {
|
||||
return o.length
|
||||
}
|
||||
|
||||
func (o *randObf) DeobfuscatedLen(n int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strconv"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const chars52 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
func newRandCharObf(val string) (obf, error) {
|
||||
length, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &randCharObf{
|
||||
length: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type randCharObf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
func (o *randCharObf) Obfuscate(dst, src []byte) {
|
||||
rand.Read(dst[:o.length])
|
||||
for i := range dst[:o.length] {
|
||||
dst[i] = chars52[dst[i]%52]
|
||||
}
|
||||
}
|
||||
|
||||
func (o *randCharObf) Deobfuscate(dst, src []byte) bool {
|
||||
for _, b := range src[:o.length] {
|
||||
if !unicode.IsLetter(rune(b)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *randCharObf) ObfuscatedLen(n int) int {
|
||||
return o.length
|
||||
}
|
||||
|
||||
func (o *randCharObf) DeobfuscatedLen(n int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strconv"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const digits10 = "0123456789"
|
||||
|
||||
func newRandDigitsObf(val string) (obf, error) {
|
||||
length, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &randDigitObf{
|
||||
length: length,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type randDigitObf struct {
|
||||
length int
|
||||
}
|
||||
|
||||
func (o *randDigitObf) Obfuscate(dst, src []byte) {
|
||||
rand.Read(dst[:o.length])
|
||||
for i := range dst[:o.length] {
|
||||
dst[i] = digits10[dst[i]%10]
|
||||
}
|
||||
}
|
||||
|
||||
func (o *randDigitObf) Deobfuscate(dst, src []byte) bool {
|
||||
for _, b := range src[:o.length] {
|
||||
if !unicode.IsDigit(rune(b)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *randDigitObf) ObfuscatedLen(n int) int {
|
||||
return o.length
|
||||
}
|
||||
|
||||
func (o *randDigitObf) DeobfuscatedLen(n int) int {
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newTimestampObf(_ string) (obf, error) {
|
||||
return ×tampObf{}, nil
|
||||
}
|
||||
|
||||
type timestampObf struct{}
|
||||
|
||||
func (o *timestampObf) Obfuscate(dst, src []byte) {
|
||||
t := uint32(time.Now().Unix())
|
||||
binary.BigEndian.PutUint32(dst, t)
|
||||
}
|
||||
|
||||
func (o *timestampObf) Deobfuscate(dst, src []byte) bool {
|
||||
// replay attack check?
|
||||
// requires time to be always synchronized
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *timestampObf) ObfuscatedLen(n int) int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func (o *timestampObf) DeobfuscatedLen(n int) int {
|
||||
return 0
|
||||
}
|
||||
+12
-5
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
)
|
||||
|
||||
type Peer struct {
|
||||
@@ -39,6 +39,7 @@ type Peer struct {
|
||||
zeroKeyMaterial *Timer
|
||||
persistentKeepalive *Timer
|
||||
handshakeAttempts atomic.Uint32
|
||||
maxHandshakeAttempts atomic.Uint32
|
||||
needAnotherKeepalive atomic.Bool
|
||||
sentLastMinuteHandshake atomic.Bool
|
||||
}
|
||||
@@ -55,7 +56,8 @@ type Peer struct {
|
||||
|
||||
cookieGenerator CookieGenerator
|
||||
trieEntries list.List
|
||||
persistentKeepaliveInterval atomic.Uint32
|
||||
persistentKeepaliveInterval AtomicUintRange
|
||||
udpWindow atomic.Uint32
|
||||
}
|
||||
|
||||
func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
@@ -78,6 +80,8 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
// create peer
|
||||
peer := new(Peer)
|
||||
|
||||
peer.udpWindow.Store(DefaultUdpWindow)
|
||||
|
||||
peer.cookieGenerator.Init(pk)
|
||||
peer.device = device
|
||||
peer.queue.outbound = newAutodrainingOutboundQueue(device)
|
||||
@@ -192,7 +196,7 @@ func (peer *Peer) Start() {
|
||||
peer.stopping.Add(2)
|
||||
|
||||
peer.handshake.mutex.Lock()
|
||||
peer.handshake.lastSentHandshake = time.Now().Add(-(RekeyTimeout + time.Second))
|
||||
peer.handshake.lastSentHandshake = time.Now().Add(-(peer.device.rekeyMinTimeout() + time.Second))
|
||||
peer.handshake.mutex.Unlock()
|
||||
|
||||
peer.device.queue.encryption.wg.Add(1) // keep encryption queue open for our writes
|
||||
@@ -242,7 +246,7 @@ func (peer *Peer) ExpireCurrentKeypairs() {
|
||||
handshake.mutex.Lock()
|
||||
peer.device.indexTable.Delete(handshake.localIndex)
|
||||
handshake.Clear()
|
||||
peer.handshake.lastSentHandshake = time.Now().Add(-(RekeyTimeout + time.Second))
|
||||
peer.handshake.lastSentHandshake = time.Now().Add(-(peer.device.rekeyMinTimeout() + time.Second))
|
||||
handshake.mutex.Unlock()
|
||||
|
||||
keypairs := &peer.keypairs
|
||||
@@ -282,6 +286,9 @@ func (peer *Peer) SetEndpointFromPacket(endpoint conn.Endpoint) {
|
||||
if peer.endpoint.disableRoaming {
|
||||
return
|
||||
}
|
||||
if peer.endpoint.val != endpoint {
|
||||
peer.udpWindow.Store(DefaultUdpWindow)
|
||||
}
|
||||
peer.endpoint.clearSrcOnTx = false
|
||||
peer.endpoint.val = endpoint
|
||||
}
|
||||
|
||||
+7
-6
@@ -1,20 +1,19 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type WaitPool struct {
|
||||
pool sync.Pool
|
||||
cond sync.Cond
|
||||
lock sync.Mutex
|
||||
count atomic.Uint32
|
||||
count uint32 // Get calls not yet Put back
|
||||
max uint32
|
||||
}
|
||||
|
||||
@@ -27,10 +26,10 @@ func NewWaitPool(max uint32, new func() any) *WaitPool {
|
||||
func (p *WaitPool) Get() any {
|
||||
if p.max != 0 {
|
||||
p.lock.Lock()
|
||||
for p.count.Load() >= p.max {
|
||||
for p.count >= p.max {
|
||||
p.cond.Wait()
|
||||
}
|
||||
p.count.Add(1)
|
||||
p.count++
|
||||
p.lock.Unlock()
|
||||
}
|
||||
return p.pool.Get()
|
||||
@@ -41,7 +40,9 @@ func (p *WaitPool) Put(x any) {
|
||||
if p.max == 0 {
|
||||
return
|
||||
}
|
||||
p.count.Add(^uint32(0))
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
p.count--
|
||||
p.cond.Signal()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -32,7 +32,9 @@ func TestWaitPool(t *testing.T) {
|
||||
wg.Add(workers)
|
||||
var max atomic.Uint32
|
||||
updateMax := func() {
|
||||
count := p.count.Load()
|
||||
p.lock.Lock()
|
||||
count := p.count
|
||||
p.lock.Unlock()
|
||||
if count > p.max {
|
||||
t.Errorf("count (%d) > max (%d)", count, p.max)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import "github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
import "github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
|
||||
/* Reduce memory consumption for Android */
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import "github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
import "github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
|
||||
const (
|
||||
QueueStagedSize = conn.IdealBatchSize
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
+123
-41
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
@@ -32,6 +32,7 @@ type QueueInboundElement struct {
|
||||
counter uint64
|
||||
keypair *Keypair
|
||||
endpoint conn.Endpoint
|
||||
padding uint32
|
||||
}
|
||||
|
||||
type QueueInboundElementsContainer struct {
|
||||
@@ -59,7 +60,8 @@ func (peer *Peer) keepKeyFreshReceiving() {
|
||||
return
|
||||
}
|
||||
keypair := peer.keypairs.Current()
|
||||
if keypair != nil && keypair.isInitiator && time.Since(keypair.created) > (RejectAfterTime-KeepaliveTimeout-RekeyTimeout) {
|
||||
|
||||
if keypair != nil && keypair.isInitiator && time.Since(keypair.created) > peer.device.keyRefreshTimeoutReceiving() {
|
||||
peer.timers.sentLastMinuteHandshake.Store(true)
|
||||
peer.SendHandshakeInitiation(false)
|
||||
}
|
||||
@@ -95,15 +97,16 @@ func (device *Device) RoutineReceiveIncoming(
|
||||
endpoints = make([]conn.Endpoint, maxBatchSize)
|
||||
deathSpiral int
|
||||
elemsByPeer = make(map[*Peer]*QueueInboundElementsContainer, maxBatchSize)
|
||||
typeHashBuf [4]byte
|
||||
)
|
||||
|
||||
for i := range bufsArrs {
|
||||
for i := range maxBatchSize {
|
||||
bufsArrs[i] = device.GetMessageBuffer()
|
||||
bufs[i] = bufsArrs[i][:]
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for i := 0; i < maxBatchSize; i++ {
|
||||
for i := range maxBatchSize {
|
||||
if bufsArrs[i] != nil {
|
||||
device.PutMessageBuffer(bufsArrs[i])
|
||||
}
|
||||
@@ -129,7 +132,6 @@ func (device *Device) RoutineReceiveIncoming(
|
||||
}
|
||||
deathSpiral = 0
|
||||
|
||||
device.aSecMux.RLock()
|
||||
// handle each packet in the batch
|
||||
for i, size := range sizes[:count] {
|
||||
if size < MinMessageSize {
|
||||
@@ -137,31 +139,32 @@ func (device *Device) RoutineReceiveIncoming(
|
||||
}
|
||||
|
||||
// check size of packet
|
||||
|
||||
packet := bufsArrs[i][:size]
|
||||
var msgType uint32
|
||||
if device.isAdvancedSecurityOn() {
|
||||
if assumedMsgType, ok := packetSizeToMsgType[size]; ok {
|
||||
junkSize := msgTypeToJunkSize[assumedMsgType]
|
||||
// transport size can align with other header types;
|
||||
// making sure we have the right msgType
|
||||
msgType = binary.LittleEndian.Uint32(packet[junkSize : junkSize+4])
|
||||
if msgType == assumedMsgType {
|
||||
packet = packet[junkSize:]
|
||||
} else {
|
||||
device.log.Verbosef("Transport packet lined up with another msg type")
|
||||
msgType = binary.LittleEndian.Uint32(packet[:4])
|
||||
}
|
||||
} else {
|
||||
msgType = binary.LittleEndian.Uint32(packet[:4])
|
||||
if msgType != MessageTransportType {
|
||||
device.log.Verbosef("ASec: Received message with unknown type")
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msgType = binary.LittleEndian.Uint32(packet[:4])
|
||||
|
||||
cip, err := device.HeaderProtectionCipher(packet[:HeaderCipherNonceSize])
|
||||
if err != nil {
|
||||
device.log.Errorf("Failed to initialize header cipher")
|
||||
continue
|
||||
}
|
||||
|
||||
typeHash := typeHashBuf[:]
|
||||
clear(typeHash)
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(typeHash, typeHash)
|
||||
}
|
||||
|
||||
// get message padding and type based on information from S1-S4 and H1-H4
|
||||
msgSize, msgType, padding := device.DeterminePacketTypeAndPadding(packet, typeHash)
|
||||
|
||||
packet = packet[padding:]
|
||||
if msgType != MessageTransportType {
|
||||
packet = packet[:msgSize]
|
||||
}
|
||||
|
||||
if cip != nil {
|
||||
applyHash(packet[:4], packet[:4], typeHash)
|
||||
}
|
||||
|
||||
switch msgType {
|
||||
|
||||
// check if transport
|
||||
@@ -173,6 +176,9 @@ func (device *Device) RoutineReceiveIncoming(
|
||||
if len(packet) < MessageTransportSize {
|
||||
continue
|
||||
}
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(packet[4:MessageTransportHeaderSize], packet[4:MessageTransportHeaderSize])
|
||||
}
|
||||
|
||||
// lookup key pair
|
||||
|
||||
@@ -187,7 +193,7 @@ func (device *Device) RoutineReceiveIncoming(
|
||||
|
||||
// check keypair expiry
|
||||
|
||||
if keypair.created.Add(RejectAfterTime).Before(time.Now()) {
|
||||
if keypair.created.Add(device.keychainExpireTime()).Before(time.Now()) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -199,6 +205,7 @@ func (device *Device) RoutineReceiveIncoming(
|
||||
elem.keypair = keypair
|
||||
elem.endpoint = endpoints[i]
|
||||
elem.counter = 0
|
||||
elem.padding = padding
|
||||
|
||||
elemsForPeer, ok := elemsByPeer[peer]
|
||||
if !ok {
|
||||
@@ -217,16 +224,25 @@ func (device *Device) RoutineReceiveIncoming(
|
||||
if len(packet) != MessageInitiationSize {
|
||||
continue
|
||||
}
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(packet[4:MessageInitiationSize], packet[4:MessageInitiationSize])
|
||||
}
|
||||
|
||||
case MessageResponseType:
|
||||
if len(packet) != MessageResponseSize {
|
||||
continue
|
||||
}
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(packet[4:MessageResponseSize], packet[4:MessageResponseSize])
|
||||
}
|
||||
|
||||
case MessageCookieReplyType:
|
||||
if len(packet) != MessageCookieReplySize {
|
||||
continue
|
||||
}
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(packet[4:MessageCookieReplySize], packet[4:MessageCookieReplySize])
|
||||
}
|
||||
|
||||
default:
|
||||
device.log.Verbosef("Received message with unknown type")
|
||||
@@ -245,7 +261,6 @@ func (device *Device) RoutineReceiveIncoming(
|
||||
default:
|
||||
}
|
||||
}
|
||||
device.aSecMux.RUnlock()
|
||||
for peer, elemsContainer := range elemsByPeer {
|
||||
if peer.isRunning.Load() {
|
||||
peer.queue.inbound.c <- elemsContainer
|
||||
@@ -303,9 +318,6 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
device.log.Verbosef("Routine: handshake worker %d - started", id)
|
||||
|
||||
for elem := range device.queue.handshake.c {
|
||||
|
||||
device.aSecMux.RLock()
|
||||
|
||||
// handle cookie fields and ratelimiting
|
||||
|
||||
switch elem.msgType {
|
||||
@@ -357,7 +369,8 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
|
||||
// endpoints destination address is the source of the datagram
|
||||
|
||||
if device.IsUnderLoad() {
|
||||
disableCookies := device.disableCookies.Load()
|
||||
if !disableCookies && device.IsUnderLoad() {
|
||||
|
||||
// verify MAC2 field
|
||||
|
||||
@@ -391,6 +404,9 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
goto skip
|
||||
}
|
||||
|
||||
// have to reassign msgType for ranged msgType to work
|
||||
msg.Type = elem.msgType
|
||||
|
||||
// consume initiation
|
||||
peer := device.ConsumeMessageInitiation(&msg)
|
||||
if peer == nil {
|
||||
@@ -423,6 +439,9 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
goto skip
|
||||
}
|
||||
|
||||
// have to reassign msgType for ranged msgType to work
|
||||
msg.Type = elem.msgType
|
||||
|
||||
// consume response
|
||||
|
||||
peer := device.ConsumeMessageResponse(&msg)
|
||||
@@ -456,7 +475,6 @@ func (device *Device) RoutineHandshake(id int) {
|
||||
peer.SendKeepalive()
|
||||
}
|
||||
skip:
|
||||
device.aSecMux.RUnlock()
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
}
|
||||
@@ -497,7 +515,12 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||
}
|
||||
rxBytesLen += uint64(len(elem.packet) + MinMessageSize)
|
||||
|
||||
if len(elem.packet) == 0 {
|
||||
udpWindow := elem.padding + MessageTransportHeaderSize + uint32(len(elem.packet))
|
||||
if peer.udpWindow.Load() < udpWindow {
|
||||
peer.udpWindow.Store(udpWindow)
|
||||
}
|
||||
|
||||
if len(elem.packet) == 0 || elem.packet[0] == 0 {
|
||||
device.log.Verbosef("%v - Receiving keepalive packet", peer)
|
||||
continue
|
||||
}
|
||||
@@ -545,10 +568,7 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||
continue
|
||||
}
|
||||
|
||||
bufs = append(
|
||||
bufs,
|
||||
elem.buffer[:MessageTransportOffsetContent+len(elem.packet)],
|
||||
)
|
||||
bufs = append(bufs, elem.buffer[int(elem.padding):int(elem.padding)+MessageTransportHeaderSize+len(elem.packet)])
|
||||
}
|
||||
|
||||
peer.rxBytes.Add(rxBytesLen)
|
||||
@@ -575,3 +595,65 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
|
||||
device.PutInboundElementsContainer(elemsContainer)
|
||||
}
|
||||
}
|
||||
|
||||
func applyHash(dst, src, hash []byte) {
|
||||
for i := range len(dst) {
|
||||
dst[i] = src[i] ^ hash[i]
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) DeterminePacketTypeAndPadding(packet []byte, typeHash []byte) (int, uint32, uint32) {
|
||||
var headerBytes [4]byte
|
||||
var padding uint32
|
||||
var header UintRange
|
||||
var expectedSize int
|
||||
|
||||
size := len(packet)
|
||||
randomTrailers := device.randomTrailers.Load()
|
||||
|
||||
padding = device.paddings.init.Load()
|
||||
header = device.headers.init.Load()
|
||||
expectedSize = int(padding) + MessageInitiationSize
|
||||
|
||||
if size == expectedSize || randomTrailers && size > expectedSize {
|
||||
applyHash(headerBytes[:], packet[padding:padding+4], typeHash)
|
||||
if header.Contains(binary.LittleEndian.Uint32(headerBytes[:])) {
|
||||
return MessageInitiationSize, MessageInitiationType, padding
|
||||
}
|
||||
}
|
||||
|
||||
padding = device.paddings.response.Load()
|
||||
header = device.headers.response.Load()
|
||||
expectedSize = int(padding) + MessageResponseSize
|
||||
|
||||
if size == expectedSize || randomTrailers && size > expectedSize {
|
||||
applyHash(headerBytes[:], packet[padding:padding+4], typeHash)
|
||||
if header.Contains(binary.LittleEndian.Uint32(headerBytes[:])) {
|
||||
return MessageResponseSize, MessageResponseType, padding
|
||||
}
|
||||
}
|
||||
|
||||
padding = device.paddings.cookie.Load()
|
||||
header = device.headers.cookie.Load()
|
||||
expectedSize = int(padding) + MessageCookieReplySize
|
||||
|
||||
if size == expectedSize || randomTrailers && size > expectedSize {
|
||||
applyHash(headerBytes[:], packet[padding:padding+4], typeHash)
|
||||
if header.Contains(binary.LittleEndian.Uint32(headerBytes[:])) {
|
||||
return MessageCookieReplySize, MessageCookieReplyType, padding
|
||||
}
|
||||
}
|
||||
|
||||
padding = device.paddings.transport.Load()
|
||||
header = device.headers.transport.Load()
|
||||
expectedSize = int(padding) + MessageTransportSize
|
||||
|
||||
if size >= expectedSize {
|
||||
applyHash(headerBytes[:], packet[padding:padding+4], typeHash)
|
||||
if header.Contains(binary.LittleEndian.Uint32(headerBytes[:])) {
|
||||
return MessageTransportSize, MessageTransportType, padding
|
||||
}
|
||||
}
|
||||
|
||||
return 0, MessageUnknownType, 0
|
||||
}
|
||||
+188
-109
@@ -1,22 +1,23 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
@@ -47,11 +48,13 @@ import (
|
||||
*/
|
||||
|
||||
type QueueOutboundElement struct {
|
||||
buffer *[MaxMessageSize]byte // slice holding the packet data
|
||||
packet []byte // slice of "buffer" (always!)
|
||||
nonce uint64 // nonce for encryption
|
||||
keypair *Keypair // keypair for encryption
|
||||
peer *Peer // related peer
|
||||
buffer *[MaxMessageSize]byte // slice holding the packet data
|
||||
packet []byte // slice of "buffer" (always!)
|
||||
nonce uint64 // nonce for encryption
|
||||
keypair *Keypair // keypair for encryption
|
||||
peer *Peer // related peer
|
||||
padding uint32
|
||||
isKeepalive bool
|
||||
}
|
||||
|
||||
type QueueOutboundElementsContainer struct {
|
||||
@@ -63,6 +66,8 @@ func (device *Device) NewOutboundElement() *QueueOutboundElement {
|
||||
elem := device.GetOutboundElement()
|
||||
elem.buffer = device.GetMessageBuffer()
|
||||
elem.nonce = 0
|
||||
elem.padding = device.paddings.transport.Load()
|
||||
elem.isKeepalive = false
|
||||
// keypair and peer were cleared (if necessary) by clearPointers.
|
||||
return elem
|
||||
}
|
||||
@@ -83,6 +88,7 @@ func (elem *QueueOutboundElement) clearPointers() {
|
||||
func (peer *Peer) SendKeepalive() {
|
||||
if len(peer.queue.staged) == 0 && peer.isRunning.Load() {
|
||||
elem := peer.device.NewOutboundElement()
|
||||
elem.isKeepalive = true
|
||||
elemsContainer := peer.device.GetOutboundElementsContainer()
|
||||
elemsContainer.elems = append(elemsContainer.elems, elem)
|
||||
select {
|
||||
@@ -100,17 +106,20 @@ func (peer *Peer) SendKeepalive() {
|
||||
func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
if !isRetry {
|
||||
peer.timers.handshakeAttempts.Store(0)
|
||||
peer.timers.maxHandshakeAttempts.Store(peer.device.maxHandshakeAttemps())
|
||||
}
|
||||
|
||||
timeout := peer.device.rekeyMinTimeout()
|
||||
|
||||
peer.handshake.mutex.RLock()
|
||||
if time.Since(peer.handshake.lastSentHandshake) < RekeyTimeout {
|
||||
if time.Since(peer.handshake.lastSentHandshake) < timeout {
|
||||
peer.handshake.mutex.RUnlock()
|
||||
return nil
|
||||
}
|
||||
peer.handshake.mutex.RUnlock()
|
||||
|
||||
peer.handshake.mutex.Lock()
|
||||
if time.Since(peer.handshake.lastSentHandshake) < RekeyTimeout {
|
||||
if time.Since(peer.handshake.lastSentHandshake) < timeout {
|
||||
peer.handshake.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
@@ -124,55 +133,47 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
peer.device.log.Errorf("%v - Failed to create initiation message: %v", peer, err)
|
||||
return err
|
||||
}
|
||||
|
||||
var sendBuffer [][]byte
|
||||
// so only packet processed for cookie generation
|
||||
var junkedHeader []byte
|
||||
if peer.device.isAdvancedSecurityOn() {
|
||||
peer.device.aSecMux.RLock()
|
||||
junks, err := peer.createJunkPackets()
|
||||
peer.device.aSecMux.RUnlock()
|
||||
|
||||
if err != nil {
|
||||
peer.device.log.Errorf("%v - %v", peer, err)
|
||||
return err
|
||||
for _, ipacket := range peer.device.ipackets {
|
||||
if ipacket != nil {
|
||||
buf := make([]byte, ipacket.ObfuscatedLen(0))
|
||||
ipacket.Obfuscate(buf, nil)
|
||||
sendBuffer = append(sendBuffer, buf)
|
||||
}
|
||||
|
||||
if len(junks) > 0 {
|
||||
err = peer.SendBuffers(junks)
|
||||
|
||||
if err != nil {
|
||||
peer.device.log.Errorf("%v - Failed to send junk packets: %v", peer, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
peer.device.aSecMux.RLock()
|
||||
if peer.device.aSecCfg.initPacketJunkSize != 0 {
|
||||
buf := make([]byte, 0, peer.device.aSecCfg.initPacketJunkSize)
|
||||
writer := bytes.NewBuffer(buf[:0])
|
||||
err = appendJunk(writer, peer.device.aSecCfg.initPacketJunkSize)
|
||||
if err != nil {
|
||||
peer.device.log.Errorf("%v - %v", peer, err)
|
||||
peer.device.aSecMux.RUnlock()
|
||||
return err
|
||||
}
|
||||
junkedHeader = writer.Bytes()
|
||||
}
|
||||
peer.device.aSecMux.RUnlock()
|
||||
}
|
||||
|
||||
var buf [MessageInitiationSize]byte
|
||||
writer := bytes.NewBuffer(buf[:0])
|
||||
sendBuffer = append(sendBuffer, peer.device.JunkPackets()...)
|
||||
|
||||
padding := int(peer.device.paddings.init.Load())
|
||||
trailerLen := max(peer.randomTrailer(padding+MessageInitiationSize), 0)
|
||||
|
||||
buf := make([]byte, padding+MessageInitiationSize+trailerLen)
|
||||
|
||||
crypt := buf[:padding]
|
||||
rand.Read(crypt)
|
||||
|
||||
writer := bytes.NewBuffer(buf[padding:padding])
|
||||
binary.Write(writer, binary.LittleEndian, msg)
|
||||
packet := writer.Bytes()
|
||||
peer.cookieGenerator.AddMacs(packet)
|
||||
junkedHeader = append(junkedHeader, packet...)
|
||||
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketSent()
|
||||
|
||||
sendBuffer = append(sendBuffer, junkedHeader)
|
||||
cip, err := peer.device.HeaderProtectionCipher(crypt[:HeaderCipherNonceSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(packet, packet)
|
||||
}
|
||||
|
||||
trailer := buf[padding+MessageInitiationSize:]
|
||||
rand.Read(trailer)
|
||||
|
||||
sendBuffer = append(sendBuffer, buf)
|
||||
err = peer.SendBuffers(sendBuffer)
|
||||
if err != nil {
|
||||
peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err)
|
||||
@@ -194,29 +195,19 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
peer.device.log.Errorf("%v - Failed to create response message: %v", peer, err)
|
||||
return err
|
||||
}
|
||||
var junkedHeader []byte
|
||||
if peer.device.isAdvancedSecurityOn() {
|
||||
peer.device.aSecMux.RLock()
|
||||
if peer.device.aSecCfg.responsePacketJunkSize != 0 {
|
||||
buf := make([]byte, 0, peer.device.aSecCfg.responsePacketJunkSize)
|
||||
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
|
||||
writer := bytes.NewBuffer(buf[:0])
|
||||
|
||||
padding := int(peer.device.paddings.response.Load())
|
||||
trailerLen := max(peer.randomTrailer(padding+MessageResponseSize), 0)
|
||||
|
||||
buf := make([]byte, padding+MessageResponseSize+trailerLen)
|
||||
|
||||
crypt := buf[:padding]
|
||||
rand.Read(crypt)
|
||||
|
||||
writer := bytes.NewBuffer(buf[padding:padding])
|
||||
binary.Write(writer, binary.LittleEndian, response)
|
||||
packet := writer.Bytes()
|
||||
peer.cookieGenerator.AddMacs(packet)
|
||||
junkedHeader = append(junkedHeader, packet...)
|
||||
|
||||
err = peer.BeginSymmetricSession()
|
||||
if err != nil {
|
||||
@@ -228,35 +219,67 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
peer.timersAnyAuthenticatedPacketTraversal()
|
||||
peer.timersAnyAuthenticatedPacketSent()
|
||||
|
||||
cip, err := peer.device.HeaderProtectionCipher(crypt[:HeaderCipherNonceSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(packet, packet)
|
||||
}
|
||||
|
||||
trailer := buf[padding+MessageResponseSize:]
|
||||
rand.Read(trailer)
|
||||
|
||||
// TODO: allocation could be avoided
|
||||
err = peer.SendBuffers([][]byte{junkedHeader})
|
||||
err = peer.SendBuffers([][]byte{buf})
|
||||
if err != nil {
|
||||
peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (device *Device) SendHandshakeCookie(
|
||||
initiatingElem *QueueHandshakeElement,
|
||||
) error {
|
||||
func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement) error {
|
||||
device.log.Verbosef("Sending cookie response for denied handshake message for %v", initiatingElem.endpoint.DstToString())
|
||||
|
||||
sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8])
|
||||
msgType := device.headers.cookie.Load().PickOne()
|
||||
|
||||
reply, err := device.cookieChecker.CreateReply(
|
||||
initiatingElem.packet,
|
||||
sender,
|
||||
initiatingElem.endpoint.DstToBytes(),
|
||||
msgType,
|
||||
)
|
||||
if err != nil {
|
||||
device.log.Errorf("Failed to create cookie reply: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
var buf [MessageCookieReplySize]byte
|
||||
writer := bytes.NewBuffer(buf[:0])
|
||||
padding := int(device.paddings.cookie.Load())
|
||||
trailerLen := max(device.randomTrailer(padding+MessageCookieReplySize), 0)
|
||||
|
||||
buf := make([]byte, padding+MessageCookieReplySize+trailerLen)
|
||||
|
||||
crypt := buf[:padding]
|
||||
rand.Read(crypt)
|
||||
|
||||
writer := bytes.NewBuffer(buf[padding:padding])
|
||||
binary.Write(writer, binary.LittleEndian, reply)
|
||||
packet := writer.Bytes()
|
||||
|
||||
cip, err := device.HeaderProtectionCipher(crypt[:HeaderCipherNonceSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(packet, packet)
|
||||
}
|
||||
|
||||
trailer := buf[padding+MessageCookieReplySize:]
|
||||
rand.Read(trailer)
|
||||
|
||||
// TODO: allocation could be avoided
|
||||
device.net.bind.Send([][]byte{writer.Bytes()}, initiatingElem.endpoint)
|
||||
device.net.bind.Send([][]byte{buf}, initiatingElem.endpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -266,7 +289,7 @@ func (peer *Peer) keepKeyFreshSending() {
|
||||
return
|
||||
}
|
||||
nonce := keypair.sendNonce.Load()
|
||||
if nonce > RekeyAfterMessages || (keypair.isInitiator && time.Since(keypair.created) > RekeyAfterTime) {
|
||||
if nonce > RekeyAfterMessages || (keypair.isInitiator && time.Since(keypair.created) > peer.device.keyRefreshTimeoutSending()) {
|
||||
peer.SendHandshakeInitiation(false)
|
||||
}
|
||||
}
|
||||
@@ -288,7 +311,6 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
elemsByPeer = make(map[*Peer]*QueueOutboundElementsContainer, batchSize)
|
||||
count = 0
|
||||
sizes = make([]int, batchSize)
|
||||
offset = MessageTransportHeaderSize
|
||||
)
|
||||
|
||||
for i := range elems {
|
||||
@@ -306,6 +328,9 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
}()
|
||||
|
||||
for {
|
||||
padding := device.paddings.transport.Load()
|
||||
offset := MessageTransportHeaderSize + int(padding)
|
||||
|
||||
// read packets
|
||||
count, readErr = device.tun.device.Read(bufs, sizes, offset)
|
||||
for i := 0; i < count; i++ {
|
||||
@@ -315,6 +340,7 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
|
||||
elem := elems[i]
|
||||
elem.packet = bufs[i][offset : offset+sizes[i]]
|
||||
elem.padding = padding
|
||||
|
||||
// lookup peer
|
||||
var peer *Peer
|
||||
@@ -409,7 +435,7 @@ top:
|
||||
}
|
||||
|
||||
keypair := peer.keypairs.Current()
|
||||
if keypair == nil || keypair.sendNonce.Load() >= RejectAfterMessages || time.Since(keypair.created) >= RejectAfterTime {
|
||||
if keypair == nil || keypair.sendNonce.Load() >= RejectAfterMessages || time.Since(keypair.created) >= peer.device.keychainExpireTime() {
|
||||
peer.SendHandshakeInitiation(false)
|
||||
return
|
||||
}
|
||||
@@ -469,31 +495,6 @@ 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() {
|
||||
for {
|
||||
select {
|
||||
@@ -524,13 +525,55 @@ func calculatePaddingSize(packetSize, mtu int) int {
|
||||
return paddedSize - lastUnit
|
||||
}
|
||||
|
||||
func (peer *Peer) randomPaddingAddition(packetSize int) int {
|
||||
addition := peer.device.contentPaddingAddition.Load()
|
||||
|
||||
if addition.IsZero() {
|
||||
return -1
|
||||
}
|
||||
|
||||
udpWindow := int(peer.udpWindow.Load())
|
||||
if udpWindow < packetSize {
|
||||
return 0
|
||||
}
|
||||
|
||||
add := int(addition.PickOne())
|
||||
space := udpWindow - packetSize
|
||||
if add > space {
|
||||
add = space
|
||||
}
|
||||
return add
|
||||
}
|
||||
|
||||
func (device *Device) randomTrailer(packetSize int) int {
|
||||
if !device.randomTrailers.Load() {
|
||||
return -1
|
||||
}
|
||||
|
||||
if DefaultUdpWindow < packetSize {
|
||||
return 0
|
||||
}
|
||||
return int(fastrandn(uint32(DefaultUdpWindow - packetSize)))
|
||||
}
|
||||
|
||||
func (peer *Peer) randomTrailer(packetSize int) int {
|
||||
if !peer.device.randomTrailers.Load() {
|
||||
return -1
|
||||
}
|
||||
|
||||
udpWindow := int(peer.udpWindow.Load())
|
||||
if udpWindow < packetSize {
|
||||
return 0
|
||||
}
|
||||
return int(fastrandn(uint32(udpWindow - packetSize)))
|
||||
}
|
||||
|
||||
/* Encrypts the elements in the queue
|
||||
* and marks them for sequential consumption (by releasing the mutex)
|
||||
*
|
||||
* Obs. One instance per core
|
||||
*/
|
||||
func (device *Device) RoutineEncryption(id int) {
|
||||
var paddingZeros [PaddingMultiple]byte
|
||||
var nonce [chacha20poly1305.NonceSize]byte
|
||||
|
||||
defer device.log.Verbosef("Routine: encryption worker %d - stopped", id)
|
||||
@@ -538,30 +581,63 @@ func (device *Device) RoutineEncryption(id int) {
|
||||
|
||||
for elemsContainer := range device.queue.encryption.c {
|
||||
for _, elem := range elemsContainer.elems {
|
||||
udpWindow := elem.padding + MinMessageSize + uint32(len(elem.packet))
|
||||
if elem.peer.udpWindow.Load() < udpWindow {
|
||||
elem.peer.udpWindow.Store(udpWindow)
|
||||
}
|
||||
|
||||
// fill crypto padding
|
||||
crypt := elem.buffer[:elem.padding]
|
||||
rand.Read(crypt)
|
||||
|
||||
// populate header fields
|
||||
header := elem.buffer[:MessageTransportHeaderSize]
|
||||
header := elem.buffer[elem.padding : elem.padding+MessageTransportHeaderSize]
|
||||
|
||||
fieldType := header[0:4]
|
||||
fieldReceiver := header[4:8]
|
||||
fieldNonce := header[8:16]
|
||||
|
||||
binary.LittleEndian.PutUint32(fieldType, MessageTransportType)
|
||||
binary.LittleEndian.PutUint32(fieldType, device.headers.transport.Load().PickOne())
|
||||
binary.LittleEndian.PutUint32(fieldReceiver, elem.keypair.remoteIndex)
|
||||
binary.LittleEndian.PutUint64(fieldNonce, elem.nonce)
|
||||
|
||||
// pad content to multiple of 16
|
||||
paddingSize := calculatePaddingSize(len(elem.packet), int(device.tun.mtu.Load()))
|
||||
elem.packet = append(elem.packet, paddingZeros[:paddingSize]...)
|
||||
packetSize := len(elem.packet) + MinMessageSize + int(elem.padding)
|
||||
mtu := int(device.tun.mtu.Load())
|
||||
|
||||
paddingSize := elem.peer.randomPaddingAddition(packetSize)
|
||||
if paddingSize < 0 {
|
||||
paddingSize = elem.peer.randomTrailer(packetSize)
|
||||
}
|
||||
if paddingSize < 0 {
|
||||
// pad content to multiple of 16
|
||||
paddingSize = calculatePaddingSize(len(elem.packet), mtu)
|
||||
}
|
||||
|
||||
// append trailing zeroes
|
||||
oldLen := len(elem.packet)
|
||||
elem.packet = slices.Grow(elem.packet, paddingSize)
|
||||
elem.packet = elem.packet[:oldLen+paddingSize]
|
||||
clear(elem.packet[oldLen:])
|
||||
|
||||
// encrypt content and release to consumer
|
||||
|
||||
binary.LittleEndian.PutUint64(nonce[4:], elem.nonce)
|
||||
elem.packet = elem.keypair.send.Seal(
|
||||
header,
|
||||
elem.buffer[:elem.padding+MessageTransportHeaderSize],
|
||||
nonce[:],
|
||||
elem.packet,
|
||||
nil,
|
||||
)
|
||||
|
||||
cip, err := device.HeaderProtectionCipher(crypt[:HeaderCipherNonceSize])
|
||||
if err != nil {
|
||||
device.log.Errorf("Routing: header obfuscation failed - packet dropped")
|
||||
elem.packet = nil
|
||||
continue
|
||||
}
|
||||
if cip != nil {
|
||||
cip.XORKeyStream(header, header)
|
||||
}
|
||||
}
|
||||
elemsContainer.Unlock()
|
||||
}
|
||||
@@ -594,14 +670,16 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
device.PutOutboundElementsContainer(elemsContainer)
|
||||
continue
|
||||
}
|
||||
dataSent := false
|
||||
elemsContainer.Lock()
|
||||
for _, elem := range elemsContainer.elems {
|
||||
if len(elem.packet) != MessageKeepaliveSize {
|
||||
if !elem.isKeepalive {
|
||||
dataSent = true
|
||||
}
|
||||
|
||||
bufs = append(bufs, elem.packet)
|
||||
}
|
||||
|
||||
@@ -612,6 +690,7 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
|
||||
if dataSent {
|
||||
peer.timersDataSent()
|
||||
}
|
||||
|
||||
for _, elem := range elemsContainer.elems {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/rwcancel"
|
||||
)
|
||||
|
||||
func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) {
|
||||
func (device *Device) startRouteListener(_ conn.Bind) (*rwcancel.RWCancel, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*
|
||||
* This implements userspace semantics of "sticky sockets", modeled after
|
||||
* WireGuard's kernelspace implementation. This is more or less a straight port
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
* Currently there is no way to achieve this within the net package:
|
||||
* See e.g. https://github.com/golang/go/issues/17930
|
||||
* So this code is remains platform dependent.
|
||||
* So this code remains platform dependent.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/rwcancel"
|
||||
)
|
||||
|
||||
func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) {
|
||||
@@ -47,7 +47,7 @@ func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, er
|
||||
return netlinkCancel, nil
|
||||
}
|
||||
|
||||
func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netlinkCancel *rwcancel.RWCancel) {
|
||||
func (device *Device) routineRouteListener(_ conn.Bind, netlinkSock int, netlinkCancel *rwcancel.RWCancel) {
|
||||
type peerEndpointPtr struct {
|
||||
peer *Peer
|
||||
endpoint *conn.Endpoint
|
||||
|
||||
+125
-28
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*
|
||||
* This is based heavily on timers.c from the kernel implementation.
|
||||
*/
|
||||
@@ -22,24 +22,25 @@ type Timer struct {
|
||||
*time.Timer
|
||||
modifyingLock sync.RWMutex
|
||||
runningLock sync.Mutex
|
||||
isPending bool
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
|
||||
func (peer *Peer) NewTimer(expirationFunction func(*Peer, time.Duration)) *Timer {
|
||||
timer := &Timer{}
|
||||
timer.Timer = time.AfterFunc(time.Hour, func() {
|
||||
timer.runningLock.Lock()
|
||||
defer timer.runningLock.Unlock()
|
||||
|
||||
timer.modifyingLock.Lock()
|
||||
if !timer.isPending {
|
||||
if timer.duration == 0 {
|
||||
timer.modifyingLock.Unlock()
|
||||
return
|
||||
}
|
||||
timer.isPending = false
|
||||
duration := timer.duration
|
||||
timer.modifyingLock.Unlock()
|
||||
timer.duration = 0
|
||||
|
||||
expirationFunction(peer)
|
||||
expirationFunction(peer, duration)
|
||||
})
|
||||
timer.Stop()
|
||||
return timer
|
||||
@@ -47,14 +48,14 @@ func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
|
||||
|
||||
func (timer *Timer) Mod(d time.Duration) {
|
||||
timer.modifyingLock.Lock()
|
||||
timer.isPending = true
|
||||
timer.duration = d
|
||||
timer.Reset(d)
|
||||
timer.modifyingLock.Unlock()
|
||||
}
|
||||
|
||||
func (timer *Timer) Del() {
|
||||
timer.modifyingLock.Lock()
|
||||
timer.isPending = false
|
||||
timer.duration = 0
|
||||
timer.Stop()
|
||||
timer.modifyingLock.Unlock()
|
||||
}
|
||||
@@ -69,16 +70,18 @@ func (timer *Timer) DelSync() {
|
||||
func (timer *Timer) IsPending() bool {
|
||||
timer.modifyingLock.RLock()
|
||||
defer timer.modifyingLock.RUnlock()
|
||||
return timer.isPending
|
||||
return timer.duration > 0
|
||||
}
|
||||
|
||||
func (peer *Peer) timersActive() bool {
|
||||
return peer.isRunning.Load() && peer.device != nil && peer.device.isUp()
|
||||
}
|
||||
|
||||
func expiredRetransmitHandshake(peer *Peer) {
|
||||
if peer.timers.handshakeAttempts.Load() > MaxTimerHandshakes {
|
||||
peer.device.log.Verbosef("%s - Handshake did not complete after %d attempts, giving up", peer, MaxTimerHandshakes+2)
|
||||
func expiredRetransmitHandshake(peer *Peer, d time.Duration) {
|
||||
maxAttempts := peer.timers.maxHandshakeAttempts.Load()
|
||||
|
||||
if peer.timers.handshakeAttempts.Load() > maxAttempts {
|
||||
peer.device.log.Verbosef("%s - Handshake did not complete after %d attempts, giving up", peer, maxAttempts+2)
|
||||
|
||||
if peer.timersActive() {
|
||||
peer.timers.sendKeepalive.Del()
|
||||
@@ -93,11 +96,11 @@ func expiredRetransmitHandshake(peer *Peer) {
|
||||
* of a partial exchange.
|
||||
*/
|
||||
if peer.timersActive() && !peer.timers.zeroKeyMaterial.IsPending() {
|
||||
peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3)
|
||||
peer.timers.zeroKeyMaterial.Mod(peer.device.keychainExpireTime() * 3)
|
||||
}
|
||||
} else {
|
||||
peer.timers.handshakeAttempts.Add(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)
|
||||
peer.device.log.Verbosef("%s - Handshake did not complete after %d seconds, retrying (try %d)", peer, int(d.Seconds()), peer.timers.handshakeAttempts.Load()+1)
|
||||
|
||||
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||
peer.markEndpointSrcForClearing()
|
||||
@@ -106,30 +109,30 @@ func expiredRetransmitHandshake(peer *Peer) {
|
||||
}
|
||||
}
|
||||
|
||||
func expiredSendKeepalive(peer *Peer) {
|
||||
func expiredSendKeepalive(peer *Peer, d time.Duration) {
|
||||
peer.SendKeepalive()
|
||||
if peer.timers.needAnotherKeepalive.Load() {
|
||||
peer.timers.needAnotherKeepalive.Store(false)
|
||||
if peer.timersActive() {
|
||||
peer.timers.sendKeepalive.Mod(KeepaliveTimeout)
|
||||
peer.timers.sendKeepalive.Mod(peer.sendKeepaliveTimeout())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expiredNewHandshake(peer *Peer) {
|
||||
peer.device.log.Verbosef("%s - Retrying handshake because we stopped hearing back after %d seconds", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
|
||||
func expiredNewHandshake(peer *Peer, d time.Duration) {
|
||||
peer.device.log.Verbosef("%s - Retrying handshake because we stopped hearing back after %d seconds", peer, int(d.Seconds()))
|
||||
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||
peer.markEndpointSrcForClearing()
|
||||
peer.SendHandshakeInitiation(false)
|
||||
}
|
||||
|
||||
func expiredZeroKeyMaterial(peer *Peer) {
|
||||
peer.device.log.Verbosef("%s - Removing all keys, since we haven't received a new one in %d seconds", peer, int((RejectAfterTime * 3).Seconds()))
|
||||
func expiredZeroKeyMaterial(peer *Peer, d time.Duration) {
|
||||
peer.device.log.Verbosef("%s - Removing all keys, since we haven't received a new one in %d seconds", peer, int(d.Seconds()))
|
||||
peer.ZeroAndFlushAll()
|
||||
}
|
||||
|
||||
func expiredPersistentKeepalive(peer *Peer) {
|
||||
if peer.persistentKeepaliveInterval.Load() > 0 {
|
||||
func expiredPersistentKeepalive(peer *Peer, d time.Duration) {
|
||||
if !peer.persistentKeepaliveInterval.Load().IsZero() {
|
||||
peer.SendKeepalive()
|
||||
}
|
||||
}
|
||||
@@ -137,7 +140,7 @@ func expiredPersistentKeepalive(peer *Peer) {
|
||||
/* Should be called after an authenticated data packet is sent. */
|
||||
func (peer *Peer) timersDataSent() {
|
||||
if peer.timersActive() && !peer.timers.newHandshake.IsPending() {
|
||||
peer.timers.newHandshake.Mod(KeepaliveTimeout + RekeyTimeout + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs)))
|
||||
peer.timers.newHandshake.Mod(peer.newHandshakeTimeout() + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +148,7 @@ func (peer *Peer) timersDataSent() {
|
||||
func (peer *Peer) timersDataReceived() {
|
||||
if peer.timersActive() {
|
||||
if !peer.timers.sendKeepalive.IsPending() {
|
||||
peer.timers.sendKeepalive.Mod(KeepaliveTimeout)
|
||||
peer.timers.sendKeepalive.Mod(peer.sendKeepaliveTimeout())
|
||||
} else {
|
||||
peer.timers.needAnotherKeepalive.Store(true)
|
||||
}
|
||||
@@ -169,7 +172,7 @@ func (peer *Peer) timersAnyAuthenticatedPacketReceived() {
|
||||
/* Should be called after a handshake initiation message is sent. */
|
||||
func (peer *Peer) timersHandshakeInitiated() {
|
||||
if peer.timersActive() {
|
||||
peer.timers.retransmitHandshake.Mod(RekeyTimeout + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs)))
|
||||
peer.timers.retransmitHandshake.Mod(peer.retransmitHandshakeTimeout() + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +182,7 @@ func (peer *Peer) timersHandshakeComplete() {
|
||||
peer.timers.retransmitHandshake.Del()
|
||||
}
|
||||
peer.timers.handshakeAttempts.Store(0)
|
||||
peer.timers.maxHandshakeAttempts.Store(peer.device.maxHandshakeAttemps())
|
||||
peer.timers.sentLastMinuteHandshake.Store(false)
|
||||
peer.lastHandshakeNano.Store(time.Now().UnixNano())
|
||||
}
|
||||
@@ -186,15 +190,15 @@ func (peer *Peer) timersHandshakeComplete() {
|
||||
/* Should be called after an ephemeral key is created, which is before sending a handshake response or after receiving a handshake response. */
|
||||
func (peer *Peer) timersSessionDerived() {
|
||||
if peer.timersActive() {
|
||||
peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3)
|
||||
peer.timers.zeroKeyMaterial.Mod(peer.device.keychainExpireTime() * 3)
|
||||
}
|
||||
}
|
||||
|
||||
/* Should be called before a packet with authentication -- keepalive, data, or handshake -- is sent, or after one is received. */
|
||||
func (peer *Peer) timersAnyAuthenticatedPacketTraversal() {
|
||||
keepalive := peer.persistentKeepaliveInterval.Load()
|
||||
if keepalive > 0 && peer.timersActive() {
|
||||
peer.timers.persistentKeepalive.Mod(time.Duration(keepalive) * time.Second)
|
||||
if !keepalive.IsZero() && peer.timersActive() {
|
||||
peer.timers.persistentKeepalive.Mod(time.Duration(keepalive.PickOne()) * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +212,7 @@ func (peer *Peer) timersInit() {
|
||||
|
||||
func (peer *Peer) timersStart() {
|
||||
peer.timers.handshakeAttempts.Store(0)
|
||||
peer.timers.maxHandshakeAttempts.Store(peer.device.maxHandshakeAttemps())
|
||||
peer.timers.sentLastMinuteHandshake.Store(false)
|
||||
peer.timers.needAnotherKeepalive.Store(false)
|
||||
}
|
||||
@@ -219,3 +224,95 @@ func (peer *Peer) timersStop() {
|
||||
peer.timers.zeroKeyMaterial.DelSync()
|
||||
peer.timers.persistentKeepalive.DelSync()
|
||||
}
|
||||
|
||||
func (peer *Peer) retransmitHandshakeTimeout() time.Duration {
|
||||
timeout := RekeyTimeout
|
||||
|
||||
if t := peer.device.timings.rekeyTimeoutSec.Load(); !t.IsZero() {
|
||||
timeout = time.Duration(t.PickOne()) * time.Second
|
||||
}
|
||||
|
||||
return timeout
|
||||
}
|
||||
|
||||
func (peer *Peer) sendKeepaliveTimeout() time.Duration {
|
||||
timeout := KeepaliveTimeout
|
||||
|
||||
if t := peer.device.timings.keepaliveTimeoutSec.Load(); !t.IsZero() {
|
||||
timeout = time.Duration(t.PickOne()) * time.Second
|
||||
}
|
||||
|
||||
return timeout
|
||||
}
|
||||
|
||||
func (peer *Peer) newHandshakeTimeout() time.Duration {
|
||||
keepaliveTimeout := KeepaliveTimeout
|
||||
rekeyTimeout := RekeyTimeout
|
||||
|
||||
if t := peer.device.timings.keepaliveTimeoutSec.Load(); !t.IsZero() {
|
||||
keepaliveTimeout = time.Duration(t.Hi()) * time.Second
|
||||
}
|
||||
if t := peer.device.timings.rekeyTimeoutSec.Load(); !t.IsZero() {
|
||||
rekeyTimeout = time.Duration(t.PickOne()) * time.Second
|
||||
}
|
||||
|
||||
return keepaliveTimeout + rekeyTimeout
|
||||
}
|
||||
|
||||
func (device *Device) keyRefreshTimeoutSending() time.Duration {
|
||||
rekeyAfterTime := RekeyAfterTime
|
||||
|
||||
if t := device.timings.rekeyAfterTimeSec.Load(); !t.IsZero() {
|
||||
rekeyAfterTime = time.Duration(t.PickOne()) * time.Second
|
||||
}
|
||||
|
||||
return rekeyAfterTime
|
||||
}
|
||||
|
||||
func (device *Device) keyRefreshTimeoutReceiving() time.Duration {
|
||||
rejectAfterTime := RejectAfterTime
|
||||
keepaliveTimeout := KeepaliveTimeout
|
||||
rekeyTimeout := RekeyTimeout
|
||||
|
||||
if t := device.timings.rejectAfterTimeSec.Load(); !t.IsZero() {
|
||||
rejectAfterTime = time.Duration(t.PickOne()) * time.Second
|
||||
}
|
||||
if t := device.timings.keepaliveTimeoutSec.Load(); !t.IsZero() {
|
||||
keepaliveTimeout = time.Duration(t.Lo()) * time.Second
|
||||
}
|
||||
if t := device.timings.rekeyTimeoutSec.Load(); !t.IsZero() {
|
||||
rekeyTimeout = time.Duration(t.Lo()) * time.Second
|
||||
}
|
||||
|
||||
return max(0, rejectAfterTime-keepaliveTimeout-rekeyTimeout)
|
||||
}
|
||||
|
||||
func (device *Device) keychainExpireTime() time.Duration {
|
||||
rejectAfterTime := RejectAfterTime
|
||||
|
||||
if t := device.timings.rejectAfterTimeSec.Load(); !t.IsZero() {
|
||||
rejectAfterTime = time.Duration(t.Hi()) * time.Second
|
||||
}
|
||||
|
||||
return rejectAfterTime
|
||||
}
|
||||
|
||||
func (device *Device) rekeyMinTimeout() time.Duration {
|
||||
rekeyTimeout := RekeyTimeout
|
||||
|
||||
if t := device.timings.rekeyTimeoutSec.Load(); !t.IsZero() {
|
||||
rekeyTimeout = time.Duration(t.Lo()) * time.Second
|
||||
}
|
||||
|
||||
return rekeyTimeout
|
||||
}
|
||||
|
||||
func (device *Device) maxHandshakeAttemps() uint32 {
|
||||
res := uint32(MaxTimerHandshakes)
|
||||
|
||||
if t := device.timings.maxHandshakeAttemps.Load(); !t.IsZero() {
|
||||
res = t.PickOne()
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -8,7 +8,7 @@ package device
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun"
|
||||
)
|
||||
|
||||
const DefaultMTU = 1420
|
||||
|
||||
+386
-92
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/ipc"
|
||||
)
|
||||
|
||||
type IPCError struct {
|
||||
@@ -70,6 +70,18 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
boolf := func(prefix string, val bool) {
|
||||
buf.Grow(3 + len(prefix))
|
||||
buf.WriteString(prefix)
|
||||
buf.WriteByte('=')
|
||||
if val {
|
||||
buf.WriteByte('1')
|
||||
} else {
|
||||
buf.WriteByte('0')
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
|
||||
}
|
||||
|
||||
func() {
|
||||
// lock required resources
|
||||
@@ -83,6 +95,9 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
|
||||
device.peers.RLock()
|
||||
defer device.peers.RUnlock()
|
||||
|
||||
device.headerProtection.RLock()
|
||||
defer device.headerProtection.RUnlock()
|
||||
|
||||
// serialize device related values
|
||||
|
||||
if !device.staticIdentity.privateKey.IsZero() {
|
||||
@@ -97,36 +112,82 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
|
||||
sendf("fwmark=%d", device.net.fwmark)
|
||||
}
|
||||
|
||||
if device.isAdvancedSecurityOn() {
|
||||
if device.aSecCfg.junkPacketCount != 0 {
|
||||
sendf("jc=%d", device.aSecCfg.junkPacketCount)
|
||||
}
|
||||
if device.aSecCfg.junkPacketMinSize != 0 {
|
||||
sendf("jmin=%d", device.aSecCfg.junkPacketMinSize)
|
||||
}
|
||||
if device.aSecCfg.junkPacketMaxSize != 0 {
|
||||
sendf("jmax=%d", device.aSecCfg.junkPacketMaxSize)
|
||||
}
|
||||
if device.aSecCfg.initPacketJunkSize != 0 {
|
||||
sendf("s1=%d", device.aSecCfg.initPacketJunkSize)
|
||||
}
|
||||
if device.aSecCfg.responsePacketJunkSize != 0 {
|
||||
sendf("s2=%d", device.aSecCfg.responsePacketJunkSize)
|
||||
}
|
||||
if device.aSecCfg.initPacketMagicHeader != 0 {
|
||||
sendf("h1=%d", device.aSecCfg.initPacketMagicHeader)
|
||||
}
|
||||
if device.aSecCfg.responsePacketMagicHeader != 0 {
|
||||
sendf("h2=%d", device.aSecCfg.responsePacketMagicHeader)
|
||||
}
|
||||
if device.aSecCfg.underloadPacketMagicHeader != 0 {
|
||||
sendf("h3=%d", device.aSecCfg.underloadPacketMagicHeader)
|
||||
}
|
||||
if device.aSecCfg.transportPacketMagicHeader != 0 {
|
||||
sendf("h4=%d", device.aSecCfg.transportPacketMagicHeader)
|
||||
if count := device.junk.count.Load(); count != 0 {
|
||||
sendf("jc=%d", count)
|
||||
}
|
||||
|
||||
if min := device.junk.min.Load(); min != 0 {
|
||||
sendf("jmin=%d", min)
|
||||
}
|
||||
|
||||
if max := device.junk.max.Load(); max != 0 {
|
||||
sendf("jmax=%d", max)
|
||||
}
|
||||
|
||||
if padding := device.paddings.init.Load(); padding != 0 {
|
||||
sendf("s1=%d", padding)
|
||||
}
|
||||
|
||||
if padding := device.paddings.response.Load(); padding != 0 {
|
||||
sendf("s2=%d", padding)
|
||||
}
|
||||
|
||||
if padding := device.paddings.cookie.Load(); padding != 0 {
|
||||
sendf("s3=%d", padding)
|
||||
}
|
||||
|
||||
if padding := device.paddings.transport.Load(); padding != 0 {
|
||||
sendf("s4=%d", padding)
|
||||
}
|
||||
|
||||
if header := device.headers.init.Load(); !header.IsZero() {
|
||||
sendf("h1=%s", header.ToString())
|
||||
}
|
||||
|
||||
if header := device.headers.response.Load(); !header.IsZero() {
|
||||
sendf("h2=%s", header.ToString())
|
||||
}
|
||||
|
||||
if header := device.headers.cookie.Load(); !header.IsZero() {
|
||||
sendf("h3=%s", header.ToString())
|
||||
}
|
||||
|
||||
if header := device.headers.transport.Load(); !header.IsZero() {
|
||||
sendf("h4=%s", header.ToString())
|
||||
}
|
||||
|
||||
for i, ipacket := range device.ipackets {
|
||||
if ipacket != nil {
|
||||
sendf("i%d=%s", i+1, ipacket.Spec)
|
||||
}
|
||||
}
|
||||
|
||||
if !device.headerProtection.key.IsZero() {
|
||||
keyf("header_protection_key", (*[32]byte)(&device.headerProtection.key))
|
||||
}
|
||||
|
||||
if addition := device.contentPaddingAddition.Load(); !addition.IsZero() {
|
||||
sendf("content_padding_addition=%s", addition.ToString())
|
||||
}
|
||||
|
||||
if timing := device.timings.rekeyAfterTimeSec.Load(); !timing.IsZero() {
|
||||
sendf("rekey_after_time=%s", timing.ToString())
|
||||
}
|
||||
if timing := device.timings.rekeyTimeoutSec.Load(); !timing.IsZero() {
|
||||
sendf("rekey_timeout=%s", timing.ToString())
|
||||
}
|
||||
if timing := device.timings.rejectAfterTimeSec.Load(); !timing.IsZero() {
|
||||
sendf("reject_after_time=%s", timing.ToString())
|
||||
}
|
||||
if timing := device.timings.keepaliveTimeoutSec.Load(); !timing.IsZero() {
|
||||
sendf("keepalive_timeout=%s", timing.ToString())
|
||||
}
|
||||
if rang := device.timings.maxHandshakeAttemps.Load(); !rang.IsZero() {
|
||||
sendf("max_handshake_attempts=%s", rang.ToString())
|
||||
}
|
||||
boolf("random_trailers", device.randomTrailers.Load())
|
||||
boolf("disable_cookies", device.disableCookies.Load())
|
||||
|
||||
for _, peer := range device.peers.keyMap {
|
||||
// Serialize peer state.
|
||||
peer.handshake.mutex.RLock()
|
||||
@@ -148,7 +209,10 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
|
||||
sendf("last_handshake_time_nsec=%d", nano)
|
||||
sendf("tx_bytes=%d", peer.txBytes.Load())
|
||||
sendf("rx_bytes=%d", peer.rxBytes.Load())
|
||||
sendf("persistent_keepalive_interval=%d", peer.persistentKeepaliveInterval.Load())
|
||||
|
||||
if keepalive := peer.persistentKeepaliveInterval.Load(); !keepalive.IsZero() {
|
||||
sendf("persistent_keepalive_interval=%s", keepalive.ToString())
|
||||
}
|
||||
|
||||
device.allowedips.EntriesForPeer(peer, func(prefix netip.Prefix) bool {
|
||||
sendf("allowed_ip=%s", prefix.String())
|
||||
@@ -177,18 +241,19 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
ipcDev := new(ipcSetDevice)
|
||||
ipcDev.fromDevice(device)
|
||||
peer := new(ipcSetPeer)
|
||||
deviceConfig := true
|
||||
|
||||
tempASecCfg := aSecCfgType{}
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
// Blank line means terminate operation.
|
||||
err := device.handlePostConfig(&tempASecCfg)
|
||||
err := ipcDev.mergeWithDevice(device)
|
||||
if err != nil {
|
||||
return err
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to merge with device: %w", err)
|
||||
}
|
||||
peer.handlePostConfig()
|
||||
return nil
|
||||
@@ -217,7 +282,7 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||
|
||||
var err error
|
||||
if deviceConfig {
|
||||
err = device.handleDeviceLine(key, value, &tempASecCfg)
|
||||
err = device.handleDeviceLine(ipcDev, key, value)
|
||||
} else {
|
||||
err = device.handlePeerLine(peer, key, value)
|
||||
}
|
||||
@@ -225,9 +290,9 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = device.handlePostConfig(&tempASecCfg)
|
||||
err = ipcDev.mergeWithDevice(device)
|
||||
if err != nil {
|
||||
return err
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to merge with device: %w", err)
|
||||
}
|
||||
peer.handlePostConfig()
|
||||
|
||||
@@ -237,7 +302,7 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) handleDeviceLine(key, value string, tempASecCfg *aSecCfgType) error {
|
||||
func (device *Device) handleDeviceLine(ipcDev *ipcSetDevice, key, value string) error {
|
||||
switch key {
|
||||
case "private_key":
|
||||
var sk NoisePrivateKey
|
||||
@@ -278,87 +343,205 @@ func (device *Device) handleDeviceLine(key, value string, tempASecCfg *aSecCfgTy
|
||||
|
||||
case "replace_peers":
|
||||
if value != "true" {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set replace_peers, invalid value: %v", value)
|
||||
return ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"failed to set replace_peers, invalid value: %v",
|
||||
value,
|
||||
)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Removing all peers")
|
||||
device.RemoveAllPeers()
|
||||
|
||||
case "jc":
|
||||
junkPacketCount, err := strconv.Atoi(value)
|
||||
jc, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse junk_packet_count %w", err)
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jc: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating junk_packet_count")
|
||||
tempASecCfg.junkPacketCount = junkPacketCount
|
||||
tempASecCfg.isSet = true
|
||||
|
||||
device.log.Verbosef("UAPI: Updating junk count")
|
||||
device.junk.count.Store(uint32(jc))
|
||||
|
||||
case "jmin":
|
||||
junkPacketMinSize, err := strconv.Atoi(value)
|
||||
jmin, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse junk_packet_min_size %w", err)
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jmin: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating junk_packet_min_size")
|
||||
tempASecCfg.junkPacketMinSize = junkPacketMinSize
|
||||
tempASecCfg.isSet = true
|
||||
|
||||
device.log.Verbosef("UAPI: Updating junk min")
|
||||
device.junk.min.Store(uint32(jmin))
|
||||
|
||||
case "jmax":
|
||||
junkPacketMaxSize, err := strconv.Atoi(value)
|
||||
jmax, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse junk_packet_max_size %w", err)
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jmax: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating junk_packet_max_size")
|
||||
tempASecCfg.junkPacketMaxSize = junkPacketMaxSize
|
||||
tempASecCfg.isSet = true
|
||||
|
||||
device.log.Verbosef("UAPI: Updating junk max")
|
||||
device.junk.max.Store(uint32(jmax))
|
||||
|
||||
case "s1":
|
||||
initPacketJunkSize, err := strconv.Atoi(value)
|
||||
padding, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse init_packet_junk_size %w", err)
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s1: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating init_packet_junk_size")
|
||||
tempASecCfg.initPacketJunkSize = initPacketJunkSize
|
||||
tempASecCfg.isSet = true
|
||||
ipcDev.paddings.init = uint32(padding)
|
||||
|
||||
case "s2":
|
||||
responsePacketJunkSize, err := strconv.Atoi(value)
|
||||
padding, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse response_packet_junk_size %w", err)
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s2: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating response_packet_junk_size")
|
||||
tempASecCfg.responsePacketJunkSize = responsePacketJunkSize
|
||||
tempASecCfg.isSet = true
|
||||
ipcDev.paddings.response = uint32(padding)
|
||||
|
||||
case "s3":
|
||||
padding, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s3: %w", err)
|
||||
}
|
||||
ipcDev.paddings.cookie = uint32(padding)
|
||||
|
||||
case "s4":
|
||||
padding, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s4: %w", err)
|
||||
}
|
||||
ipcDev.paddings.transport = uint32(padding)
|
||||
|
||||
case "h1":
|
||||
initPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse init_packet_magic_header %w", err)
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H1: %w", err)
|
||||
}
|
||||
tempASecCfg.initPacketMagicHeader = uint32(initPacketMagicHeader)
|
||||
tempASecCfg.isSet = true
|
||||
ipcDev.headers.init = rang
|
||||
|
||||
case "h2":
|
||||
responsePacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse response_packet_magic_header %w", err)
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H2: %w", err)
|
||||
}
|
||||
tempASecCfg.responsePacketMagicHeader = uint32(responsePacketMagicHeader)
|
||||
tempASecCfg.isSet = true
|
||||
ipcDev.headers.response = rang
|
||||
|
||||
case "h3":
|
||||
underloadPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse underload_packet_magic_header %w", err)
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H3: %w", err)
|
||||
}
|
||||
tempASecCfg.underloadPacketMagicHeader = uint32(underloadPacketMagicHeader)
|
||||
tempASecCfg.isSet = true
|
||||
ipcDev.headers.cookie = rang
|
||||
|
||||
case "h4":
|
||||
transportPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse transport_packet_magic_header %w", err)
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H4: %w", err)
|
||||
}
|
||||
tempASecCfg.transportPacketMagicHeader = uint32(transportPacketMagicHeader)
|
||||
tempASecCfg.isSet = true
|
||||
ipcDev.headers.transport = rang
|
||||
|
||||
case "i1":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I1: %w", err)
|
||||
}
|
||||
device.ipackets[0] = chain
|
||||
|
||||
case "i2":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I2: %w", err)
|
||||
}
|
||||
device.ipackets[1] = chain
|
||||
|
||||
case "i3":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I3: %w", err)
|
||||
}
|
||||
device.ipackets[2] = chain
|
||||
|
||||
case "i4":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I4: %w", err)
|
||||
}
|
||||
device.ipackets[3] = chain
|
||||
|
||||
case "i5":
|
||||
chain, err := newObfChain(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I5: %w", err)
|
||||
}
|
||||
device.ipackets[4] = chain
|
||||
|
||||
case "header_protection_key":
|
||||
var key HeaderCipherKey
|
||||
err := key.FromHex(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set header_protection_key: %w", err)
|
||||
}
|
||||
ipcDev.headerProtectionKey = key
|
||||
|
||||
case "content_padding_addition":
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse content_padding_addition: %w", err)
|
||||
}
|
||||
|
||||
device.log.Verbosef("UAPI: Updating content padding addition")
|
||||
device.contentPaddingAddition.Store(rang)
|
||||
|
||||
case "rekey_after_time":
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse rekey after time: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating rekey after time")
|
||||
device.timings.rekeyAfterTimeSec.Store(rang)
|
||||
|
||||
case "rekey_timeout":
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse rekey timeout: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating rekey timeout")
|
||||
device.timings.rekeyTimeoutSec.Store(rang)
|
||||
|
||||
case "reject_after_time":
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse reject after time: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating reject after time")
|
||||
device.timings.rejectAfterTimeSec.Store(rang)
|
||||
|
||||
case "keepalive_timeout":
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse keepalive timeout: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating keepalive timeout")
|
||||
device.timings.keepaliveTimeoutSec.Store(rang)
|
||||
|
||||
case "max_handshake_attempts":
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse max handshake attempts: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating max handshake attempts")
|
||||
device.timings.maxHandshakeAttemps.Store(rang)
|
||||
|
||||
case "random_trailers":
|
||||
val, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse random trailers: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating random trailers")
|
||||
device.randomTrailers.Store(val)
|
||||
|
||||
case "disable_cookies":
|
||||
val, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse disable cookies: %w", err)
|
||||
}
|
||||
device.log.Verbosef("UAPI: Updating disable cookies")
|
||||
device.disableCookies.Store(val)
|
||||
|
||||
default:
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "invalid UAPI device key: %v", key)
|
||||
@@ -432,7 +615,11 @@ func (device *Device) handlePeerLine(
|
||||
case "update_only":
|
||||
// allow disabling of creation
|
||||
if value != "true" {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set update only, invalid value: %v", value)
|
||||
return ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"failed to set update only, invalid value: %v",
|
||||
value,
|
||||
)
|
||||
}
|
||||
if peer.created && !peer.dummy {
|
||||
device.RemovePeer(peer.handshake.remoteStatic)
|
||||
@@ -476,20 +663,24 @@ func (device *Device) handlePeerLine(
|
||||
case "persistent_keepalive_interval":
|
||||
device.log.Verbosef("%v - UAPI: Updating persistent keepalive interval", peer.Peer)
|
||||
|
||||
secs, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
var rang UintRange
|
||||
if err := rang.FromString(value); err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set persistent keepalive interval: %w", err)
|
||||
}
|
||||
|
||||
old := peer.persistentKeepaliveInterval.Swap(uint32(secs))
|
||||
old := peer.persistentKeepaliveInterval.Swap(rang)
|
||||
|
||||
// Send immediate keepalive if we're turning it on and before it wasn't on.
|
||||
peer.pkaOn = old == 0 && secs != 0
|
||||
peer.pkaOn = old.IsZero() && !rang.IsZero()
|
||||
|
||||
case "replace_allowed_ips":
|
||||
device.log.Verbosef("%v - UAPI: Removing all allowedips", peer.Peer)
|
||||
if value != "true" {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to replace allowedips, invalid value: %v", value)
|
||||
return ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"failed to replace allowedips, invalid value: %v",
|
||||
value,
|
||||
)
|
||||
}
|
||||
if peer.dummy {
|
||||
return nil
|
||||
@@ -497,7 +688,14 @@ func (device *Device) handlePeerLine(
|
||||
device.allowedips.RemoveByPeer(peer.Peer)
|
||||
|
||||
case "allowed_ip":
|
||||
device.log.Verbosef("%v - UAPI: Adding allowedip", peer.Peer)
|
||||
add := true
|
||||
verb := "Adding"
|
||||
if len(value) > 0 && value[0] == '-' {
|
||||
add = false
|
||||
verb = "Removing"
|
||||
value = value[1:]
|
||||
}
|
||||
device.log.Verbosef("%v - UAPI: %s allowedip", peer.Peer, verb)
|
||||
prefix, err := netip.ParsePrefix(value)
|
||||
if err != nil {
|
||||
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set allowed ip: %w", err)
|
||||
@@ -505,7 +703,11 @@ func (device *Device) handlePeerLine(
|
||||
if peer.dummy {
|
||||
return nil
|
||||
}
|
||||
device.allowedips.Insert(prefix, peer.Peer)
|
||||
if add {
|
||||
device.allowedips.Insert(prefix, peer.Peer)
|
||||
} else {
|
||||
device.allowedips.Remove(prefix, peer.Peer)
|
||||
}
|
||||
|
||||
case "protocol_version":
|
||||
if value != "1" {
|
||||
@@ -557,7 +759,11 @@ func (device *Device) IpcHandle(socket net.Conn) {
|
||||
return
|
||||
}
|
||||
if nextByte != '\n' {
|
||||
err = ipcErrorf(ipc.IpcErrorInvalid, "trailing character in UAPI get: %q", nextByte)
|
||||
err = ipcErrorf(
|
||||
ipc.IpcErrorInvalid,
|
||||
"trailing character in UAPI get: %q",
|
||||
nextByte,
|
||||
)
|
||||
break
|
||||
}
|
||||
err = device.IpcGetOperation(buffered.Writer)
|
||||
@@ -581,3 +787,91 @@ func (device *Device) IpcHandle(socket net.Conn) {
|
||||
buffered.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
type ipcSetDevice struct {
|
||||
headers struct {
|
||||
init UintRange
|
||||
response UintRange
|
||||
cookie UintRange
|
||||
transport UintRange
|
||||
}
|
||||
paddings struct {
|
||||
init uint32
|
||||
response uint32
|
||||
cookie uint32
|
||||
transport uint32
|
||||
}
|
||||
headerProtectionKey HeaderCipherKey
|
||||
}
|
||||
|
||||
func (d *ipcSetDevice) fromDevice(device *Device) {
|
||||
device.headerProtection.RLock()
|
||||
defer device.headerProtection.RUnlock()
|
||||
|
||||
d.headers.init = device.headers.init.Load()
|
||||
d.headers.response = device.headers.response.Load()
|
||||
d.headers.cookie = device.headers.cookie.Load()
|
||||
d.headers.transport = device.headers.transport.Load()
|
||||
|
||||
d.paddings.init = device.paddings.init.Load()
|
||||
d.paddings.response = device.paddings.response.Load()
|
||||
d.paddings.cookie = device.paddings.cookie.Load()
|
||||
d.paddings.transport = device.paddings.transport.Load()
|
||||
|
||||
d.headerProtectionKey = device.headerProtection.key
|
||||
}
|
||||
|
||||
func (d *ipcSetDevice) mergeWithDevice(device *Device) error {
|
||||
device.headerProtection.Lock()
|
||||
defer device.headerProtection.Unlock()
|
||||
|
||||
headers := []UintRange{d.headers.init, d.headers.response, d.headers.cookie, d.headers.transport}
|
||||
for i := 0; i < len(headers); i++ {
|
||||
for j := i + 1; j < len(headers); j++ {
|
||||
left := headers[i]
|
||||
right := headers[j]
|
||||
|
||||
if left.Overlap(right) {
|
||||
return errors.New("headers must not overlap")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device.log.Verbosef("UAPI: Updating h1 padding")
|
||||
device.headers.init.Store(d.headers.init)
|
||||
|
||||
device.log.Verbosef("UAPI: Updating h2 padding")
|
||||
device.headers.response.Store(d.headers.response)
|
||||
|
||||
device.log.Verbosef("UAPI: Updating h3 padding")
|
||||
device.headers.cookie.Store(d.headers.cookie)
|
||||
|
||||
device.log.Verbosef("UAPI: Updating h4 padding")
|
||||
device.headers.transport.Store(d.headers.transport)
|
||||
|
||||
if !d.headerProtectionKey.IsZero() {
|
||||
paddings := []uint32{d.paddings.init, d.paddings.response, d.paddings.cookie, d.paddings.transport}
|
||||
for i, padding := range paddings {
|
||||
if padding < HeaderCipherNonceSize {
|
||||
return fmt.Errorf("S%d must be more then %d to use headerProtection", i, HeaderCipherNonceSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
device.log.Verbosef("UAPI: Updating s1 padding")
|
||||
device.paddings.init.Store(d.paddings.init)
|
||||
|
||||
device.log.Verbosef("UAPI: Updating s2 padding")
|
||||
device.paddings.response.Store(d.paddings.response)
|
||||
|
||||
device.log.Verbosef("UAPI: Updating s3 padding")
|
||||
device.paddings.cookie.Store(d.paddings.cookie)
|
||||
|
||||
device.log.Verbosef("UAPI: Updating s4 padding")
|
||||
device.paddings.transport.Store(d.paddings.transport)
|
||||
|
||||
device.log.Verbosef("UAPI: Updating header protection key")
|
||||
device.headerProtection.key = d.headerProtectionKey
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -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
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
package main
|
||||
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
module github.com/amnezia-vpn/amneziawg-go
|
||||
module github.com/amnezia-vpn/amneziawg-go/v3
|
||||
|
||||
go 1.20
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/tevino/abool/v2 v2.1.0
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/net v0.21.0
|
||||
golang.org/x/sys v0.17.0
|
||||
github.com/goccy/go-yaml v1.17.1
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.getoutline.org/sdk v0.0.23
|
||||
golang.getoutline.org/sdk/x v0.2.0
|
||||
golang.org/x/crypto v0.42.0
|
||||
golang.org/x/net v0.44.0
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.48.1 // indirect
|
||||
github.com/shadowsocks/go-shadowsocks2 v0.1.5 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
)
|
||||
@@ -1,16 +1,95 @@
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
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=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
|
||||
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
|
||||
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
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/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
|
||||
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28=
|
||||
github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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/things-go/go-socks5 v0.0.5 h1:qvKaGcBkfDrUL33SchHN93srAmYGzb4CxSM2DPYufe8=
|
||||
github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRwzfsH64Uh0IQ=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
golang.getoutline.org/sdk v0.0.23 h1:UKoKCrRH3Ed6Jpg8ODYwOo6O1B1lvfIHPreMrTkSTcc=
|
||||
golang.getoutline.org/sdk v0.0.23/go.mod h1:nKZlO//e/sRFk+rp8gm8EJ5RasDSyY+fGDNSd3I2iaA=
|
||||
golang.getoutline.org/sdk/x v0.2.0 h1:4kuT2SgkPXktwPwT6CXuF+Jwe+COAGqiBDZbgJkS5fM=
|
||||
golang.getoutline.org/sdk/x v0.2.0/go.mod h1:vyWoHW0PUsnRHdLXiDGv1Z8X48Asp2ALgwIQfJfAaNs=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b h1:WX7nnnLfCEXg+FmdYZPai2XuP3VqCP1HZVMST0n9DF0=
|
||||
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b/go.mod h1:EiXZlVfUTaAyySFVJb9rsODuiO+WXu8HrUuySb7nYFw=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE=
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc/namedpipe"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/ipc/namedpipe"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/rwcancel"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/rwcancel"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
@@ -8,7 +8,7 @@ package ipc
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc/namedpipe"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/ipc/namedpipe"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -62,7 +62,7 @@ func init() {
|
||||
func UAPIListen(name string) (net.Listener, error) {
|
||||
listener, err := (&namedpipe.ListenConfig{
|
||||
SecurityDescriptor: UAPISecurityDescriptor,
|
||||
}).Listen(`\\.\pipe\ProtectedPrefix\Administrators\WireGuard\` + name)
|
||||
}).Listen(`\\.\pipe\ProtectedPrefix\Administrators\AmneziaWG\` + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/ipc"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@@ -46,20 +46,20 @@ func warning() {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────┐")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "│ Running wireguard-go is not required because this │")
|
||||
fmt.Fprintln(os.Stderr, "│ kernel has first class support for WireGuard. For │")
|
||||
fmt.Fprintln(os.Stderr, "│ information on installing the kernel module, │")
|
||||
fmt.Fprintln(os.Stderr, "│ please visit: │")
|
||||
fmt.Fprintln(os.Stderr, "│ https://www.wireguard.com/install/ │")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────┘")
|
||||
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────────────┐")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "│ Running amneziawg-go is not required because this │")
|
||||
fmt.Fprintln(os.Stderr, "│ kernel has first class support for AmneziaWG. For │")
|
||||
fmt.Fprintln(os.Stderr, "│ information on installing the kernel module, │")
|
||||
fmt.Fprintln(os.Stderr, "│ please visit: │")
|
||||
fmt.Fprintln(os.Stderr, "| https://github.com/amnezia-vpn/amneziawg-linux-kernel-module │")
|
||||
fmt.Fprintln(os.Stderr, "│ │")
|
||||
fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────────────┘")
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 2 && os.Args[1] == "--version" {
|
||||
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", Version, runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Printf("amneziawg-go %s\n\nUserspace AmneziaWG daemon for %s-%s.\nInformation available at https://amnezia.org\n", Version, runtime.GOOS, runtime.GOARCH)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func main() {
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
|
||||
logger.Verbosef("Starting wireguard-go version %s", Version)
|
||||
logger.Verbosef("Starting amneziawg-go version %s", Version)
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to create TUN device: %v", err)
|
||||
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -12,11 +12,11 @@ import (
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/ipc"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/ipc"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -30,13 +30,13 @@ func main() {
|
||||
}
|
||||
interfaceName := os.Args[1]
|
||||
|
||||
fmt.Fprintln(os.Stderr, "Warning: this is a test program for Windows, mainly used for debugging this Go package. For a real WireGuard for Windows client, the repo you want is <https://git.zx2c4.com/wireguard-windows/>, which includes this code as a module.")
|
||||
fmt.Fprintln(os.Stderr, "Warning: this is a test program for Windows, mainly used for debugging this Go package. For a real AmneziaWG for Windows client, please visit: https://amnezia.org")
|
||||
|
||||
logger := device.NewLogger(
|
||||
device.LogLevelVerbose,
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
logger.Verbosef("Starting wireguard-go version %s", Version)
|
||||
logger.Verbosef("Starting amneziawg-go version %s", Version)
|
||||
|
||||
tun, err := tun.CreateTUN(interfaceName, 0)
|
||||
if err == nil {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun/netstack"
|
||||
"golang.getoutline.org/sdk/transport"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
)
|
||||
|
||||
type DialerOptions struct {
|
||||
Ipc string
|
||||
Prefixes []netip.Prefix
|
||||
Mtu int
|
||||
Dns []netip.Addr
|
||||
}
|
||||
|
||||
func NewStreamDialer(opts DialerOptions) (*StreamDialer, error) {
|
||||
var localAddresses []netip.Addr
|
||||
for _, prefix := range opts.Prefixes {
|
||||
localAddresses = append(localAddresses, prefix.Addr())
|
||||
}
|
||||
|
||||
tun, tnet, err := netstack.CreateNetTUN(localAddresses, opts.Dns, opts.Mtu)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create network tun: %v", err)
|
||||
}
|
||||
|
||||
awgLogger := device.Logger{
|
||||
Verbosef: func(format string, args ...any) {
|
||||
},
|
||||
Errorf: func(format string, args ...any) {
|
||||
},
|
||||
}
|
||||
|
||||
dev := device.NewDevice(tun, conn.NewDefaultBind(), &awgLogger)
|
||||
if err := dev.IpcSet(opts.Ipc); err != nil {
|
||||
return nil, fmt.Errorf("failed to configure device: %v", err)
|
||||
}
|
||||
|
||||
if err := dev.Up(); err != nil {
|
||||
return nil, fmt.Errorf("failed to start awg device: %v", err)
|
||||
}
|
||||
|
||||
return &StreamDialer{
|
||||
tnet: tnet,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ transport.StreamDialer = (*StreamDialer)(nil)
|
||||
|
||||
type StreamDialer struct {
|
||||
tnet *netstack.Net
|
||||
}
|
||||
|
||||
func (d *StreamDialer) DialStream(ctx context.Context, raddr string) (transport.StreamConn, error) {
|
||||
host, port, err := net.SplitHostPort(raddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse raddr: %v", err)
|
||||
}
|
||||
if l := len(host); l > 0 && host[l-1] == '.' {
|
||||
host = host[:l-1]
|
||||
raddr = net.JoinHostPort(host, port)
|
||||
}
|
||||
|
||||
conn, err := d.tnet.DialContext(ctx, "tcp", raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn.(*gonet.TCPConn), nil
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package outline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"golang.getoutline.org/sdk/transport"
|
||||
"golang.getoutline.org/sdk/x/mobileproxy"
|
||||
"golang.getoutline.org/sdk/x/smart"
|
||||
)
|
||||
|
||||
type DeviceConfig struct {
|
||||
PrivateKey string `yaml:"private_key"`
|
||||
Address []string `yaml:"address"`
|
||||
Dns []string `yaml:"dns"`
|
||||
Mtu int `yaml:"mtu,omitempty"`
|
||||
Jc int `yaml:"jc,omitempty"`
|
||||
Jmin int `yaml:"jmin,omitempty"`
|
||||
Jmax int `yaml:"jmax,omitempty"`
|
||||
S1 int `yaml:"s1,omitempty"`
|
||||
S2 int `yaml:"s2,omitempty"`
|
||||
S3 int `yaml:"s3,omitempty"`
|
||||
S4 int `yaml:"s4,omitempty"`
|
||||
H1 string `yaml:"h1,omitempty"`
|
||||
H2 string `yaml:"h2,omitempty"`
|
||||
H3 string `yaml:"h3,omitempty"`
|
||||
H4 string `yaml:"h4,omitempty"`
|
||||
I1 string `yaml:"i1,omitempty"`
|
||||
I2 string `yaml:"i2,omitempty"`
|
||||
I3 string `yaml:"i3,omitempty"`
|
||||
I4 string `yaml:"i4,omitempty"`
|
||||
I5 string `yaml:"i5,omitempty"`
|
||||
Peers []PeerConfig `yaml:"peers,omitempty"`
|
||||
}
|
||||
|
||||
type PeerConfig struct {
|
||||
PublicKey string `yaml:"public_key"`
|
||||
PresharedKey string `yaml:"preshared_key,omitempty"`
|
||||
Endpoint string `yaml:"endpoint"`
|
||||
AllowedIPs []string `yaml:"allowed_ips"`
|
||||
PersistentKeepaliveInterval uint16 `yaml:"persistent_keepalive_interval,omitempty"`
|
||||
}
|
||||
|
||||
func mapYamlToConfig(y smart.YAMLNode) (*DeviceConfig, error) {
|
||||
bytes, err := yaml.Marshal(y)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal yaml: %v", err)
|
||||
}
|
||||
|
||||
var cfg DeviceConfig
|
||||
if err = yaml.Unmarshal(bytes, &cfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal yaml: %v", err)
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func genIpcString(cfg *DeviceConfig) (string, error) {
|
||||
privateKeyBytes, err := base64.StdEncoding.DecodeString(cfg.PrivateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to decode private key: %v", err)
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("private_key=")
|
||||
b.WriteString(hex.EncodeToString(privateKeyBytes))
|
||||
|
||||
if cfg.Jc != 0 {
|
||||
b.WriteString("\njc=")
|
||||
b.WriteString(strconv.Itoa(cfg.Jc))
|
||||
}
|
||||
if cfg.Jmin != 0 {
|
||||
b.WriteString("\njmin=")
|
||||
b.WriteString(strconv.Itoa(cfg.Jmin))
|
||||
}
|
||||
if cfg.Jmax != 0 {
|
||||
b.WriteString("\njmax=")
|
||||
b.WriteString(strconv.Itoa(cfg.Jmax))
|
||||
}
|
||||
if cfg.S1 != 0 {
|
||||
b.WriteString("\ns1=")
|
||||
b.WriteString(strconv.Itoa(cfg.S1))
|
||||
}
|
||||
if cfg.S2 != 0 {
|
||||
b.WriteString("\ns2=")
|
||||
b.WriteString(strconv.Itoa(cfg.S2))
|
||||
}
|
||||
if cfg.S3 != 0 {
|
||||
b.WriteString("\ns3=")
|
||||
b.WriteString(strconv.Itoa(cfg.S3))
|
||||
}
|
||||
if cfg.S4 != 0 {
|
||||
b.WriteString("\ns4=")
|
||||
b.WriteString(strconv.Itoa(cfg.S4))
|
||||
}
|
||||
if cfg.H1 != "" {
|
||||
b.WriteString("\nh1=")
|
||||
b.WriteString(cfg.H1)
|
||||
}
|
||||
if cfg.H2 != "" {
|
||||
b.WriteString("\nh2=")
|
||||
b.WriteString(cfg.H2)
|
||||
}
|
||||
if cfg.H3 != "" {
|
||||
b.WriteString("\nh3=")
|
||||
b.WriteString(cfg.H3)
|
||||
}
|
||||
if cfg.H4 != "" {
|
||||
b.WriteString("\nh4=")
|
||||
b.WriteString(cfg.H4)
|
||||
}
|
||||
if cfg.I1 != "" {
|
||||
b.WriteString("\ni1=")
|
||||
b.WriteString(cfg.I1)
|
||||
}
|
||||
if cfg.I2 != "" {
|
||||
b.WriteString("\ni2=")
|
||||
b.WriteString(cfg.I2)
|
||||
}
|
||||
if cfg.I3 != "" {
|
||||
b.WriteString("\ni3=")
|
||||
b.WriteString(cfg.I3)
|
||||
}
|
||||
if cfg.I4 != "" {
|
||||
b.WriteString("\ni4=")
|
||||
b.WriteString(cfg.I4)
|
||||
}
|
||||
if cfg.I5 != "" {
|
||||
b.WriteString("\ni5=")
|
||||
b.WriteString(cfg.I5)
|
||||
}
|
||||
|
||||
for _, peer := range cfg.Peers {
|
||||
publicKeyBytes, err := base64.StdEncoding.DecodeString(peer.PublicKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to decode public key: %v", err)
|
||||
}
|
||||
|
||||
b.WriteString("\npublic_key=")
|
||||
b.WriteString(hex.EncodeToString(publicKeyBytes))
|
||||
|
||||
b.WriteString("\nendpoint=")
|
||||
b.WriteString(peer.Endpoint)
|
||||
|
||||
for _, allowedIp := range peer.AllowedIPs {
|
||||
b.WriteString("\nallowed_ip=")
|
||||
b.WriteString(allowedIp)
|
||||
}
|
||||
|
||||
if peer.PresharedKey != "" {
|
||||
presharedKeyBytes, err := base64.StdEncoding.DecodeString(peer.PresharedKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to decode preshared key: %v", err)
|
||||
}
|
||||
|
||||
b.WriteString("\npreshared_key=")
|
||||
b.WriteString(hex.EncodeToString(presharedKeyBytes))
|
||||
}
|
||||
|
||||
if peer.PersistentKeepaliveInterval != 0 {
|
||||
b.WriteString("\npersistent_keepalive_interval=")
|
||||
b.WriteString(strconv.Itoa(int(peer.PersistentKeepaliveInterval)))
|
||||
}
|
||||
}
|
||||
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
func FallbackParser(ctx context.Context, y smart.YAMLNode) (transport.StreamDialer, string, error) {
|
||||
cfg, err := mapYamlToConfig(y)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to map yaml to config: %v", err)
|
||||
}
|
||||
|
||||
ipc, err := genIpcString(cfg)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("faield to generate ipc config: %v", err)
|
||||
}
|
||||
|
||||
var prefixes []netip.Prefix
|
||||
for _, address := range cfg.Address {
|
||||
prefix, err := netip.ParsePrefix(address)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to parse address: %v", err)
|
||||
}
|
||||
prefixes = append(prefixes, prefix)
|
||||
}
|
||||
|
||||
var dns []netip.Addr
|
||||
for _, saddr := range cfg.Dns {
|
||||
addr, err := netip.ParseAddr(saddr)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to parse dns: %v", err)
|
||||
}
|
||||
dns = append(dns, addr)
|
||||
}
|
||||
|
||||
if cfg.Mtu == 0 {
|
||||
cfg.Mtu = 1408
|
||||
}
|
||||
|
||||
dialer, err := NewStreamDialer(DialerOptions{
|
||||
Ipc: ipc,
|
||||
Prefixes: prefixes,
|
||||
Mtu: cfg.Mtu,
|
||||
Dns: dns,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to create dialer: %v", err)
|
||||
}
|
||||
|
||||
return dialer, ipc, nil
|
||||
}
|
||||
|
||||
func RegisterFallbackParser(opt *mobileproxy.SmartDialerOptions, name string) {
|
||||
opt.RegisterFallbackParser(name, FallbackParser)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package outline_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
awg "github.com/amnezia-vpn/amneziawg-go/v3/outline"
|
||||
"golang.getoutline.org/sdk/x/mobileproxy"
|
||||
)
|
||||
|
||||
const cfg = `
|
||||
dns:
|
||||
- {system: {}}
|
||||
tls:
|
||||
- ""
|
||||
fallback:
|
||||
- awg:
|
||||
address: [10.0.0.0/32]
|
||||
dns: [8.8.8.8, 8.8.4.4]
|
||||
private_key: +CdqlYvjqZ3OUr4mLWvGJo1h67CWpQwMIxA5OpyiJUM=
|
||||
jc: 4
|
||||
jmin: 50
|
||||
jmax: 100
|
||||
s1: 87
|
||||
s2: 65
|
||||
s3: 43
|
||||
s4: 21
|
||||
h1: 1000000000-1000000001
|
||||
h2: 2000000000-2000000002
|
||||
h3: 3000000000-3000000003
|
||||
h4: 4000000000-4000000004
|
||||
peers:
|
||||
- public_key: EGxNYihRLKQ9nvdOE5j5aZ7rtw3ttzJS1xxaJpgYYHI=
|
||||
preshared_key: 2OiSh6rP3t/g39jgJNGK70B+nize821yIFNtUqi8/XU=
|
||||
endpoint: 123.123.123.123:51820
|
||||
allowed_ips: [0.0.0.0/0, ::/0]
|
||||
persistent_keepalive_interval: 25
|
||||
`
|
||||
|
||||
var testDomains = mobileproxy.NewListFromLines("example.com")
|
||||
|
||||
func Test_outlineIntegration(t *testing.T) {
|
||||
opts := mobileproxy.NewSmartDialerOptions(testDomains, cfg)
|
||||
opts.SetLogWriter(mobileproxy.NewStderrLogWriter())
|
||||
awg.RegisterFallbackParser(opts, "awg")
|
||||
dialer, err := opts.NewStreamDialer()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = mobileproxy.RunProxy("", dialer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
// Package replay implements an efficient anti-replay algorithm as specified in RFC 6479.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package replay
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
// Package rwcancel implements cancelable read/write operations on
|
||||
@@ -64,7 +64,7 @@ func (rw *RWCancel) ReadyRead() bool {
|
||||
|
||||
func (rw *RWCancel) ReadyWrite() bool {
|
||||
closeFd := int32(rw.closingReader.Fd())
|
||||
pollFds := []unix.PollFd{{Fd: int32(rw.fd), Events: unix.POLLOUT}, {Fd: closeFd, Events: unix.POLLOUT}}
|
||||
pollFds := []unix.PollFd{{Fd: int32(rw.fd), Events: unix.POLLOUT}, {Fd: closeFd, Events: unix.POLLIN}}
|
||||
var err error
|
||||
for {
|
||||
_, err = unix.Poll(pollFds, -1)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tai64n
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tai64n
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
+53
-69
@@ -1,102 +1,86 @@
|
||||
package tun
|
||||
|
||||
import "encoding/binary"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// TODO: Explore SIMD and/or other assembly optimizations.
|
||||
// TODO: Test native endian loads. See RFC 1071 section 2 part B.
|
||||
func checksumNoFold(b []byte, initial uint64) uint64 {
|
||||
ac := initial
|
||||
tmp := make([]byte, 8)
|
||||
binary.NativeEndian.PutUint64(tmp, initial)
|
||||
ac := binary.BigEndian.Uint64(tmp)
|
||||
var carry uint64
|
||||
|
||||
for len(b) >= 128 {
|
||||
ac += uint64(binary.BigEndian.Uint32(b[:4]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[4:8]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[8:12]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[12:16]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[16:20]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[20:24]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[24:28]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[28:32]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[32:36]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[36:40]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[40:44]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[44:48]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[48:52]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[52:56]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[56:60]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[60:64]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[64:68]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[68:72]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[72:76]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[76:80]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[80:84]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[84:88]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[88:92]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[92:96]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[96:100]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[100:104]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[104:108]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[108:112]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[112:116]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[116:120]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[120:124]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[124:128]))
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[32:40]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[40:48]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[48:56]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[56:64]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[64:72]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[72:80]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[80:88]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[88:96]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[96:104]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[104:112]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[112:120]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[120:128]), carry)
|
||||
ac += carry
|
||||
b = b[128:]
|
||||
}
|
||||
if len(b) >= 64 {
|
||||
ac += uint64(binary.BigEndian.Uint32(b[:4]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[4:8]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[8:12]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[12:16]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[16:20]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[20:24]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[24:28]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[28:32]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[32:36]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[36:40]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[40:44]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[44:48]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[48:52]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[52:56]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[56:60]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[60:64]))
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[32:40]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[40:48]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[48:56]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[56:64]), carry)
|
||||
ac += carry
|
||||
b = b[64:]
|
||||
}
|
||||
if len(b) >= 32 {
|
||||
ac += uint64(binary.BigEndian.Uint32(b[:4]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[4:8]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[8:12]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[12:16]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[16:20]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[20:24]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[24:28]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[28:32]))
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry)
|
||||
ac += carry
|
||||
b = b[32:]
|
||||
}
|
||||
if len(b) >= 16 {
|
||||
ac += uint64(binary.BigEndian.Uint32(b[:4]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[4:8]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[8:12]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[12:16]))
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry)
|
||||
ac += carry
|
||||
b = b[16:]
|
||||
}
|
||||
if len(b) >= 8 {
|
||||
ac += uint64(binary.BigEndian.Uint32(b[:4]))
|
||||
ac += uint64(binary.BigEndian.Uint32(b[4:8]))
|
||||
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0)
|
||||
ac += carry
|
||||
b = b[8:]
|
||||
}
|
||||
if len(b) >= 4 {
|
||||
ac += uint64(binary.BigEndian.Uint32(b))
|
||||
ac, carry = bits.Add64(ac, uint64(binary.NativeEndian.Uint32(b[:4])), 0)
|
||||
ac += carry
|
||||
b = b[4:]
|
||||
}
|
||||
if len(b) >= 2 {
|
||||
ac += uint64(binary.BigEndian.Uint16(b))
|
||||
ac, carry = bits.Add64(ac, uint64(binary.NativeEndian.Uint16(b[:2])), 0)
|
||||
ac += carry
|
||||
b = b[2:]
|
||||
}
|
||||
if len(b) == 1 {
|
||||
ac += uint64(b[0]) << 8
|
||||
tmp := binary.NativeEndian.Uint16([]byte{b[0], 0})
|
||||
ac, carry = bits.Add64(ac, uint64(tmp), 0)
|
||||
ac += carry
|
||||
}
|
||||
|
||||
return ac
|
||||
binary.NativeEndian.PutUint64(tmp, ac)
|
||||
return binary.BigEndian.Uint64(tmp)
|
||||
}
|
||||
|
||||
func checksum(b []byte, initial uint64) uint16 {
|
||||
|
||||
@@ -1,11 +1,74 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func checksumRef(b []byte, initial uint16) uint16 {
|
||||
ac := uint64(initial)
|
||||
|
||||
for len(b) >= 2 {
|
||||
ac += uint64(binary.BigEndian.Uint16(b))
|
||||
b = b[2:]
|
||||
}
|
||||
if len(b) == 1 {
|
||||
ac += uint64(b[0]) << 8
|
||||
}
|
||||
|
||||
for (ac >> 16) > 0 {
|
||||
ac = (ac >> 16) + (ac & 0xffff)
|
||||
}
|
||||
return uint16(ac)
|
||||
}
|
||||
|
||||
func pseudoHeaderChecksumRefNoFold(protocol uint8, srcAddr, dstAddr []byte, totalLen uint16) uint16 {
|
||||
sum := checksumRef(srcAddr, 0)
|
||||
sum = checksumRef(dstAddr, sum)
|
||||
sum = checksumRef([]byte{0, protocol}, sum)
|
||||
tmp := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(tmp, totalLen)
|
||||
return checksumRef(tmp, sum)
|
||||
}
|
||||
|
||||
func TestChecksum(t *testing.T) {
|
||||
for length := 0; length <= 9001; length++ {
|
||||
buf := make([]byte, length)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
rng.Read(buf)
|
||||
csum := checksum(buf, 0x1234)
|
||||
csumRef := checksumRef(buf, 0x1234)
|
||||
if csum != csumRef {
|
||||
t.Error("Expected checksum", csumRef, "got", csum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPseudoHeaderChecksum(t *testing.T) {
|
||||
for _, addrLen := range []int{4, 16} {
|
||||
for length := 0; length <= 9001; length++ {
|
||||
srcAddr := make([]byte, addrLen)
|
||||
dstAddr := make([]byte, addrLen)
|
||||
buf := make([]byte, length)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
rng.Read(srcAddr)
|
||||
rng.Read(dstAddr)
|
||||
rng.Read(buf)
|
||||
phSum := pseudoHeaderChecksumNoFold(unix.IPPROTO_TCP, srcAddr, dstAddr, uint16(length))
|
||||
csum := checksum(buf, phSum)
|
||||
phSumRef := pseudoHeaderChecksumRefNoFold(unix.IPPROTO_TCP, srcAddr, dstAddr, uint16(length))
|
||||
csumRef := checksumRef(buf, phSumRef)
|
||||
if csum != csumRef {
|
||||
t.Error("Expected checksumRef", csumRef, "got", csum)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkChecksum(b *testing.B) {
|
||||
lengths := []int{
|
||||
64,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
"net/http"
|
||||
"net/netip"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/netstack"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/v3/tun/netstack"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
Loaded 100 of 114 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user