Compare commits

..
Author SHA1 Message Date
Iurii Egorov 28b96cd1b0 udptlspipe basic implementation 2024-03-13 17:44:59 +03:00
114 changed files with 1146 additions and 2662 deletions

No files matched your search

+7 -14
View File
@@ -1,24 +1,17 @@
FROM golang:1.25.12 as awg FROM golang:1.20 as awg
COPY . /awg COPY . /awg
WORKDIR /awg WORKDIR /awg
RUN go mod download && \ RUN go mod download && \
go mod verify && \ go mod verify && \
go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin 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 FROM alpine:3.19
ARG AWGTOOLS_RELEASE="1.0.20240213"
RUN apk --no-cache add iproute2 iptables bash RUN apk --no-cache add iproute2 iptables bash && \
COPY --from=tools /amneziawg-tools/src/wg /usr/bin/awg cd /usr/bin/ && \
COPY --from=tools /amneziawg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick wget https://github.com/amnezia-vpn/amneziawg-tools/releases/download/v${AWGTOOLS_RELEASE}/alpine-3.19-amneziawg-tools.zip && \
RUN chmod +x /usr/bin/awg /usr/bin/awg-quick && \ unzip -j alpine-3.19-amneziawg-tools.zip && \
chmod +x /usr/bin/awg /usr/bin/awg-quick && \
ln -s /usr/bin/awg /usr/bin/wg && \ ln -s /usr/bin/awg /usr/bin/wg && \
ln -s /usr/bin/awg-quick /usr/bin/wg-quick ln -s /usr/bin/awg-quick /usr/bin/wg-quick
COPY --from=awg /usr/bin/amneziawg-go /usr/bin/amneziawg-go COPY --from=awg /usr/bin/amneziawg-go /usr/bin/amneziawg-go
-2
View File
@@ -1,5 +1,3 @@
Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
+1 -1
View File
@@ -9,7 +9,7 @@ MAKEFLAGS += --no-print-directory
generate-version-and-build: generate-version-and-build:
@export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \ @export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \
tag="$$(git describe --tags --dirty 2>/dev/null)" && \ tag="$$(git describe --dirty 2>/dev/null)" && \
ver="$$(printf 'package main\n\nconst Version = "%s"\n' "$$tag")" && \ ver="$$(printf 'package main\n\nconst Version = "%s"\n' "$$tag")" && \
[ "$$(cat version.go 2>/dev/null)" != "$$ver" ] && \ [ "$$(cat version.go 2>/dev/null)" != "$$ver" ] && \
echo "$$ver" > version.go && \ echo "$$ver" > version.go && \
+3 -114
View File
@@ -21,7 +21,7 @@ To run amneziawg-go without forking to the background, pass `-f` or `--foregroun
``` ```
$ amneziawg-go -f wg0 $ amneziawg-go -f wg0
``` ```
When an interface is running, you may use [`amneziawg-tools `](https://github.com/amnezia-vpn/amneziawg-tools) to configure it, as well as the usual `ip(8)` and `ifconfig(8)` commands. When an interface is running, you may use [`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.
To run with more logging you may set the environment variable `LOG_LEVEL=debug`. To run with more logging you may set the environment variable `LOG_LEVEL=debug`.
@@ -34,11 +34,11 @@ This will run on Linux; you should run amnezia-wg instead of using default linux
### macOS ### macOS
This runs on macOS using the utun driver. It does not yet support sticky sockets, and won't support fwmarks because of Darwin limitations. Since the utun driver cannot have arbitrary interface names, you must either use `utun[0-9]+` for an explicit interface name or `utun` to have the kernel select one for you. If you choose `utun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable. This runs on macOS using the utun driver. It does not yet support sticky sockets, and won't support fwmarks because of Darwin limitations. Since the utun driver cannot have arbitrary interface names, you must either use `utun[0-9]+` for an explicit interface name or `utun` to have the kernel select one for you. If you choose `utun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
This runs on MacOS, you should use it from [amneziawg-apple](https://github.com/amnezia-vpn/amneziawg-apple) This runs on MacOS, you should use it from [awg-apple](https://github.com/amnezia-vpn/awg-apple)
### Windows ### Windows
This runs on Windows, you should use it from [amneziawg-windows](https://github.com/amnezia-vpn/amneziawg-windows), which uses this as a module. This runs on Windows, you should use it from [awg-windows](https://github.com/amnezia-vpn/awg-windows), which uses this as a module.
## Building ## Building
@@ -50,114 +50,3 @@ $ git clone https://github.com/amnezia-vpn/amneziawg-go
$ cd amneziawg-go $ cd amneziawg-go
$ make $ 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
+7 -2
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
@@ -298,6 +298,11 @@ func (s *StdNetBind) BatchSize() int {
return 1 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 { func (s *StdNetBind) Close() error {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@@ -331,7 +336,7 @@ type ErrUDPGSODisabled struct {
} }
func (e ErrUDPGSODisabled) Error() string { func (e ErrUDPGSODisabled) Error() string {
return fmt.Sprintf("disabled UDP GSO on %s, NIC(s) may not support checksum offload or peer MTU with protocol headers is greater than path MTU", e.onLaddr) return fmt.Sprintf("disabled UDP GSO on %s, NIC(s) may not support checksum offload", e.onLaddr)
} }
func (e ErrUDPGSODisabled) Unwrap() error { func (e ErrUDPGSODisabled) Unwrap() error {
+6 -20
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
@@ -17,7 +17,7 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"github.com/amnezia-vpn/amneziawg-go/v3/conn/winrio" "github.com/amnezia-vpn/amneziawg-go/conn/winrio"
) )
const ( const (
@@ -328,6 +328,10 @@ func (bind *WinRingBind) BatchSize() int {
return 1 return 1
} }
func (bind *WinRingBind) GetOffloadInfo() string {
return ""
}
func (bind *WinRingBind) SetMark(mark uint32) error { func (bind *WinRingBind) SetMark(mark uint32) error {
return nil return nil
} }
@@ -582,24 +586,6 @@ func (bind *WinRingBind) BindSocketToInterface6(interfaceIndex uint32, blackhole
return nil 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 { func bindSocketToInterface4(handle windows.Handle, interfaceIndex uint32) error {
const IP_UNICAST_IF = 31 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. */ /* MSDN says for IPv4 this needs to be in net byte order, so that it's like an IP address with leading zeros. */
+4 -2
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package bindtest package bindtest
@@ -12,7 +12,7 @@ import (
"net/netip" "net/netip"
"os" "os"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
) )
type ChannelBind struct { type ChannelBind struct {
@@ -91,6 +91,8 @@ func (c *ChannelBind) Close() error {
func (c *ChannelBind) BatchSize() int { return 1 } 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) SetMark(mark uint32) error { return nil }
func (c *ChannelBind) makeReceiveFunc(ch chan []byte) conn.ReceiveFunc { func (c *ChannelBind) makeReceiveFunc(ch chan []byte) conn.ReceiveFunc {
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+3 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
// Package conn implements WireGuard's network connections. // Package conn implements WireGuard's network connections.
@@ -55,6 +55,8 @@ type Bind interface {
// BatchSize is the number of buffers expected to be passed to // BatchSize is the number of buffers expected to be passed to
// the ReceiveFuncs, and the maximum expected to be passed to SendBatch. // the ReceiveFuncs, and the maximum expected to be passed to SendBatch.
BatchSize() int BatchSize() int
GetOffloadInfo() string
} }
// BindSocketToInterface is implemented by Bind objects that support being // BindSocketToInterface is implemented by Bind objects that support being
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -49
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
@@ -13,35 +13,6 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
// Taken from go/src/internal/syscall/unix/kernel_version_linux.go
func kernelVersion() (major, minor int) {
var uname unix.Utsname
if err := unix.Uname(&uname); err != nil {
return
}
var (
values [2]int
value, vi int
)
for _, c := range uname.Release {
if '0' <= c && c <= '9' {
value = (value * 10) + int(c-'0')
} else {
// Note that we're assuming N.N.N here.
// If we see anything else, we are likely to mis-parse it.
values[vi] = value
vi++
if vi >= len(values) {
break
}
value = 0
}
}
return values[0], values[1]
}
func init() { func init() {
controlFns = append(controlFns, controlFns = append(controlFns,
@@ -86,24 +57,5 @@ func init() {
} }
return err return err
}, },
// Attempt to enable UDP_GRO
func(network, address string, c syscall.RawConn) error {
// Kernels below 5.12 are missing 98184612aca0 ("net:
// udp: Add support for getsockopt(..., ..., UDP_GRO,
// ..., ...);"), which means we can't read this back
// later. We could pipe the return value through to
// the rest of the code, but UDP_GRO is kind of buggy
// anyway, so just gate this here.
major, minor := kernelVersion()
if major < 5 || (major == 5 && minor < 12) {
return nil
}
c.Control(func(fd uintptr) {
_ = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
})
return nil
},
) )
} }
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+2 -2
View File
@@ -2,11 +2,11 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
func errShouldDisableUDPGSO(_ error) bool { func errShouldDisableUDPGSO(err error) bool {
return false return false
} }
+2 -4
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
@@ -20,9 +20,7 @@ func errShouldDisableUDPGSO(err error) bool {
// See: // 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/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 // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/udp.c?h=v6.2&id=c9c3395d5e3dcc6daee66c6908354d47bf98cb0c#n942
// If gso_size + udp + ip headers > fragment size EINVAL is returned. return serr.Err == unix.EIO
// It occurs when the peer mtu + wg headers is greater than path mtu.
return serr.Err == unix.EIO || serr.Err == unix.EINVAL
} }
return false return false
} }
+2 -2
View File
@@ -3,13 +3,13 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
import "net" import "net"
func supportsUDPOffload(_ *net.UDPConn) (txOffload, rxOffload bool) { func supportsUDPOffload(conn *net.UDPConn) (txOffload, rxOffload bool) {
return return
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package conn package conn
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package winrio package winrio
+13 -36
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -223,11 +223,19 @@ func (table *AllowedIPs) EntriesForPeer(peer *Peer, cb func(prefix netip.Prefix)
} }
} }
func (node *trieEntry) remove() { func (table *AllowedIPs) RemoveByPeer(peer *Peer) {
table.mutex.Lock()
defer table.mutex.Unlock()
var next *list.Element
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
next = elem.Next()
node := elem.Value.(*trieEntry)
node.removeFromPeerEntries() node.removeFromPeerEntries()
node.peer = nil node.peer = nil
if node.child[0] != nil && node.child[1] != nil { if node.child[0] != nil && node.child[1] != nil {
return continue
} }
bit := 0 bit := 0
if node.child[0] == nil { if node.child[0] == nil {
@@ -240,12 +248,12 @@ func (node *trieEntry) remove() {
*node.parent.parentBit = child *node.parent.parentBit = child
if node.child[0] != nil || node.child[1] != nil || node.parent.parentBitType > 1 { if node.child[0] != nil || node.child[1] != nil || node.parent.parentBitType > 1 {
node.zeroizePointers() node.zeroizePointers()
return continue
} }
parent := (*trieEntry)(unsafe.Pointer(uintptr(unsafe.Pointer(node.parent.parentBit)) - unsafe.Offsetof(node.child) - unsafe.Sizeof(node.child[0])*uintptr(node.parent.parentBitType))) 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 { if parent.peer != nil {
node.zeroizePointers() node.zeroizePointers()
return continue
} }
child = parent.child[node.parent.parentBitType^1] child = parent.child[node.parent.parentBitType^1]
if child != nil { if child != nil {
@@ -255,37 +263,6 @@ func (node *trieEntry) remove() {
node.zeroizePointers() node.zeroizePointers()
parent.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()
var next *list.Element
for elem := peer.trieEntries.Front(); elem != nil; elem = next {
next = elem.Next()
elem.Value.(*trieEntry).remove()
}
} }
func (table *AllowedIPs) Insert(prefix netip.Prefix, peer *Peer) { func (table *AllowedIPs) Insert(prefix netip.Prefix, peer *Peer) {
+6 -6
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -83,7 +83,7 @@ func TestTrieRandom(t *testing.T) {
var peers []*Peer var peers []*Peer
var allowedIPs AllowedIPs var allowedIPs AllowedIPs
rng := rand.New(rand.NewSource(1)) rand.Seed(1)
for n := 0; n < NumberOfPeers; n++ { for n := 0; n < NumberOfPeers; n++ {
peers = append(peers, &Peer{}) peers = append(peers, &Peer{})
@@ -91,14 +91,14 @@ func TestTrieRandom(t *testing.T) {
for n := 0; n < NumberOfAddresses; n++ { for n := 0; n < NumberOfAddresses; n++ {
var addr4 [4]byte var addr4 [4]byte
rng.Read(addr4[:]) rand.Read(addr4[:])
cidr := uint8(rand.Intn(32) + 1) cidr := uint8(rand.Intn(32) + 1)
index := rand.Intn(NumberOfPeers) index := rand.Intn(NumberOfPeers)
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4(addr4), int(cidr)), peers[index]) allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4(addr4), int(cidr)), peers[index])
slow4 = slow4.Insert(addr4[:], cidr, peers[index]) slow4 = slow4.Insert(addr4[:], cidr, peers[index])
var addr6 [16]byte var addr6 [16]byte
rng.Read(addr6[:]) rand.Read(addr6[:])
cidr = uint8(rand.Intn(128) + 1) cidr = uint8(rand.Intn(128) + 1)
index = rand.Intn(NumberOfPeers) index = rand.Intn(NumberOfPeers)
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(addr6), int(cidr)), peers[index]) allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(addr6), int(cidr)), peers[index])
@@ -109,7 +109,7 @@ func TestTrieRandom(t *testing.T) {
for p = 0; ; p++ { for p = 0; ; p++ {
for n := 0; n < NumberOfTests; n++ { for n := 0; n < NumberOfTests; n++ {
var addr4 [4]byte var addr4 [4]byte
rng.Read(addr4[:]) rand.Read(addr4[:])
peer1 := slow4.Lookup(addr4[:]) peer1 := slow4.Lookup(addr4[:])
peer2 := allowedIPs.Lookup(addr4[:]) peer2 := allowedIPs.Lookup(addr4[:])
if peer1 != peer2 { if peer1 != peer2 {
@@ -117,7 +117,7 @@ func TestTrieRandom(t *testing.T) {
} }
var addr6 [16]byte var addr6 [16]byte
rng.Read(addr6[:]) rand.Read(addr6[:])
peer1 = slow6.Lookup(addr6[:]) peer1 = slow6.Lookup(addr6[:])
peer2 = allowedIPs.Lookup(addr6[:]) peer2 = allowedIPs.Lookup(addr6[:])
if peer1 != peer2 { if peer1 != peer2 {
+7 -64
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -39,12 +39,12 @@ func TestCommonBits(t *testing.T) {
} }
} }
func benchmarkTrie(peerNumber, addressNumber, _ int, b *testing.B) { func benchmarkTrie(peerNumber, addressNumber, addressLength int, b *testing.B) {
var trie *trieEntry var trie *trieEntry
var peers []*Peer var peers []*Peer
root := parentIndirection{&trie, 2} root := parentIndirection{&trie, 2}
rng := rand.New(rand.NewSource(1)) rand.Seed(1)
const AddressLength = 4 const AddressLength = 4
@@ -54,15 +54,15 @@ func benchmarkTrie(peerNumber, addressNumber, _ int, b *testing.B) {
for n := 0; n < addressNumber; n++ { for n := 0; n < addressNumber; n++ {
var addr [AddressLength]byte var addr [AddressLength]byte
rng.Read(addr[:]) rand.Read(addr[:])
cidr := uint8(rng.Uint32() % (AddressLength * 8)) cidr := uint8(rand.Uint32() % (AddressLength * 8))
index := rng.Int() % peerNumber index := rand.Int() % peerNumber
root.insert(addr[:], cidr, peers[index]) root.insert(addr[:], cidr, peers[index])
} }
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
var addr [AddressLength]byte var addr [AddressLength]byte
rng.Read(addr[:]) rand.Read(addr[:])
trie.lookup(addr[:]) trie.lookup(addr[:])
} }
} }
@@ -101,10 +101,6 @@ func TestTrieIPv4(t *testing.T) {
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4([4]byte{a, b, c, d}), int(cidr)), peer) allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom4([4]byte{a, b, c, d}), int(cidr)), peer)
} }
remove := func(peer *Peer, a, b, c, d byte, cidr uint8) {
allowedIPs.Remove(netip.PrefixFrom(netip.AddrFrom4([4]byte{a, b, c, d}), int(cidr)), peer)
}
assertEQ := func(peer *Peer, a, b, c, d byte) { assertEQ := func(peer *Peer, a, b, c, d byte) {
p := allowedIPs.Lookup([]byte{a, b, c, d}) p := allowedIPs.Lookup([]byte{a, b, c, d})
if p != peer { if p != peer {
@@ -180,21 +176,6 @@ func TestTrieIPv4(t *testing.T) {
allowedIPs.RemoveByPeer(a) allowedIPs.RemoveByPeer(a)
assertNEQ(a, 192, 168, 0, 1) assertNEQ(a, 192, 168, 0, 1)
insert(a, 1, 0, 0, 0, 32)
insert(a, 192, 0, 0, 0, 24)
assertEQ(a, 1, 0, 0, 0)
assertEQ(a, 192, 0, 0, 1)
remove(a, 192, 0, 0, 0, 32)
assertEQ(a, 192, 0, 0, 1)
remove(nil, 192, 0, 0, 0, 24)
assertEQ(a, 192, 0, 0, 1)
remove(b, 192, 0, 0, 0, 24)
assertEQ(a, 192, 0, 0, 1)
remove(a, 192, 0, 0, 0, 24)
assertNEQ(a, 192, 0, 0, 1)
remove(a, 1, 0, 0, 0, 32)
assertNEQ(a, 1, 0, 0, 0)
} }
/* Test ported from kernel implementation: /* Test ported from kernel implementation:
@@ -230,15 +211,6 @@ func TestTrieIPv6(t *testing.T) {
allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(*(*[16]byte)(addr)), int(cidr)), peer) allowedIPs.Insert(netip.PrefixFrom(netip.AddrFrom16(*(*[16]byte)(addr)), int(cidr)), peer)
} }
remove := func(peer *Peer, a, b, c, d uint32, cidr uint8) {
var addr []byte
addr = append(addr, expand(a)...)
addr = append(addr, expand(b)...)
addr = append(addr, expand(c)...)
addr = append(addr, expand(d)...)
allowedIPs.Remove(netip.PrefixFrom(netip.AddrFrom16(*(*[16]byte)(addr)), int(cidr)), peer)
}
assertEQ := func(peer *Peer, a, b, c, d uint32) { assertEQ := func(peer *Peer, a, b, c, d uint32) {
var addr []byte var addr []byte
addr = append(addr, expand(a)...) addr = append(addr, expand(a)...)
@@ -251,18 +223,6 @@ func TestTrieIPv6(t *testing.T) {
} }
} }
assertNEQ := func(peer *Peer, a, b, c, d uint32) {
var addr []byte
addr = append(addr, expand(a)...)
addr = append(addr, expand(b)...)
addr = append(addr, expand(c)...)
addr = append(addr, expand(d)...)
p := allowedIPs.Lookup(addr)
if p == peer {
t.Error("Assert NEQ failed")
}
}
insert(d, 0x26075300, 0x60006b00, 0, 0xc05f0543, 128) insert(d, 0x26075300, 0x60006b00, 0, 0xc05f0543, 128)
insert(c, 0x26075300, 0x60006b00, 0, 0, 64) insert(c, 0x26075300, 0x60006b00, 0, 0, 64)
insert(e, 0, 0, 0, 0, 0) insert(e, 0, 0, 0, 0, 0)
@@ -284,21 +244,4 @@ func TestTrieIPv6(t *testing.T) {
assertEQ(h, 0x24046800, 0x40040800, 0, 0) assertEQ(h, 0x24046800, 0x40040800, 0, 0)
assertEQ(h, 0x24046800, 0x40040800, 0x10101010, 0x10101010) assertEQ(h, 0x24046800, 0x40040800, 0x10101010, 0x10101010)
assertEQ(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef) assertEQ(a, 0x24046800, 0x40040800, 0xdeadbeef, 0xdeadbeef)
insert(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
insert(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
assertEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
remove(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 96)
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
remove(nil, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
remove(b, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
assertEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
remove(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef, 128)
assertNEQ(a, 0x24446801, 0x40e40800, 0xdeaebeef, 0xdefbeef)
remove(b, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
assertEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
remove(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0, 98)
assertNEQ(a, 0x24446800, 0xf0e40800, 0xeeaebeef, 0x10101010)
} }
+2 -2
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -8,7 +8,7 @@ package device
import ( import (
"errors" "errors"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
) )
type DummyDatagram struct { type DummyDatagram struct {
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -2
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -24,7 +24,6 @@ const (
CookieRefreshTime = time.Second * 120 CookieRefreshTime = time.Second * 120
HandshakeInitationRate = time.Second / 50 HandshakeInitationRate = time.Second / 50
PaddingMultiple = 16 PaddingMultiple = 16
DefaultUdpWindow = 500
) )
const ( const (
+2 -3
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -118,7 +118,6 @@ func (st *CookieChecker) CreateReply(
msg []byte, msg []byte,
recv uint32, recv uint32,
src []byte, src []byte,
msgType uint32,
) (*MessageCookieReply, error) { ) (*MessageCookieReply, error) {
st.RLock() st.RLock()
@@ -154,7 +153,7 @@ func (st *CookieChecker) CreateReply(
smac1 := smac2 - blake2s.Size128 smac1 := smac2 - blake2s.Size128
reply := new(MessageCookieReply) reply := new(MessageCookieReply)
reply.Type = msgType reply.Type = MessageCookieReplyType
reply.Receiver = recv reply.Receiver = recv
_, err := rand.Read(reply.Nonce[:]) _, err := rand.Read(reply.Nonce[:])
+2 -2
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -99,7 +99,7 @@ func TestCookieMAC1(t *testing.T) {
0x8c, 0xe1, 0xe8, 0xfa, 0x67, 0x20, 0x80, 0x6d, 0x8c, 0xe1, 0xe8, 0xfa, 0x67, 0x20, 0x80, 0x6d,
} }
generator.AddMacs(msg) generator.AddMacs(msg)
reply, err := checker.CreateReply(msg, 1377, src, MessageCookieReplyType) reply, err := checker.CreateReply(msg, 1377, src)
if err != nil { if err != nil {
t.Fatal("Failed to create cookie reply:", err) t.Fatal("Failed to create cookie reply:", err)
} }
+331 -57
View File
@@ -1,20 +1,25 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
import ( import (
"context"
"fmt"
"runtime" "runtime"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/v3/ratelimiter" "github.com/amnezia-vpn/amneziawg-go/ipc"
"github.com/amnezia-vpn/amneziawg-go/v3/rwcancel" "github.com/amnezia-vpn/amneziawg-go/ratelimiter"
"github.com/amnezia-vpn/amneziawg-go/v3/tun" "github.com/amnezia-vpn/amneziawg-go/rwcancel"
"github.com/amnezia-vpn/amneziawg-go/tun"
"github.com/leninalive/udptlspipe/pipe"
"github.com/tevino/abool/v2"
) )
type Device struct { type Device struct {
@@ -90,45 +95,25 @@ type Device struct {
closed chan struct{} closed chan struct{}
log *Logger log *Logger
junk struct { isASecOn abool.AtomicBool
min atomic.Uint32 aSecMux sync.RWMutex
max atomic.Uint32 aSecCfg aSecCfgType
count atomic.Uint32
udptlspipe *pipe.Server
serverMode bool
} }
headers struct { type aSecCfgType struct {
init AtomicUintRange isSet bool
cookie AtomicUintRange junkPacketCount int
response AtomicUintRange junkPacketMinSize int
transport AtomicUintRange junkPacketMaxSize int
} initPacketJunkSize int
responsePacketJunkSize int
paddings struct { initPacketMagicHeader uint32
init atomic.Uint32 responsePacketMagicHeader uint32
response atomic.Uint32 underloadPacketMagicHeader uint32
cookie atomic.Uint32 transportPacketMagicHeader 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. // deviceState represents the state of a Device.
@@ -223,7 +208,7 @@ func (device *Device) upLocked() error {
device.peers.RLock() device.peers.RLock()
for _, peer := range device.peers.keyMap { for _, peer := range device.peers.keyMap {
peer.Start() peer.Start()
if !peer.persistentKeepaliveInterval.Load().IsZero() { if peer.persistentKeepaliveInterval.Load() > 0 {
peer.SendKeepalive() peer.SendKeepalive()
} }
} }
@@ -323,9 +308,8 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
} }
func NewDevice(tunDevice tun.Device, bind conn.Bind, logger *Logger) *Device { func NewDevice(tunDevice tun.Device, bind conn.Bind, logger *Logger) *Device {
var rang UintRange
device := new(Device) device := new(Device)
device.serverMode = false
device.state.state.Store(uint32(deviceStateDown)) device.state.state.Store(uint32(deviceStateDown))
device.closed = make(chan struct{}) device.closed = make(chan struct{})
device.log = logger device.log = logger
@@ -341,15 +325,6 @@ func NewDevice(tunDevice tun.Device, bind conn.Bind, logger *Logger) *Device {
device.rate.limiter.Init() device.rate.limiter.Init()
device.indexTable.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() device.PopulatePools()
// create queues // create queues
@@ -460,12 +435,10 @@ func (device *Device) SendKeepalivesToPeersWithCurrentKeypair() {
return return
} }
timeout := device.keychainExpireTime()
device.peers.RLock() device.peers.RLock()
for _, peer := range device.peers.keyMap { for _, peer := range device.peers.keyMap {
peer.keypairs.RLock() peer.keypairs.RLock()
sendKeepalive := peer.keypairs.current != nil && !peer.keypairs.current.created.Add(timeout).Before(time.Now()) sendKeepalive := peer.keypairs.current != nil && !peer.keypairs.current.created.Add(RejectAfterTime).Before(time.Now())
peer.keypairs.RUnlock() peer.keypairs.RUnlock()
if sendKeepalive { if sendKeepalive {
peer.SendKeepalive() peer.SendKeepalive()
@@ -478,6 +451,9 @@ func (device *Device) SendKeepalivesToPeersWithCurrentKeypair() {
// The caller must hold the net mutex. // The caller must hold the net mutex.
func closeBindLocked(device *Device) error { func closeBindLocked(device *Device) error {
var err error var err error
if device.udptlspipe != nil {
err = device.udptlspipe.Shutdown(context.TODO())
}
netc := &device.net netc := &device.net
if netc.netlinkCancel != nil { if netc.netlinkCancel != nil {
netc.netlinkCancel.Cancel() netc.netlinkCancel.Cancel()
@@ -579,6 +555,18 @@ func (device *Device) BindUpdate() error {
} }
device.log.Verbosef("UDP bind has been updated") device.log.Verbosef("UDP bind has been updated")
device.log.Verbosef(netc.bind.GetOffloadInfo())
if device.serverMode {
srv, err := device.StartUDPTLSPipe(true, "0.0.0.0:443", fmt.Sprintf("127.0.0.1:%d", netc.port))
if err != nil {
netc.bind.Close()
return err
}
device.udptlspipe = srv
}
return nil return nil
} }
@@ -588,3 +576,289 @@ func (device *Device) BindClose() error {
device.net.Unlock() device.net.Unlock()
return err 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
}
func (device *Device) StartUDPTLSPipe(isServer bool, localAddr string, destAddr string) (*pipe.Server, error) {
// setup UDP TLS pipe server
cfg := &pipe.Config{
ListenAddr: localAddr,
DestinationAddr: destAddr,
Password: "amn3zias3curep4ssword",
ServerMode: isServer,
//ProxyURL: o.ProxyURL,
//VerifyCertificate: o.VerifyCertificate,
//TLSServerName: o.TLSServerName,
//ProbeReverseProxyURL: o.ProbeReverseProxyURL,
}
/*if o.TLSCertPath != "" {
if !o.ServerMode {
log.Error("TLS certificate only works in server mode")
os.Exit(1)
}
cert, certErr := loadX509KeyPair(o.TLSCertPath, o.TLSCertKey)
if certErr != nil {
log.Error("Failed to load TLS certificate: %v", err)
os.Exit(1)
}
cfg.TLSCertificate = cert
}*/
srv, err := pipe.NewServer(cfg)
if err != nil {
device.log.Errorf("Failed to initialize TLS server: %v", err)
return nil, err
}
err = srv.Start()
if err != nil {
device.log.Errorf("Failed to start the TLS server: %v", err)
return nil, err
}
return srv, err
}
+84 -91
View File
@@ -1,32 +1,29 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
import ( import (
"bytes" "bytes"
"context"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io" "io"
"math/rand" "math/rand"
"net/netip" "net/netip"
"os" "os"
"os/signal"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
"go.uber.org/atomic" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/conn/bindtest"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/tun"
"github.com/amnezia-vpn/amneziawg-go/v3/conn/bindtest" "github.com/amnezia-vpn/amneziawg-go/tun/tuntest"
"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. // uapiCfg returns a string that contains cfg formatted use with IpcSet.
@@ -53,7 +50,7 @@ func uapiCfg(cfg ...string) string {
// genConfigs generates a pair of configs that connect to each other. // genConfigs generates a pair of configs that connect to each other.
// The configs use distinct, probably-usable ports. // The configs use distinct, probably-usable ports.
func genConfigs(tb testing.TB, cfg ...string) (cfgs, endpointCfgs [2]string) { func genConfigs(tb testing.TB) (cfgs, endpointCfgs [2]string) {
var key1, key2 NoisePrivateKey var key1, key2 NoisePrivateKey
_, err := rand.Read(key1[:]) _, err := rand.Read(key1[:])
if err != nil { if err != nil {
@@ -65,8 +62,7 @@ func genConfigs(tb testing.TB, cfg ...string) (cfgs, endpointCfgs [2]string) {
} }
pub1, pub2 := key1.publicKey(), key2.publicKey() pub1, pub2 := key1.publicKey(), key2.publicKey()
args0 := append([]string(nil), cfg...) cfgs[0] = uapiCfg(
args0 = append(args0, []string{
"private_key", hex.EncodeToString(key1[:]), "private_key", hex.EncodeToString(key1[:]),
"listen_port", "0", "listen_port", "0",
"replace_peers", "true", "replace_peers", "true",
@@ -74,16 +70,12 @@ func genConfigs(tb testing.TB, cfg ...string) (cfgs, endpointCfgs [2]string) {
"protocol_version", "1", "protocol_version", "1",
"replace_allowed_ips", "true", "replace_allowed_ips", "true",
"allowed_ip", "1.0.0.2/32", "allowed_ip", "1.0.0.2/32",
}...) )
cfgs[0] = uapiCfg(args0...)
endpointCfgs[0] = uapiCfg( endpointCfgs[0] = uapiCfg(
"public_key", hex.EncodeToString(pub2[:]), "public_key", hex.EncodeToString(pub2[:]),
"endpoint", "127.0.0.1:%d", "endpoint", "127.0.0.1:%d",
) )
cfgs[1] = uapiCfg(
args1 := append([]string(nil), cfg...)
args1 = append(args1, []string{
"private_key", hex.EncodeToString(key2[:]), "private_key", hex.EncodeToString(key2[:]),
"listen_port", "0", "listen_port", "0",
"replace_peers", "true", "replace_peers", "true",
@@ -91,9 +83,66 @@ func genConfigs(tb testing.TB, cfg ...string) (cfgs, endpointCfgs [2]string) {
"protocol_version", "1", "protocol_version", "1",
"replace_allowed_ips", "true", "replace_allowed_ips", "true",
"allowed_ip", "1.0.0.1/32", "allowed_ip", "1.0.0.1/32",
}...) )
endpointCfgs[1] = uapiCfg(
"public_key", hex.EncodeToString(pub1[:]),
"endpoint", "127.0.0.1:%d",
)
return
}
cfgs[1] = uapiCfg(args1...) 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",
)
endpointCfgs[1] = uapiCfg( endpointCfgs[1] = uapiCfg(
"public_key", hex.EncodeToString(pub1[:]), "public_key", hex.EncodeToString(pub1[:]),
"endpoint", "127.0.0.1:%d", "endpoint", "127.0.0.1:%d",
@@ -136,10 +185,9 @@ func (pair *testPair) Send(
// pong is the new ping // pong is the new ping
p0, p1 = p1, p0 p0, p1 = p1, p0
} }
msg := tuntest.Ping(p0.ip, p1.ip) msg := tuntest.Ping(p0.ip, p1.ip)
p1.tun.Outbound <- msg p1.tun.Outbound <- msg
timer := time.NewTimer(6 * time.Second) timer := time.NewTimer(5 * time.Second)
defer timer.Stop() defer timer.Stop()
var err error var err error
select { select {
@@ -166,12 +214,14 @@ func (pair *testPair) Send(
// genTestPair creates a testPair. // genTestPair creates a testPair.
func genTestPair( func genTestPair(
tb testing.TB, tb testing.TB,
realSocket bool, realSocket, withASecurity bool,
extraCfg ...string,
) (pair testPair) { ) (pair testPair) {
var cfg, endpointCfg [2]string var cfg, endpointCfg [2]string
cfg, endpointCfg = genConfigs(tb, extraCfg...) if withASecurity {
cfg, endpointCfg = genASecurityConfigs(tb)
} else {
cfg, endpointCfg = genConfigs(tb)
}
var binds [2]conn.Bind var binds [2]conn.Bind
if realSocket { if realSocket {
binds[0], binds[1] = conn.NewDefaultBind(), conn.NewDefaultBind() binds[0], binds[1] = conn.NewDefaultBind(), conn.NewDefaultBind()
@@ -215,7 +265,7 @@ func genTestPair(
func TestTwoDevicePing(t *testing.T) { func TestTwoDevicePing(t *testing.T) {
goroutineLeakCheck(t) goroutineLeakCheck(t)
pair := genTestPair(t, true) pair := genTestPair(t, true, false)
t.Run("ping 1.0.0.1", func(t *testing.T) { t.Run("ping 1.0.0.1", func(t *testing.T) {
pair.Send(t, Ping, nil) pair.Send(t, Ping, nil)
}) })
@@ -224,23 +274,9 @@ func TestTwoDevicePing(t *testing.T) {
}) })
} }
// Run test with -race=false to avoid the race for setting the default msgTypes 2 times func TestTwoDevicePingASecurity(t *testing.T) {
func TestAWGDevicePing(t *testing.T) {
goroutineLeakCheck(t) goroutineLeakCheck(t)
pair := genTestPair(t, true, true)
pair := genTestPair(t, true,
"jc", "5",
"jmin", "500",
"jmax", "1000",
"s1", "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) { t.Run("ping 1.0.0.1", func(t *testing.T) {
pair.Send(t, Ping, nil) pair.Send(t, Ping, nil)
}) })
@@ -249,56 +285,13 @@ func TestAWGDevicePing(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) { func TestUpDown(t *testing.T) {
goroutineLeakCheck(t) goroutineLeakCheck(t)
const itrials = 50 const itrials = 50
const otrials = 10 const otrials = 10
for n := 0; n < otrials; n++ { for n := 0; n < otrials; n++ {
pair := genTestPair(t, false) pair := genTestPair(t, false, false)
for i := range pair { for i := range pair {
for k := range pair[i].dev.peers.keyMap { for k := range pair[i].dev.peers.keyMap {
pair[i].dev.IpcSet(fmt.Sprintf("public_key=%s\npersistent_keepalive_interval=1\n", hex.EncodeToString(k[:]))) pair[i].dev.IpcSet(fmt.Sprintf("public_key=%s\npersistent_keepalive_interval=1\n", hex.EncodeToString(k[:])))
@@ -332,7 +325,7 @@ func TestUpDown(t *testing.T) {
// TestConcurrencySafety does other things concurrently with tunnel use. // TestConcurrencySafety does other things concurrently with tunnel use.
// It is intended to be used with the race detector to catch data races. // It is intended to be used with the race detector to catch data races.
func TestConcurrencySafety(t *testing.T) { func TestConcurrencySafety(t *testing.T) {
pair := genTestPair(t, true) pair := genTestPair(t, true, false)
done := make(chan struct{}) done := make(chan struct{})
const warmupIters = 10 const warmupIters = 10
@@ -413,7 +406,7 @@ func TestConcurrencySafety(t *testing.T) {
} }
func BenchmarkLatency(b *testing.B) { func BenchmarkLatency(b *testing.B) {
pair := genTestPair(b, true) pair := genTestPair(b, true, false)
// Establish a connection. // Establish a connection.
pair.Send(b, Ping, nil) pair.Send(b, Ping, nil)
@@ -427,7 +420,7 @@ func BenchmarkLatency(b *testing.B) {
} }
func BenchmarkThroughput(b *testing.B) { func BenchmarkThroughput(b *testing.B) {
pair := genTestPair(b, true) pair := genTestPair(b, true, false)
// Establish a connection. // Establish a connection.
pair.Send(b, Ping, nil) pair.Send(b, Ping, nil)
@@ -471,7 +464,7 @@ func BenchmarkThroughput(b *testing.B) {
} }
func BenchmarkUAPIGet(b *testing.B) { func BenchmarkUAPIGet(b *testing.B) {
pair := genTestPair(b, true) pair := genTestPair(b, true, false)
pair.Send(b, Ping, nil) pair.Send(b, Ping, nil)
pair.Send(b, Pong, nil) pair.Send(b, Pong, nil)
b.ReportAllocs() b.ReportAllocs()
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+2 -2
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -11,7 +11,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/amnezia-vpn/amneziawg-go/v3/replay" "github.com/amnezia-vpn/amneziawg-go/replay"
) )
/* Due to limitations in Go and /x/crypto there is currently /* Due to limitations in Go and /x/crypto there is currently
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+19 -36
View File
@@ -1,23 +1,21 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
import ( import (
"crypto/rand"
"errors" "errors"
"fmt" "fmt"
"sync" "sync"
"time" "time"
"golang.org/x/crypto/blake2s" "golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20"
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/poly1305" "golang.org/x/crypto/poly1305"
"github.com/amnezia-vpn/amneziawg-go/v3/tai64n" "github.com/amnezia-vpn/amneziawg-go/tai64n"
) )
type handshakeState int type handshakeState int
@@ -54,8 +52,7 @@ const (
WGLabelCookie = "cookie--" WGLabelCookie = "cookie--"
) )
const ( var (
MessageUnknownType uint32 = 0
MessageInitiationType uint32 = 1 MessageInitiationType uint32 = 1
MessageResponseType uint32 = 2 MessageResponseType uint32 = 2
MessageCookieReplyType uint32 = 3 MessageCookieReplyType uint32 = 3
@@ -78,6 +75,10 @@ const (
MessageTransportOffsetContent = 16 MessageTransportOffsetContent = 16
) )
var packetSizeToMsgType map[int]uint32
var msgTypeToJunkSize map[uint32]int
/* Type is an 8-bit field, followed by 3 nul bytes, /* Type is an 8-bit field, followed by 3 nul bytes,
* by marshalling the messages in little-endian byteorder * by marshalling the messages in little-endian byteorder
* we can treat these as a 32-bit unsigned int (for now) * we can treat these as a 32-bit unsigned int (for now)
@@ -196,12 +197,12 @@ func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, e
handshake.mixHash(handshake.remoteStatic[:]) handshake.mixHash(handshake.remoteStatic[:])
msgType := device.headers.init.Load().PickOne() device.aSecMux.RLock()
msg := MessageInitiation{ msg := MessageInitiation{
Type: msgType, Type: MessageInitiationType,
Ephemeral: handshake.localEphemeral.publicKey(), Ephemeral: handshake.localEphemeral.publicKey(),
} }
device.aSecMux.RUnlock()
handshake.mixKey(msg.Ephemeral[:]) handshake.mixKey(msg.Ephemeral[:])
handshake.mixHash(msg.Ephemeral[:]) handshake.mixHash(msg.Ephemeral[:])
@@ -255,9 +256,12 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
chainKey [blake2s.Size]byte chainKey [blake2s.Size]byte
) )
device.aSecMux.RLock()
if msg.Type != MessageInitiationType { if msg.Type != MessageInitiationType {
device.aSecMux.RUnlock()
return nil return nil
} }
device.aSecMux.RUnlock()
device.staticIdentity.RLock() device.staticIdentity.RLock()
defer device.staticIdentity.RUnlock() defer device.staticIdentity.RUnlock()
@@ -372,7 +376,9 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error
} }
var msg MessageResponse var msg MessageResponse
msg.Type = device.headers.response.Load().PickOne() device.aSecMux.RLock()
msg.Type = MessageResponseType
device.aSecMux.RUnlock()
msg.Sender = handshake.localIndex msg.Sender = handshake.localIndex
msg.Receiver = handshake.remoteIndex msg.Receiver = handshake.remoteIndex
@@ -422,9 +428,12 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error
} }
func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer { func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
device.aSecMux.RLock()
if msg.Type != MessageResponseType { if msg.Type != MessageResponseType {
device.aSecMux.RUnlock()
return nil return nil
} }
device.aSecMux.RUnlock()
// lookup handshake by receiver // lookup handshake by receiver
@@ -628,29 +637,3 @@ func (peer *Peer) ReceivedWithKeypair(receivedKeypair *Keypair) bool {
keypairs.next.Store(nil) keypairs.next.Store(nil)
return true 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)
}
+1 -109
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -9,18 +9,12 @@ import (
"crypto/subtle" "crypto/subtle"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt"
"strconv"
"strings"
"sync/atomic"
) )
const ( const (
NoisePublicKeySize = 32 NoisePublicKeySize = 32
NoisePrivateKeySize = 32 NoisePrivateKeySize = 32
NoisePresharedKeySize = 32 NoisePresharedKeySize = 32
HeaderCipherKeySize = 32
HeaderCipherNonceSize = 12
) )
type ( type (
@@ -28,7 +22,6 @@ type (
NoisePrivateKey [NoisePrivateKeySize]byte NoisePrivateKey [NoisePrivateKeySize]byte
NoisePresharedKey [NoisePresharedKeySize]byte NoisePresharedKey [NoisePresharedKeySize]byte
NoiseNonce uint64 // padded to 12-bytes NoiseNonce uint64 // padded to 12-bytes
HeaderCipherKey [HeaderCipherKeySize]byte
) )
func loadExactHex(dst []byte, src string) error { func loadExactHex(dst []byte, src string) error {
@@ -83,104 +76,3 @@ func (key NoisePublicKey) Equals(tar NoisePublicKey) bool {
func (key *NoisePresharedKey) FromHex(src string) error { func (key *NoisePresharedKey) FromHex(src string) error {
return loadExactHex(key[:], src) 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)))
}
+3 -3
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -10,8 +10,8 @@ import (
"encoding/binary" "encoding/binary"
"testing" "testing"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/v3/tun/tuntest" "github.com/amnezia-vpn/amneziawg-go/tun/tuntest"
) )
func TestCurveWrappers(t *testing.T) { func TestCurveWrappers(t *testing.T) {
-143
View File
@@ -1,143 +0,0 @@
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
}
-47
View File
@@ -1,47 +0,0 @@
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
}
-25
View File
@@ -1,25 +0,0 @@
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
}
-38
View File
@@ -1,38 +0,0 @@
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
}
-29
View File
@@ -1,29 +0,0 @@
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)
}
-39
View File
@@ -1,39 +0,0 @@
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
}
-48
View File
@@ -1,48 +0,0 @@
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
}
-48
View File
@@ -1,48 +0,0 @@
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
}
-31
View File
@@ -1,31 +0,0 @@
package device
import (
"encoding/binary"
"time"
)
func newTimestampObf(_ string) (obf, error) {
return &timestampObf{}, 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
}
+30 -12
View File
@@ -1,18 +1,21 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
import ( import (
"container/list" "container/list"
"context"
"errors" "errors"
"fmt"
"net/netip"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
) )
type Peer struct { type Peer struct {
@@ -39,7 +42,6 @@ type Peer struct {
zeroKeyMaterial *Timer zeroKeyMaterial *Timer
persistentKeepalive *Timer persistentKeepalive *Timer
handshakeAttempts atomic.Uint32 handshakeAttempts atomic.Uint32
maxHandshakeAttempts atomic.Uint32
needAnotherKeepalive atomic.Bool needAnotherKeepalive atomic.Bool
sentLastMinuteHandshake atomic.Bool sentLastMinuteHandshake atomic.Bool
} }
@@ -56,8 +58,7 @@ type Peer struct {
cookieGenerator CookieGenerator cookieGenerator CookieGenerator
trieEntries list.List trieEntries list.List
persistentKeepaliveInterval AtomicUintRange persistentKeepaliveInterval atomic.Uint32
udpWindow atomic.Uint32
} }
func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) { func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
@@ -80,8 +81,6 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
// create peer // create peer
peer := new(Peer) peer := new(Peer)
peer.udpWindow.Store(DefaultUdpWindow)
peer.cookieGenerator.Init(pk) peer.cookieGenerator.Init(pk)
peer.device = device peer.device = device
peer.queue.outbound = newAutodrainingOutboundQueue(device) peer.queue.outbound = newAutodrainingOutboundQueue(device)
@@ -191,12 +190,34 @@ func (peer *Peer) Start() {
device := peer.device device := peer.device
device.log.Verbosef("%v - Starting", peer) device.log.Verbosef("%v - Starting", peer)
if !device.serverMode {
device.log.Verbosef("Starting udptlspipe")
if device.udptlspipe != nil {
device.udptlspipe.Shutdown(context.TODO())
}
oldEndpoint, _ := netip.ParseAddrPort(peer.endpoint.val.DstToString())
peer.endpoint.val, _ = device.net.bind.ParseEndpoint(fmt.Sprintf("127.0.0.1:%d", oldEndpoint.Port()))
srv, err := device.StartUDPTLSPipe(
false,
fmt.Sprintf("127.0.0.1:%d", oldEndpoint.Port()),
fmt.Sprintf("%s:443", oldEndpoint.Addr().String()),
)
if err != nil {
return
}
device.udptlspipe = srv
}
// reset routine state // reset routine state
peer.stopping.Wait() peer.stopping.Wait()
peer.stopping.Add(2) peer.stopping.Add(2)
peer.handshake.mutex.Lock() peer.handshake.mutex.Lock()
peer.handshake.lastSentHandshake = time.Now().Add(-(peer.device.rekeyMinTimeout() + time.Second)) peer.handshake.lastSentHandshake = time.Now().Add(-(RekeyTimeout + time.Second))
peer.handshake.mutex.Unlock() peer.handshake.mutex.Unlock()
peer.device.queue.encryption.wg.Add(1) // keep encryption queue open for our writes peer.device.queue.encryption.wg.Add(1) // keep encryption queue open for our writes
@@ -246,7 +267,7 @@ func (peer *Peer) ExpireCurrentKeypairs() {
handshake.mutex.Lock() handshake.mutex.Lock()
peer.device.indexTable.Delete(handshake.localIndex) peer.device.indexTable.Delete(handshake.localIndex)
handshake.Clear() handshake.Clear()
peer.handshake.lastSentHandshake = time.Now().Add(-(peer.device.rekeyMinTimeout() + time.Second)) peer.handshake.lastSentHandshake = time.Now().Add(-(RekeyTimeout + time.Second))
handshake.mutex.Unlock() handshake.mutex.Unlock()
keypairs := &peer.keypairs keypairs := &peer.keypairs
@@ -286,9 +307,6 @@ func (peer *Peer) SetEndpointFromPacket(endpoint conn.Endpoint) {
if peer.endpoint.disableRoaming { if peer.endpoint.disableRoaming {
return return
} }
if peer.endpoint.val != endpoint {
peer.udpWindow.Store(DefaultUdpWindow)
}
peer.endpoint.clearSrcOnTx = false peer.endpoint.clearSrcOnTx = false
peer.endpoint.val = endpoint peer.endpoint.val = endpoint
} }
+6 -7
View File
@@ -1,19 +1,20 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
import ( import (
"sync" "sync"
"sync/atomic"
) )
type WaitPool struct { type WaitPool struct {
pool sync.Pool pool sync.Pool
cond sync.Cond cond sync.Cond
lock sync.Mutex lock sync.Mutex
count uint32 // Get calls not yet Put back count atomic.Uint32
max uint32 max uint32
} }
@@ -26,10 +27,10 @@ func NewWaitPool(max uint32, new func() any) *WaitPool {
func (p *WaitPool) Get() any { func (p *WaitPool) Get() any {
if p.max != 0 { if p.max != 0 {
p.lock.Lock() p.lock.Lock()
for p.count >= p.max { for p.count.Load() >= p.max {
p.cond.Wait() p.cond.Wait()
} }
p.count++ p.count.Add(1)
p.lock.Unlock() p.lock.Unlock()
} }
return p.pool.Get() return p.pool.Get()
@@ -40,9 +41,7 @@ func (p *WaitPool) Put(x any) {
if p.max == 0 { if p.max == 0 {
return return
} }
p.lock.Lock() p.count.Add(^uint32(0))
defer p.lock.Unlock()
p.count--
p.cond.Signal() p.cond.Signal()
} }
+2 -4
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -32,9 +32,7 @@ func TestWaitPool(t *testing.T) {
wg.Add(workers) wg.Add(workers)
var max atomic.Uint32 var max atomic.Uint32
updateMax := func() { updateMax := func() {
p.lock.Lock() count := p.count.Load()
count := p.count
p.lock.Unlock()
if count > p.max { if count > p.max {
t.Errorf("count (%d) > max (%d)", count, p.max) t.Errorf("count (%d) > max (%d)", count, p.max)
} }
+2 -2
View File
@@ -1,11 +1,11 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
import "github.com/amnezia-vpn/amneziawg-go/v3/conn" import "github.com/amnezia-vpn/amneziawg-go/conn"
/* Reduce memory consumption for Android */ /* Reduce memory consumption for Android */
+2 -2
View File
@@ -2,12 +2,12 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
import "github.com/amnezia-vpn/amneziawg-go/v3/conn" import "github.com/amnezia-vpn/amneziawg-go/conn"
const ( const (
QueueStagedSize = conn.IdealBatchSize QueueStagedSize = conn.IdealBatchSize
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
+38 -120
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -13,7 +13,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
@@ -32,7 +32,6 @@ type QueueInboundElement struct {
counter uint64 counter uint64
keypair *Keypair keypair *Keypair
endpoint conn.Endpoint endpoint conn.Endpoint
padding uint32
} }
type QueueInboundElementsContainer struct { type QueueInboundElementsContainer struct {
@@ -60,8 +59,7 @@ func (peer *Peer) keepKeyFreshReceiving() {
return return
} }
keypair := peer.keypairs.Current() 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.timers.sentLastMinuteHandshake.Store(true)
peer.SendHandshakeInitiation(false) peer.SendHandshakeInitiation(false)
} }
@@ -97,16 +95,15 @@ func (device *Device) RoutineReceiveIncoming(
endpoints = make([]conn.Endpoint, maxBatchSize) endpoints = make([]conn.Endpoint, maxBatchSize)
deathSpiral int deathSpiral int
elemsByPeer = make(map[*Peer]*QueueInboundElementsContainer, maxBatchSize) elemsByPeer = make(map[*Peer]*QueueInboundElementsContainer, maxBatchSize)
typeHashBuf [4]byte
) )
for i := range maxBatchSize { for i := range bufsArrs {
bufsArrs[i] = device.GetMessageBuffer() bufsArrs[i] = device.GetMessageBuffer()
bufs[i] = bufsArrs[i][:] bufs[i] = bufsArrs[i][:]
} }
defer func() { defer func() {
for i := range maxBatchSize { for i := 0; i < maxBatchSize; i++ {
if bufsArrs[i] != nil { if bufsArrs[i] != nil {
device.PutMessageBuffer(bufsArrs[i]) device.PutMessageBuffer(bufsArrs[i])
} }
@@ -132,6 +129,7 @@ func (device *Device) RoutineReceiveIncoming(
} }
deathSpiral = 0 deathSpiral = 0
device.aSecMux.RLock()
// handle each packet in the batch // handle each packet in the batch
for i, size := range sizes[:count] { for i, size := range sizes[:count] {
if size < MinMessageSize { if size < MinMessageSize {
@@ -139,32 +137,31 @@ func (device *Device) RoutineReceiveIncoming(
} }
// check size of packet // check size of packet
packet := bufsArrs[i][:size]
cip, err := device.HeaderProtectionCipher(packet[:HeaderCipherNonceSize]) packet := bufsArrs[i][:size]
if err != nil { var msgType uint32
device.log.Errorf("Failed to initialize header cipher") 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 continue
} }
typeHash := typeHashBuf[:]
clear(typeHash)
if cip != nil {
cip.XORKeyStream(typeHash, typeHash)
} }
} else {
// get message padding and type based on information from S1-S4 and H1-H4 msgType = binary.LittleEndian.Uint32(packet[:4])
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 { switch msgType {
// check if transport // check if transport
@@ -176,9 +173,6 @@ func (device *Device) RoutineReceiveIncoming(
if len(packet) < MessageTransportSize { if len(packet) < MessageTransportSize {
continue continue
} }
if cip != nil {
cip.XORKeyStream(packet[4:MessageTransportHeaderSize], packet[4:MessageTransportHeaderSize])
}
// lookup key pair // lookup key pair
@@ -193,7 +187,7 @@ func (device *Device) RoutineReceiveIncoming(
// check keypair expiry // check keypair expiry
if keypair.created.Add(device.keychainExpireTime()).Before(time.Now()) { if keypair.created.Add(RejectAfterTime).Before(time.Now()) {
continue continue
} }
@@ -205,7 +199,6 @@ func (device *Device) RoutineReceiveIncoming(
elem.keypair = keypair elem.keypair = keypair
elem.endpoint = endpoints[i] elem.endpoint = endpoints[i]
elem.counter = 0 elem.counter = 0
elem.padding = padding
elemsForPeer, ok := elemsByPeer[peer] elemsForPeer, ok := elemsByPeer[peer]
if !ok { if !ok {
@@ -224,25 +217,16 @@ func (device *Device) RoutineReceiveIncoming(
if len(packet) != MessageInitiationSize { if len(packet) != MessageInitiationSize {
continue continue
} }
if cip != nil {
cip.XORKeyStream(packet[4:MessageInitiationSize], packet[4:MessageInitiationSize])
}
case MessageResponseType: case MessageResponseType:
if len(packet) != MessageResponseSize { if len(packet) != MessageResponseSize {
continue continue
} }
if cip != nil {
cip.XORKeyStream(packet[4:MessageResponseSize], packet[4:MessageResponseSize])
}
case MessageCookieReplyType: case MessageCookieReplyType:
if len(packet) != MessageCookieReplySize { if len(packet) != MessageCookieReplySize {
continue continue
} }
if cip != nil {
cip.XORKeyStream(packet[4:MessageCookieReplySize], packet[4:MessageCookieReplySize])
}
default: default:
device.log.Verbosef("Received message with unknown type") device.log.Verbosef("Received message with unknown type")
@@ -261,6 +245,7 @@ func (device *Device) RoutineReceiveIncoming(
default: default:
} }
} }
device.aSecMux.RUnlock()
for peer, elemsContainer := range elemsByPeer { for peer, elemsContainer := range elemsByPeer {
if peer.isRunning.Load() { if peer.isRunning.Load() {
peer.queue.inbound.c <- elemsContainer peer.queue.inbound.c <- elemsContainer
@@ -318,6 +303,9 @@ func (device *Device) RoutineHandshake(id int) {
device.log.Verbosef("Routine: handshake worker %d - started", id) device.log.Verbosef("Routine: handshake worker %d - started", id)
for elem := range device.queue.handshake.c { for elem := range device.queue.handshake.c {
device.aSecMux.RLock()
// handle cookie fields and ratelimiting // handle cookie fields and ratelimiting
switch elem.msgType { switch elem.msgType {
@@ -369,8 +357,7 @@ func (device *Device) RoutineHandshake(id int) {
// endpoints destination address is the source of the datagram // endpoints destination address is the source of the datagram
disableCookies := device.disableCookies.Load() if device.IsUnderLoad() {
if !disableCookies && device.IsUnderLoad() {
// verify MAC2 field // verify MAC2 field
@@ -404,9 +391,6 @@ func (device *Device) RoutineHandshake(id int) {
goto skip goto skip
} }
// have to reassign msgType for ranged msgType to work
msg.Type = elem.msgType
// consume initiation // consume initiation
peer := device.ConsumeMessageInitiation(&msg) peer := device.ConsumeMessageInitiation(&msg)
if peer == nil { if peer == nil {
@@ -439,9 +423,6 @@ func (device *Device) RoutineHandshake(id int) {
goto skip goto skip
} }
// have to reassign msgType for ranged msgType to work
msg.Type = elem.msgType
// consume response // consume response
peer := device.ConsumeMessageResponse(&msg) peer := device.ConsumeMessageResponse(&msg)
@@ -475,6 +456,7 @@ func (device *Device) RoutineHandshake(id int) {
peer.SendKeepalive() peer.SendKeepalive()
} }
skip: skip:
device.aSecMux.RUnlock()
device.PutMessageBuffer(elem.buffer) device.PutMessageBuffer(elem.buffer)
} }
} }
@@ -515,12 +497,7 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
} }
rxBytesLen += uint64(len(elem.packet) + MinMessageSize) rxBytesLen += uint64(len(elem.packet) + MinMessageSize)
udpWindow := elem.padding + MessageTransportHeaderSize + uint32(len(elem.packet)) if len(elem.packet) == 0 {
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) device.log.Verbosef("%v - Receiving keepalive packet", peer)
continue continue
} }
@@ -568,7 +545,10 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
continue continue
} }
bufs = append(bufs, elem.buffer[int(elem.padding):int(elem.padding)+MessageTransportHeaderSize+len(elem.packet)]) bufs = append(
bufs,
elem.buffer[:MessageTransportOffsetContent+len(elem.packet)],
)
} }
peer.rxBytes.Add(rxBytesLen) peer.rxBytes.Add(rxBytesLen)
@@ -595,65 +575,3 @@ func (peer *Peer) RoutineSequentialReceiver(maxBatchSize int) {
device.PutInboundElementsContainer(elemsContainer) 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
}
+102 -181
View File
@@ -1,23 +1,22 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
import ( import (
"bytes" "bytes"
"crypto/rand"
"encoding/binary" "encoding/binary"
"errors" "errors"
"math/rand"
"net" "net"
"os" "os"
"slices"
"sync" "sync"
"time" "time"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/v3/tun" "github.com/amnezia-vpn/amneziawg-go/tun"
"golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/chacha20poly1305"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
@@ -53,8 +52,6 @@ type QueueOutboundElement struct {
nonce uint64 // nonce for encryption nonce uint64 // nonce for encryption
keypair *Keypair // keypair for encryption keypair *Keypair // keypair for encryption
peer *Peer // related peer peer *Peer // related peer
padding uint32
isKeepalive bool
} }
type QueueOutboundElementsContainer struct { type QueueOutboundElementsContainer struct {
@@ -66,8 +63,6 @@ func (device *Device) NewOutboundElement() *QueueOutboundElement {
elem := device.GetOutboundElement() elem := device.GetOutboundElement()
elem.buffer = device.GetMessageBuffer() elem.buffer = device.GetMessageBuffer()
elem.nonce = 0 elem.nonce = 0
elem.padding = device.paddings.transport.Load()
elem.isKeepalive = false
// keypair and peer were cleared (if necessary) by clearPointers. // keypair and peer were cleared (if necessary) by clearPointers.
return elem return elem
} }
@@ -88,7 +83,6 @@ func (elem *QueueOutboundElement) clearPointers() {
func (peer *Peer) SendKeepalive() { func (peer *Peer) SendKeepalive() {
if len(peer.queue.staged) == 0 && peer.isRunning.Load() { if len(peer.queue.staged) == 0 && peer.isRunning.Load() {
elem := peer.device.NewOutboundElement() elem := peer.device.NewOutboundElement()
elem.isKeepalive = true
elemsContainer := peer.device.GetOutboundElementsContainer() elemsContainer := peer.device.GetOutboundElementsContainer()
elemsContainer.elems = append(elemsContainer.elems, elem) elemsContainer.elems = append(elemsContainer.elems, elem)
select { select {
@@ -106,20 +100,17 @@ func (peer *Peer) SendKeepalive() {
func (peer *Peer) SendHandshakeInitiation(isRetry bool) error { func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
if !isRetry { if !isRetry {
peer.timers.handshakeAttempts.Store(0) peer.timers.handshakeAttempts.Store(0)
peer.timers.maxHandshakeAttempts.Store(peer.device.maxHandshakeAttemps())
} }
timeout := peer.device.rekeyMinTimeout()
peer.handshake.mutex.RLock() peer.handshake.mutex.RLock()
if time.Since(peer.handshake.lastSentHandshake) < timeout { if time.Since(peer.handshake.lastSentHandshake) < RekeyTimeout {
peer.handshake.mutex.RUnlock() peer.handshake.mutex.RUnlock()
return nil return nil
} }
peer.handshake.mutex.RUnlock() peer.handshake.mutex.RUnlock()
peer.handshake.mutex.Lock() peer.handshake.mutex.Lock()
if time.Since(peer.handshake.lastSentHandshake) < timeout { if time.Since(peer.handshake.lastSentHandshake) < RekeyTimeout {
peer.handshake.mutex.Unlock() peer.handshake.mutex.Unlock()
return nil return nil
} }
@@ -133,47 +124,55 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
peer.device.log.Errorf("%v - Failed to create initiation message: %v", peer, err) peer.device.log.Errorf("%v - Failed to create initiation message: %v", peer, err)
return err return err
} }
var sendBuffer [][]byte 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()
for _, ipacket := range peer.device.ipackets { if err != nil {
if ipacket != nil { peer.device.log.Errorf("%v - %v", peer, err)
buf := make([]byte, ipacket.ObfuscatedLen(0)) return err
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
} }
} }
sendBuffer = append(sendBuffer, peer.device.JunkPackets()...) 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()
}
padding := int(peer.device.paddings.init.Load()) var buf [MessageInitiationSize]byte
trailerLen := max(peer.randomTrailer(padding+MessageInitiationSize), 0) writer := bytes.NewBuffer(buf[: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) binary.Write(writer, binary.LittleEndian, msg)
packet := writer.Bytes() packet := writer.Bytes()
peer.cookieGenerator.AddMacs(packet) peer.cookieGenerator.AddMacs(packet)
junkedHeader = append(junkedHeader, packet...)
peer.timersAnyAuthenticatedPacketTraversal() peer.timersAnyAuthenticatedPacketTraversal()
peer.timersAnyAuthenticatedPacketSent() peer.timersAnyAuthenticatedPacketSent()
cip, err := peer.device.HeaderProtectionCipher(crypt[:HeaderCipherNonceSize]) sendBuffer = append(sendBuffer, junkedHeader)
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) err = peer.SendBuffers(sendBuffer)
if err != nil { if err != nil {
peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err) peer.device.log.Errorf("%v - Failed to send handshake initiation: %v", peer, err)
@@ -195,19 +194,29 @@ func (peer *Peer) SendHandshakeResponse() error {
peer.device.log.Errorf("%v - Failed to create response message: %v", peer, err) peer.device.log.Errorf("%v - Failed to create response message: %v", peer, err)
return err return err
} }
var junkedHeader []byte
if peer.device.isAdvancedSecurityOn() {
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) binary.Write(writer, binary.LittleEndian, response)
packet := writer.Bytes() packet := writer.Bytes()
peer.cookieGenerator.AddMacs(packet) peer.cookieGenerator.AddMacs(packet)
junkedHeader = append(junkedHeader, packet...)
err = peer.BeginSymmetricSession() err = peer.BeginSymmetricSession()
if err != nil { if err != nil {
@@ -219,67 +228,35 @@ func (peer *Peer) SendHandshakeResponse() error {
peer.timersAnyAuthenticatedPacketTraversal() peer.timersAnyAuthenticatedPacketTraversal()
peer.timersAnyAuthenticatedPacketSent() 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 // TODO: allocation could be avoided
err = peer.SendBuffers([][]byte{buf}) err = peer.SendBuffers([][]byte{junkedHeader})
if err != nil { if err != nil {
peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err) peer.device.log.Errorf("%v - Failed to send handshake response: %v", peer, err)
} }
return 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()) device.log.Verbosef("Sending cookie response for denied handshake message for %v", initiatingElem.endpoint.DstToString())
sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8]) sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8])
msgType := device.headers.cookie.Load().PickOne()
reply, err := device.cookieChecker.CreateReply( reply, err := device.cookieChecker.CreateReply(
initiatingElem.packet, initiatingElem.packet,
sender, sender,
initiatingElem.endpoint.DstToBytes(), initiatingElem.endpoint.DstToBytes(),
msgType,
) )
if err != nil { if err != nil {
device.log.Errorf("Failed to create cookie reply: %v", err) device.log.Errorf("Failed to create cookie reply: %v", err)
return err return err
} }
padding := int(device.paddings.cookie.Load()) var buf [MessageCookieReplySize]byte
trailerLen := max(device.randomTrailer(padding+MessageCookieReplySize), 0) writer := bytes.NewBuffer(buf[: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) 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 // TODO: allocation could be avoided
device.net.bind.Send([][]byte{buf}, initiatingElem.endpoint) device.net.bind.Send([][]byte{writer.Bytes()}, initiatingElem.endpoint)
return nil return nil
} }
@@ -289,7 +266,7 @@ func (peer *Peer) keepKeyFreshSending() {
return return
} }
nonce := keypair.sendNonce.Load() nonce := keypair.sendNonce.Load()
if nonce > RekeyAfterMessages || (keypair.isInitiator && time.Since(keypair.created) > peer.device.keyRefreshTimeoutSending()) { if nonce > RekeyAfterMessages || (keypair.isInitiator && time.Since(keypair.created) > RekeyAfterTime) {
peer.SendHandshakeInitiation(false) peer.SendHandshakeInitiation(false)
} }
} }
@@ -311,6 +288,7 @@ func (device *Device) RoutineReadFromTUN() {
elemsByPeer = make(map[*Peer]*QueueOutboundElementsContainer, batchSize) elemsByPeer = make(map[*Peer]*QueueOutboundElementsContainer, batchSize)
count = 0 count = 0
sizes = make([]int, batchSize) sizes = make([]int, batchSize)
offset = MessageTransportHeaderSize
) )
for i := range elems { for i := range elems {
@@ -328,9 +306,6 @@ func (device *Device) RoutineReadFromTUN() {
}() }()
for { for {
padding := device.paddings.transport.Load()
offset := MessageTransportHeaderSize + int(padding)
// read packets // read packets
count, readErr = device.tun.device.Read(bufs, sizes, offset) count, readErr = device.tun.device.Read(bufs, sizes, offset)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
@@ -340,7 +315,6 @@ func (device *Device) RoutineReadFromTUN() {
elem := elems[i] elem := elems[i]
elem.packet = bufs[i][offset : offset+sizes[i]] elem.packet = bufs[i][offset : offset+sizes[i]]
elem.padding = padding
// lookup peer // lookup peer
var peer *Peer var peer *Peer
@@ -435,7 +409,7 @@ top:
} }
keypair := peer.keypairs.Current() keypair := peer.keypairs.Current()
if keypair == nil || keypair.sendNonce.Load() >= RejectAfterMessages || time.Since(keypair.created) >= peer.device.keychainExpireTime() { if keypair == nil || keypair.sendNonce.Load() >= RejectAfterMessages || time.Since(keypair.created) >= RejectAfterTime {
peer.SendHandshakeInitiation(false) peer.SendHandshakeInitiation(false)
return return
} }
@@ -495,6 +469,31 @@ top:
} }
} }
func (peer *Peer) createJunkPackets() ([][]byte, error) {
if peer.device.aSecCfg.junkPacketCount == 0 {
return nil, nil
}
junks := make([][]byte, 0, peer.device.aSecCfg.junkPacketCount)
for i := 0; i < peer.device.aSecCfg.junkPacketCount; i++ {
packetSize := rand.Intn(
peer.device.aSecCfg.junkPacketMaxSize-peer.device.aSecCfg.junkPacketMinSize,
) + peer.device.aSecCfg.junkPacketMinSize
junk, err := randomJunkWithSize(packetSize)
if err != nil {
peer.device.log.Errorf(
"%v - Failed to create junk packet: %v",
peer,
err,
)
return nil, err
}
junks = append(junks, junk)
}
return junks, nil
}
func (peer *Peer) FlushStagedPackets() { func (peer *Peer) FlushStagedPackets() {
for { for {
select { select {
@@ -525,55 +524,13 @@ func calculatePaddingSize(packetSize, mtu int) int {
return paddedSize - lastUnit 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 /* Encrypts the elements in the queue
* and marks them for sequential consumption (by releasing the mutex) * and marks them for sequential consumption (by releasing the mutex)
* *
* Obs. One instance per core * Obs. One instance per core
*/ */
func (device *Device) RoutineEncryption(id int) { func (device *Device) RoutineEncryption(id int) {
var paddingZeros [PaddingMultiple]byte
var nonce [chacha20poly1305.NonceSize]byte var nonce [chacha20poly1305.NonceSize]byte
defer device.log.Verbosef("Routine: encryption worker %d - stopped", id) defer device.log.Verbosef("Routine: encryption worker %d - stopped", id)
@@ -581,63 +538,30 @@ func (device *Device) RoutineEncryption(id int) {
for elemsContainer := range device.queue.encryption.c { for elemsContainer := range device.queue.encryption.c {
for _, elem := range elemsContainer.elems { 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 // populate header fields
header := elem.buffer[elem.padding : elem.padding+MessageTransportHeaderSize] header := elem.buffer[:MessageTransportHeaderSize]
fieldType := header[0:4] fieldType := header[0:4]
fieldReceiver := header[4:8] fieldReceiver := header[4:8]
fieldNonce := header[8:16] fieldNonce := header[8:16]
binary.LittleEndian.PutUint32(fieldType, device.headers.transport.Load().PickOne()) binary.LittleEndian.PutUint32(fieldType, MessageTransportType)
binary.LittleEndian.PutUint32(fieldReceiver, elem.keypair.remoteIndex) binary.LittleEndian.PutUint32(fieldReceiver, elem.keypair.remoteIndex)
binary.LittleEndian.PutUint64(fieldNonce, elem.nonce) binary.LittleEndian.PutUint64(fieldNonce, elem.nonce)
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 // pad content to multiple of 16
paddingSize = calculatePaddingSize(len(elem.packet), mtu) paddingSize := calculatePaddingSize(len(elem.packet), int(device.tun.mtu.Load()))
} elem.packet = append(elem.packet, paddingZeros[:paddingSize]...)
// 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 // encrypt content and release to consumer
binary.LittleEndian.PutUint64(nonce[4:], elem.nonce) binary.LittleEndian.PutUint64(nonce[4:], elem.nonce)
elem.packet = elem.keypair.send.Seal( elem.packet = elem.keypair.send.Seal(
elem.buffer[:elem.padding+MessageTransportHeaderSize], header,
nonce[:], nonce[:],
elem.packet, elem.packet,
nil, 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() elemsContainer.Unlock()
} }
@@ -670,16 +594,14 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
device.PutMessageBuffer(elem.buffer) device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem) device.PutOutboundElement(elem)
} }
device.PutOutboundElementsContainer(elemsContainer)
continue continue
} }
dataSent := false dataSent := false
elemsContainer.Lock() elemsContainer.Lock()
for _, elem := range elemsContainer.elems { for _, elem := range elemsContainer.elems {
if !elem.isKeepalive { if len(elem.packet) != MessageKeepaliveSize {
dataSent = true dataSent = true
} }
bufs = append(bufs, elem.packet) bufs = append(bufs, elem.packet)
} }
@@ -690,7 +612,6 @@ func (peer *Peer) RoutineSequentialSender(maxBatchSize int) {
if dataSent { if dataSent {
peer.timersDataSent() peer.timersDataSent()
} }
for _, elem := range elemsContainer.elems { for _, elem := range elemsContainer.elems {
device.PutMessageBuffer(elem.buffer) device.PutMessageBuffer(elem.buffer)
device.PutOutboundElement(elem) device.PutOutboundElement(elem)
+3 -3
View File
@@ -3,10 +3,10 @@
package device package device
import ( import (
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/v3/rwcancel" "github.com/amnezia-vpn/amneziawg-go/rwcancel"
) )
func (device *Device) startRouteListener(_ conn.Bind) (*rwcancel.RWCancel, error) { func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) {
return nil, nil return nil, nil
} }
+5 -5
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
* *
* This implements userspace semantics of "sticky sockets", modeled after * This implements userspace semantics of "sticky sockets", modeled after
* WireGuard's kernelspace implementation. This is more or less a straight port * WireGuard's kernelspace implementation. This is more or less a straight port
@@ -9,7 +9,7 @@
* *
* Currently there is no way to achieve this within the net package: * Currently there is no way to achieve this within the net package:
* See e.g. https://github.com/golang/go/issues/17930 * See e.g. https://github.com/golang/go/issues/17930
* So this code remains platform dependent. * So this code is remains platform dependent.
*/ */
package device package device
@@ -20,8 +20,8 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/v3/rwcancel" "github.com/amnezia-vpn/amneziawg-go/rwcancel"
) )
func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) { func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, error) {
@@ -47,7 +47,7 @@ func (device *Device) startRouteListener(bind conn.Bind) (*rwcancel.RWCancel, er
return netlinkCancel, nil return netlinkCancel, nil
} }
func (device *Device) routineRouteListener(_ conn.Bind, netlinkSock int, netlinkCancel *rwcancel.RWCancel) { func (device *Device) routineRouteListener(bind conn.Bind, netlinkSock int, netlinkCancel *rwcancel.RWCancel) {
type peerEndpointPtr struct { type peerEndpointPtr struct {
peer *Peer peer *Peer
endpoint *conn.Endpoint endpoint *conn.Endpoint
+28 -125
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
* *
* This is based heavily on timers.c from the kernel implementation. * This is based heavily on timers.c from the kernel implementation.
*/ */
@@ -22,25 +22,24 @@ type Timer struct {
*time.Timer *time.Timer
modifyingLock sync.RWMutex modifyingLock sync.RWMutex
runningLock sync.Mutex runningLock sync.Mutex
duration time.Duration isPending bool
} }
func (peer *Peer) NewTimer(expirationFunction func(*Peer, time.Duration)) *Timer { func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
timer := &Timer{} timer := &Timer{}
timer.Timer = time.AfterFunc(time.Hour, func() { timer.Timer = time.AfterFunc(time.Hour, func() {
timer.runningLock.Lock() timer.runningLock.Lock()
defer timer.runningLock.Unlock() defer timer.runningLock.Unlock()
timer.modifyingLock.Lock() timer.modifyingLock.Lock()
if timer.duration == 0 { if !timer.isPending {
timer.modifyingLock.Unlock() timer.modifyingLock.Unlock()
return return
} }
duration := timer.duration timer.isPending = false
timer.modifyingLock.Unlock() timer.modifyingLock.Unlock()
timer.duration = 0
expirationFunction(peer, duration) expirationFunction(peer)
}) })
timer.Stop() timer.Stop()
return timer return timer
@@ -48,14 +47,14 @@ func (peer *Peer) NewTimer(expirationFunction func(*Peer, time.Duration)) *Timer
func (timer *Timer) Mod(d time.Duration) { func (timer *Timer) Mod(d time.Duration) {
timer.modifyingLock.Lock() timer.modifyingLock.Lock()
timer.duration = d timer.isPending = true
timer.Reset(d) timer.Reset(d)
timer.modifyingLock.Unlock() timer.modifyingLock.Unlock()
} }
func (timer *Timer) Del() { func (timer *Timer) Del() {
timer.modifyingLock.Lock() timer.modifyingLock.Lock()
timer.duration = 0 timer.isPending = false
timer.Stop() timer.Stop()
timer.modifyingLock.Unlock() timer.modifyingLock.Unlock()
} }
@@ -70,18 +69,16 @@ func (timer *Timer) DelSync() {
func (timer *Timer) IsPending() bool { func (timer *Timer) IsPending() bool {
timer.modifyingLock.RLock() timer.modifyingLock.RLock()
defer timer.modifyingLock.RUnlock() defer timer.modifyingLock.RUnlock()
return timer.duration > 0 return timer.isPending
} }
func (peer *Peer) timersActive() bool { func (peer *Peer) timersActive() bool {
return peer.isRunning.Load() && peer.device != nil && peer.device.isUp() return peer.isRunning.Load() && peer.device != nil && peer.device.isUp()
} }
func expiredRetransmitHandshake(peer *Peer, d time.Duration) { func expiredRetransmitHandshake(peer *Peer) {
maxAttempts := peer.timers.maxHandshakeAttempts.Load() if peer.timers.handshakeAttempts.Load() > MaxTimerHandshakes {
peer.device.log.Verbosef("%s - Handshake did not complete after %d attempts, giving up", peer, MaxTimerHandshakes+2)
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() { if peer.timersActive() {
peer.timers.sendKeepalive.Del() peer.timers.sendKeepalive.Del()
@@ -96,11 +93,11 @@ func expiredRetransmitHandshake(peer *Peer, d time.Duration) {
* of a partial exchange. * of a partial exchange.
*/ */
if peer.timersActive() && !peer.timers.zeroKeyMaterial.IsPending() { if peer.timersActive() && !peer.timers.zeroKeyMaterial.IsPending() {
peer.timers.zeroKeyMaterial.Mod(peer.device.keychainExpireTime() * 3) peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3)
} }
} else { } else {
peer.timers.handshakeAttempts.Add(1) peer.timers.handshakeAttempts.Add(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) peer.device.log.Verbosef("%s - Handshake did not complete after %d seconds, retrying (try %d)", peer, int(RekeyTimeout.Seconds()), peer.timers.handshakeAttempts.Load()+1)
/* We clear the endpoint address src address, in case this is the cause of trouble. */ /* We clear the endpoint address src address, in case this is the cause of trouble. */
peer.markEndpointSrcForClearing() peer.markEndpointSrcForClearing()
@@ -109,30 +106,30 @@ func expiredRetransmitHandshake(peer *Peer, d time.Duration) {
} }
} }
func expiredSendKeepalive(peer *Peer, d time.Duration) { func expiredSendKeepalive(peer *Peer) {
peer.SendKeepalive() peer.SendKeepalive()
if peer.timers.needAnotherKeepalive.Load() { if peer.timers.needAnotherKeepalive.Load() {
peer.timers.needAnotherKeepalive.Store(false) peer.timers.needAnotherKeepalive.Store(false)
if peer.timersActive() { if peer.timersActive() {
peer.timers.sendKeepalive.Mod(peer.sendKeepaliveTimeout()) peer.timers.sendKeepalive.Mod(KeepaliveTimeout)
} }
} }
} }
func expiredNewHandshake(peer *Peer, d time.Duration) { func expiredNewHandshake(peer *Peer) {
peer.device.log.Verbosef("%s - Retrying handshake because we stopped hearing back after %d seconds", peer, int(d.Seconds())) peer.device.log.Verbosef("%s - Retrying handshake because we stopped hearing back after %d seconds", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
/* We clear the endpoint address src address, in case this is the cause of trouble. */ /* We clear the endpoint address src address, in case this is the cause of trouble. */
peer.markEndpointSrcForClearing() peer.markEndpointSrcForClearing()
peer.SendHandshakeInitiation(false) peer.SendHandshakeInitiation(false)
} }
func expiredZeroKeyMaterial(peer *Peer, d time.Duration) { 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(d.Seconds())) peer.device.log.Verbosef("%s - Removing all keys, since we haven't received a new one in %d seconds", peer, int((RejectAfterTime * 3).Seconds()))
peer.ZeroAndFlushAll() peer.ZeroAndFlushAll()
} }
func expiredPersistentKeepalive(peer *Peer, d time.Duration) { func expiredPersistentKeepalive(peer *Peer) {
if !peer.persistentKeepaliveInterval.Load().IsZero() { if peer.persistentKeepaliveInterval.Load() > 0 {
peer.SendKeepalive() peer.SendKeepalive()
} }
} }
@@ -140,7 +137,7 @@ func expiredPersistentKeepalive(peer *Peer, d time.Duration) {
/* Should be called after an authenticated data packet is sent. */ /* Should be called after an authenticated data packet is sent. */
func (peer *Peer) timersDataSent() { func (peer *Peer) timersDataSent() {
if peer.timersActive() && !peer.timers.newHandshake.IsPending() { if peer.timersActive() && !peer.timers.newHandshake.IsPending() {
peer.timers.newHandshake.Mod(peer.newHandshakeTimeout() + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs))) peer.timers.newHandshake.Mod(KeepaliveTimeout + RekeyTimeout + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs)))
} }
} }
@@ -148,7 +145,7 @@ func (peer *Peer) timersDataSent() {
func (peer *Peer) timersDataReceived() { func (peer *Peer) timersDataReceived() {
if peer.timersActive() { if peer.timersActive() {
if !peer.timers.sendKeepalive.IsPending() { if !peer.timers.sendKeepalive.IsPending() {
peer.timers.sendKeepalive.Mod(peer.sendKeepaliveTimeout()) peer.timers.sendKeepalive.Mod(KeepaliveTimeout)
} else { } else {
peer.timers.needAnotherKeepalive.Store(true) peer.timers.needAnotherKeepalive.Store(true)
} }
@@ -172,7 +169,7 @@ func (peer *Peer) timersAnyAuthenticatedPacketReceived() {
/* Should be called after a handshake initiation message is sent. */ /* Should be called after a handshake initiation message is sent. */
func (peer *Peer) timersHandshakeInitiated() { func (peer *Peer) timersHandshakeInitiated() {
if peer.timersActive() { if peer.timersActive() {
peer.timers.retransmitHandshake.Mod(peer.retransmitHandshakeTimeout() + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs))) peer.timers.retransmitHandshake.Mod(RekeyTimeout + time.Millisecond*time.Duration(fastrandn(RekeyTimeoutJitterMaxMs)))
} }
} }
@@ -182,7 +179,6 @@ func (peer *Peer) timersHandshakeComplete() {
peer.timers.retransmitHandshake.Del() peer.timers.retransmitHandshake.Del()
} }
peer.timers.handshakeAttempts.Store(0) peer.timers.handshakeAttempts.Store(0)
peer.timers.maxHandshakeAttempts.Store(peer.device.maxHandshakeAttemps())
peer.timers.sentLastMinuteHandshake.Store(false) peer.timers.sentLastMinuteHandshake.Store(false)
peer.lastHandshakeNano.Store(time.Now().UnixNano()) peer.lastHandshakeNano.Store(time.Now().UnixNano())
} }
@@ -190,15 +186,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. */ /* 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() { func (peer *Peer) timersSessionDerived() {
if peer.timersActive() { if peer.timersActive() {
peer.timers.zeroKeyMaterial.Mod(peer.device.keychainExpireTime() * 3) peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3)
} }
} }
/* Should be called before a packet with authentication -- keepalive, data, or handshake -- is sent, or after one is received. */ /* Should be called before a packet with authentication -- keepalive, data, or handshake -- is sent, or after one is received. */
func (peer *Peer) timersAnyAuthenticatedPacketTraversal() { func (peer *Peer) timersAnyAuthenticatedPacketTraversal() {
keepalive := peer.persistentKeepaliveInterval.Load() keepalive := peer.persistentKeepaliveInterval.Load()
if !keepalive.IsZero() && peer.timersActive() { if keepalive > 0 && peer.timersActive() {
peer.timers.persistentKeepalive.Mod(time.Duration(keepalive.PickOne()) * time.Second) peer.timers.persistentKeepalive.Mod(time.Duration(keepalive) * time.Second)
} }
} }
@@ -212,7 +208,6 @@ func (peer *Peer) timersInit() {
func (peer *Peer) timersStart() { func (peer *Peer) timersStart() {
peer.timers.handshakeAttempts.Store(0) peer.timers.handshakeAttempts.Store(0)
peer.timers.maxHandshakeAttempts.Store(peer.device.maxHandshakeAttemps())
peer.timers.sentLastMinuteHandshake.Store(false) peer.timers.sentLastMinuteHandshake.Store(false)
peer.timers.needAnotherKeepalive.Store(false) peer.timers.needAnotherKeepalive.Store(false)
} }
@@ -224,95 +219,3 @@ func (peer *Peer) timersStop() {
peer.timers.zeroKeyMaterial.DelSync() peer.timers.zeroKeyMaterial.DelSync()
peer.timers.persistentKeepalive.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
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -8,7 +8,7 @@ package device
import ( import (
"fmt" "fmt"
"github.com/amnezia-vpn/amneziawg-go/v3/tun" "github.com/amnezia-vpn/amneziawg-go/tun"
) )
const DefaultMTU = 1420 const DefaultMTU = 1420
+83 -376
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package device package device
@@ -18,7 +18,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/amnezia-vpn/amneziawg-go/v3/ipc" "github.com/amnezia-vpn/amneziawg-go/ipc"
) )
type IPCError struct { type IPCError struct {
@@ -70,18 +70,6 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
} }
buf.WriteByte('\n') 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() { func() {
// lock required resources // lock required resources
@@ -95,9 +83,6 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
device.peers.RLock() device.peers.RLock()
defer device.peers.RUnlock() defer device.peers.RUnlock()
device.headerProtection.RLock()
defer device.headerProtection.RUnlock()
// serialize device related values // serialize device related values
if !device.staticIdentity.privateKey.IsZero() { if !device.staticIdentity.privateKey.IsZero() {
@@ -112,82 +97,36 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
sendf("fwmark=%d", device.net.fwmark) sendf("fwmark=%d", device.net.fwmark)
} }
if count := device.junk.count.Load(); count != 0 { if device.isAdvancedSecurityOn() {
sendf("jc=%d", count) if device.aSecCfg.junkPacketCount != 0 {
sendf("jc=%d", device.aSecCfg.junkPacketCount)
} }
if device.aSecCfg.junkPacketMinSize != 0 {
if min := device.junk.min.Load(); min != 0 { sendf("jmin=%d", device.aSecCfg.junkPacketMinSize)
sendf("jmin=%d", min)
} }
if device.aSecCfg.junkPacketMaxSize != 0 {
if max := device.junk.max.Load(); max != 0 { sendf("jmax=%d", device.aSecCfg.junkPacketMaxSize)
sendf("jmax=%d", max)
} }
if device.aSecCfg.initPacketJunkSize != 0 {
if padding := device.paddings.init.Load(); padding != 0 { sendf("s1=%d", device.aSecCfg.initPacketJunkSize)
sendf("s1=%d", padding)
} }
if device.aSecCfg.responsePacketJunkSize != 0 {
if padding := device.paddings.response.Load(); padding != 0 { sendf("s2=%d", device.aSecCfg.responsePacketJunkSize)
sendf("s2=%d", padding)
} }
if device.aSecCfg.initPacketMagicHeader != 0 {
if padding := device.paddings.cookie.Load(); padding != 0 { sendf("h1=%d", device.aSecCfg.initPacketMagicHeader)
sendf("s3=%d", padding)
} }
if device.aSecCfg.responsePacketMagicHeader != 0 {
if padding := device.paddings.transport.Load(); padding != 0 { sendf("h2=%d", device.aSecCfg.responsePacketMagicHeader)
sendf("s4=%d", padding)
} }
if device.aSecCfg.underloadPacketMagicHeader != 0 {
if header := device.headers.init.Load(); !header.IsZero() { sendf("h3=%d", device.aSecCfg.underloadPacketMagicHeader)
sendf("h1=%s", header.ToString())
} }
if device.aSecCfg.transportPacketMagicHeader != 0 {
if header := device.headers.response.Load(); !header.IsZero() { sendf("h4=%d", device.aSecCfg.transportPacketMagicHeader)
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 { for _, peer := range device.peers.keyMap {
// Serialize peer state. // Serialize peer state.
peer.handshake.mutex.RLock() peer.handshake.mutex.RLock()
@@ -209,10 +148,7 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
sendf("last_handshake_time_nsec=%d", nano) sendf("last_handshake_time_nsec=%d", nano)
sendf("tx_bytes=%d", peer.txBytes.Load()) sendf("tx_bytes=%d", peer.txBytes.Load())
sendf("rx_bytes=%d", peer.rxBytes.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 { device.allowedips.EntriesForPeer(peer, func(prefix netip.Prefix) bool {
sendf("allowed_ip=%s", prefix.String()) sendf("allowed_ip=%s", prefix.String())
@@ -241,19 +177,18 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
} }
}() }()
ipcDev := new(ipcSetDevice)
ipcDev.fromDevice(device)
peer := new(ipcSetPeer) peer := new(ipcSetPeer)
deviceConfig := true deviceConfig := true
tempASecCfg := aSecCfgType{}
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
if line == "" { if line == "" {
// Blank line means terminate operation. // Blank line means terminate operation.
err := ipcDev.mergeWithDevice(device) err := device.handlePostConfig(&tempASecCfg)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to merge with device: %w", err) return err
} }
peer.handlePostConfig() peer.handlePostConfig()
return nil return nil
@@ -282,7 +217,7 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
var err error var err error
if deviceConfig { if deviceConfig {
err = device.handleDeviceLine(ipcDev, key, value) err = device.handleDeviceLine(key, value, &tempASecCfg)
} else { } else {
err = device.handlePeerLine(peer, key, value) err = device.handlePeerLine(peer, key, value)
} }
@@ -290,9 +225,9 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
return err return err
} }
} }
err = ipcDev.mergeWithDevice(device) err = device.handlePostConfig(&tempASecCfg)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to merge with device: %w", err) return err
} }
peer.handlePostConfig() peer.handlePostConfig()
@@ -302,7 +237,7 @@ func (device *Device) IpcSetOperation(r io.Reader) (err error) {
return nil return nil
} }
func (device *Device) handleDeviceLine(ipcDev *ipcSetDevice, key, value string) error { func (device *Device) handleDeviceLine(key, value string, tempASecCfg *aSecCfgType) error {
switch key { switch key {
case "private_key": case "private_key":
var sk NoisePrivateKey var sk NoisePrivateKey
@@ -324,6 +259,7 @@ func (device *Device) handleDeviceLine(ipcDev *ipcSetDevice, key, value string)
device.net.Lock() device.net.Lock()
device.net.port = uint16(port) device.net.port = uint16(port)
device.serverMode = true
device.net.Unlock() device.net.Unlock()
if err := device.BindUpdate(); err != nil { if err := device.BindUpdate(); err != nil {
@@ -343,205 +279,87 @@ func (device *Device) handleDeviceLine(ipcDev *ipcSetDevice, key, value string)
case "replace_peers": case "replace_peers":
if value != "true" { if value != "true" {
return ipcErrorf( return ipcErrorf(ipc.IpcErrorInvalid, "failed to set replace_peers, invalid value: %v", value)
ipc.IpcErrorInvalid,
"failed to set replace_peers, invalid value: %v",
value,
)
} }
device.log.Verbosef("UAPI: Removing all peers") device.log.Verbosef("UAPI: Removing all peers")
device.RemoveAllPeers() device.RemoveAllPeers()
case "jc": case "jc":
jc, err := strconv.ParseUint(value, 10, 32) junkPacketCount, err := strconv.Atoi(value)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jc: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse junk_packet_count %w", err)
} }
device.log.Verbosef("UAPI: Updating junk_packet_count")
device.log.Verbosef("UAPI: Updating junk count") tempASecCfg.junkPacketCount = junkPacketCount
device.junk.count.Store(uint32(jc)) tempASecCfg.isSet = true
case "jmin": case "jmin":
jmin, err := strconv.ParseUint(value, 10, 32) junkPacketMinSize, err := strconv.Atoi(value)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jmin: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse junk_packet_min_size %w", err)
} }
device.log.Verbosef("UAPI: Updating junk_packet_min_size")
device.log.Verbosef("UAPI: Updating junk min") tempASecCfg.junkPacketMinSize = junkPacketMinSize
device.junk.min.Store(uint32(jmin)) tempASecCfg.isSet = true
case "jmax": case "jmax":
jmax, err := strconv.ParseUint(value, 10, 32) junkPacketMaxSize, err := strconv.Atoi(value)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse jmax: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse junk_packet_max_size %w", err)
} }
device.log.Verbosef("UAPI: Updating junk_packet_max_size")
device.log.Verbosef("UAPI: Updating junk max") tempASecCfg.junkPacketMaxSize = junkPacketMaxSize
device.junk.max.Store(uint32(jmax)) tempASecCfg.isSet = true
case "s1": case "s1":
padding, err := strconv.ParseUint(value, 10, 16) initPacketJunkSize, err := strconv.Atoi(value)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s1: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse init_packet_junk_size %w", err)
} }
ipcDev.paddings.init = uint32(padding) device.log.Verbosef("UAPI: Updating init_packet_junk_size")
tempASecCfg.initPacketJunkSize = initPacketJunkSize
tempASecCfg.isSet = true
case "s2": case "s2":
padding, err := strconv.ParseUint(value, 10, 16) responsePacketJunkSize, err := strconv.Atoi(value)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse s2: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse response_packet_junk_size %w", err)
} }
ipcDev.paddings.response = uint32(padding) device.log.Verbosef("UAPI: Updating response_packet_junk_size")
tempASecCfg.responsePacketJunkSize = responsePacketJunkSize
case "s3": tempASecCfg.isSet = true
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": case "h1":
var rang UintRange initPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
if err := rang.FromString(value); err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H1: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse init_packet_magic_header %w", err)
} }
ipcDev.headers.init = rang tempASecCfg.initPacketMagicHeader = uint32(initPacketMagicHeader)
tempASecCfg.isSet = true
case "h2": case "h2":
var rang UintRange responsePacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
if err := rang.FromString(value); err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H2: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse response_packet_magic_header %w", err)
} }
ipcDev.headers.response = rang tempASecCfg.responsePacketMagicHeader = uint32(responsePacketMagicHeader)
tempASecCfg.isSet = true
case "h3": case "h3":
var rang UintRange underloadPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
if err := rang.FromString(value); err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H3: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse underload_packet_magic_header %w", err)
} }
ipcDev.headers.cookie = rang tempASecCfg.underloadPacketMagicHeader = uint32(underloadPacketMagicHeader)
tempASecCfg.isSet = true
case "h4": case "h4":
var rang UintRange transportPacketMagicHeader, err := strconv.ParseUint(value, 10, 32)
if err := rang.FromString(value); err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse H4: %w", err)
}
ipcDev.headers.transport = rang
case "i1":
chain, err := newObfChain(value)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to parse I1: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "faield to parse transport_packet_magic_header %w", err)
} }
device.ipackets[0] = chain tempASecCfg.transportPacketMagicHeader = uint32(transportPacketMagicHeader)
tempASecCfg.isSet = true
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: default:
return ipcErrorf(ipc.IpcErrorInvalid, "invalid UAPI device key: %v", key) return ipcErrorf(ipc.IpcErrorInvalid, "invalid UAPI device key: %v", key)
@@ -615,11 +433,7 @@ func (device *Device) handlePeerLine(
case "update_only": case "update_only":
// allow disabling of creation // allow disabling of creation
if value != "true" { if value != "true" {
return ipcErrorf( return ipcErrorf(ipc.IpcErrorInvalid, "failed to set update only, invalid value: %v", value)
ipc.IpcErrorInvalid,
"failed to set update only, invalid value: %v",
value,
)
} }
if peer.created && !peer.dummy { if peer.created && !peer.dummy {
device.RemovePeer(peer.handshake.remoteStatic) device.RemovePeer(peer.handshake.remoteStatic)
@@ -663,24 +477,20 @@ func (device *Device) handlePeerLine(
case "persistent_keepalive_interval": case "persistent_keepalive_interval":
device.log.Verbosef("%v - UAPI: Updating persistent keepalive interval", peer.Peer) device.log.Verbosef("%v - UAPI: Updating persistent keepalive interval", peer.Peer)
var rang UintRange secs, err := strconv.ParseUint(value, 10, 16)
if err := rang.FromString(value); err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set persistent keepalive interval: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "failed to set persistent keepalive interval: %w", err)
} }
old := peer.persistentKeepaliveInterval.Swap(rang) old := peer.persistentKeepaliveInterval.Swap(uint32(secs))
// Send immediate keepalive if we're turning it on and before it wasn't on. // Send immediate keepalive if we're turning it on and before it wasn't on.
peer.pkaOn = old.IsZero() && !rang.IsZero() peer.pkaOn = old == 0 && secs != 0
case "replace_allowed_ips": case "replace_allowed_ips":
device.log.Verbosef("%v - UAPI: Removing all allowedips", peer.Peer) device.log.Verbosef("%v - UAPI: Removing all allowedips", peer.Peer)
if value != "true" { if value != "true" {
return ipcErrorf( return ipcErrorf(ipc.IpcErrorInvalid, "failed to replace allowedips, invalid value: %v", value)
ipc.IpcErrorInvalid,
"failed to replace allowedips, invalid value: %v",
value,
)
} }
if peer.dummy { if peer.dummy {
return nil return nil
@@ -688,14 +498,7 @@ func (device *Device) handlePeerLine(
device.allowedips.RemoveByPeer(peer.Peer) device.allowedips.RemoveByPeer(peer.Peer)
case "allowed_ip": case "allowed_ip":
add := true device.log.Verbosef("%v - UAPI: Adding allowedip", peer.Peer)
verb := "Adding"
if len(value) > 0 && value[0] == '-' {
add = false
verb = "Removing"
value = value[1:]
}
device.log.Verbosef("%v - UAPI: %s allowedip", peer.Peer, verb)
prefix, err := netip.ParsePrefix(value) prefix, err := netip.ParsePrefix(value)
if err != nil { if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to set allowed ip: %w", err) return ipcErrorf(ipc.IpcErrorInvalid, "failed to set allowed ip: %w", err)
@@ -703,11 +506,7 @@ func (device *Device) handlePeerLine(
if peer.dummy { if peer.dummy {
return nil return nil
} }
if add {
device.allowedips.Insert(prefix, peer.Peer) device.allowedips.Insert(prefix, peer.Peer)
} else {
device.allowedips.Remove(prefix, peer.Peer)
}
case "protocol_version": case "protocol_version":
if value != "1" { if value != "1" {
@@ -759,11 +558,7 @@ func (device *Device) IpcHandle(socket net.Conn) {
return return
} }
if nextByte != '\n' { if nextByte != '\n' {
err = ipcErrorf( err = ipcErrorf(ipc.IpcErrorInvalid, "trailing character in UAPI get: %q", nextByte)
ipc.IpcErrorInvalid,
"trailing character in UAPI get: %q",
nextByte,
)
break break
} }
err = device.IpcGetOperation(buffered.Writer) err = device.IpcGetOperation(buffered.Writer)
@@ -787,91 +582,3 @@ func (device *Device) IpcHandle(socket net.Conn) {
buffered.Flush() 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
}
+25
View File
@@ -0,0 +1,25 @@
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
}
+27
View File
@@ -0,0 +1,27 @@
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
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package main package main
+21 -27
View File
@@ -1,35 +1,29 @@
module github.com/amnezia-vpn/amneziawg-go/v3 module github.com/amnezia-vpn/amneziawg-go
go 1.25.0 go 1.21.6
toolchain go1.21.8
require ( require (
github.com/goccy/go-yaml v1.17.1 github.com/leninalive/udptlspipe v0.0.0-20240313123600-80348db0072f
go.uber.org/atomic v1.11.0 github.com/tevino/abool/v2 v2.1.0
golang.getoutline.org/sdk v0.0.23 golang.org/x/crypto v0.19.0
golang.getoutline.org/sdk/x v0.2.0 golang.org/x/net v0.21.0
golang.org/x/crypto v0.42.0 golang.org/x/sys v0.17.0
golang.org/x/net v0.44.0
golang.org/x/sys v0.36.0
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259
) )
require ( require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/AdguardTeam/golibs v0.20.0 // indirect
github.com/google/btree v1.1.3 // indirect github.com/andybalholm/brotli v1.0.6 // indirect
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect github.com/cloudflare/circl v1.3.7 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gobwas/httphead v0.1.0 // indirect
github.com/onsi/ginkgo/v2 v2.12.0 // indirect github.com/gobwas/pool v0.2.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/gobwas/ws v1.3.2 // indirect
github.com/quic-go/quic-go v0.48.1 // indirect github.com/google/btree v1.0.1 // indirect
github.com/shadowsocks/go-shadowsocks2 v0.1.5 // indirect github.com/klauspost/compress v1.17.4 // indirect
github.com/stretchr/testify v1.10.0 // indirect github.com/quic-go/quic-go v0.40.1 // indirect
go.uber.org/mock v0.4.0 // indirect github.com/refraction-networking/utls v1.6.2 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // 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
) )
+51 -83
View File
@@ -1,95 +1,63 @@
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/AdguardTeam/golibs v0.20.0 h1:A9FIdYq7wUKhFYy3z+YZ/Aw5oFUYgW+xgaVAJ0pnnPY=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/AdguardTeam/golibs v0.20.0/go.mod h1:3WunclLLfrVAq7fYQRhd6f168FHOEMssnipVXCxDL/w=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/ameshkov/udptlspipe v1.3.1 h1:e+eC2Yb+04KPzH9b/Uktwn6W6lw5CgbFdHnGfAaofx8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/ameshkov/udptlspipe v1.3.1/go.mod h1:UnpDx2J//7WS/RRe5hb2UVZpwJzHga95ArLkPS9aRBk=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/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 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/leninalive/udptlspipe v0.0.0-20240313123600-80348db0072f h1:VR2M22cXDtgp78N1mkCmxiXj1zYIP9ScUXS8gMHi6Vs=
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= github.com/leninalive/udptlspipe v0.0.0-20240313123600-80348db0072f/go.mod h1:U3O6PfEGIxmmxAkOucn8Ty1akGF/1N1lDPeHPLCz3Cg=
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/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/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA= github.com/refraction-networking/utls v1.6.2 h1:iTeeGY0o6nMNcGyirxkD5bFIsVctP5InGZ3E0HrzS7k=
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/refraction-networking/utls v1.6.2/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28= github.com/tevino/abool/v2 v2.1.0 h1:7w+Vf9f/5gmKT4m4qkayb33/92M+Um45F2BkHOR+L/c=
github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM= github.com/tevino/abool/v2 v2.1.0/go.mod h1:+Lmlqk6bHDWHqN1cbxqhwEAwMPXgc8I1SDEamtseuXY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
github.com/things-go/go-socks5 v0.0.5 h1:qvKaGcBkfDrUL33SchHN93srAmYGzb4CxSM2DPYufe8= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
github.com/things-go/go-socks5 v0.0.5/go.mod h1:mtzInf8v5xmsBpHZVbIw2YQYhc4K0jRwzfsH64Uh0IQ= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.getoutline.org/sdk v0.0.23 h1:UKoKCrRH3Ed6Jpg8ODYwOo6O1B1lvfIHPreMrTkSTcc= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.getoutline.org/sdk v0.0.23/go.mod h1:nKZlO//e/sRFk+rp8gm8EJ5RasDSyY+fGDNSd3I2iaA= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.getoutline.org/sdk/x v0.2.0 h1:4kuT2SgkPXktwPwT6CXuF+Jwe+COAGqiBDZbgJkS5fM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
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 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
+1 -1
View File
@@ -20,7 +20,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/amnezia-vpn/amneziawg-go/v3/ipc/namedpipe" "github.com/amnezia-vpn/amneziawg-go/ipc/namedpipe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package ipc package ipc
+2 -2
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package ipc package ipc
@@ -9,7 +9,7 @@ import (
"net" "net"
"os" "os"
"github.com/amnezia-vpn/amneziawg-go/v3/rwcancel" "github.com/amnezia-vpn/amneziawg-go/rwcancel"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
+1 -1
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package ipc package ipc
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package ipc package ipc
+3 -3
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package ipc package ipc
@@ -8,7 +8,7 @@ package ipc
import ( import (
"net" "net"
"github.com/amnezia-vpn/amneziawg-go/v3/ipc/namedpipe" "github.com/amnezia-vpn/amneziawg-go/ipc/namedpipe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
@@ -62,7 +62,7 @@ func init() {
func UAPIListen(name string) (net.Listener, error) { func UAPIListen(name string) (net.Listener, error) {
listener, err := (&namedpipe.ListenConfig{ listener, err := (&namedpipe.ListenConfig{
SecurityDescriptor: UAPISecurityDescriptor, SecurityDescriptor: UAPISecurityDescriptor,
}).Listen(`\\.\pipe\ProtectedPrefix\Administrators\AmneziaWG\` + name) }).Listen(`\\.\pipe\ProtectedPrefix\Administrators\WireGuard\` + name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+27 -27
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package main package main
@@ -14,10 +14,10 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/v3/device" "github.com/amnezia-vpn/amneziawg-go/device"
"github.com/amnezia-vpn/amneziawg-go/v3/ipc" "github.com/amnezia-vpn/amneziawg-go/ipc"
"github.com/amnezia-vpn/amneziawg-go/v3/tun" "github.com/amnezia-vpn/amneziawg-go/tun"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -33,7 +33,7 @@ const (
) )
func printUsage() { func printUsage() {
fmt.Printf("Usage: %s [-f/--foreground] INTERFACE-NAME\n", os.Args[0]) fmt.Printf("Usage: %s [-f/--foreground] [-u/--udptlspipe] INTERFACE-NAME\n", os.Args[0])
} }
func warning() { func warning() {
@@ -46,50 +46,50 @@ func warning() {
return return
} }
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────────────┐") fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────┐")
fmt.Fprintln(os.Stderr, "│ │") fmt.Fprintln(os.Stderr, "│ │")
fmt.Fprintln(os.Stderr, "│ Running amneziawg-go is not required because this │") fmt.Fprintln(os.Stderr, "│ Running wireguard-go is not required because this │")
fmt.Fprintln(os.Stderr, "│ kernel has first class support for AmneziaWG. For │") 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, "│ information on installing the kernel module, │")
fmt.Fprintln(os.Stderr, "│ please visit: │") fmt.Fprintln(os.Stderr, "│ please visit: │")
fmt.Fprintln(os.Stderr, "| https://github.com/amnezia-vpn/amneziawg-linux-kernel-module │") 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, "└──────────────────────────────────────────────────────┘")
} }
func main() { func main() {
if len(os.Args) == 2 && os.Args[1] == "--version" { if len(os.Args) == 2 && os.Args[1] == "--version" {
fmt.Printf("amneziawg-go %s\n\nUserspace AmneziaWG daemon for %s-%s.\nInformation available at https://amnezia.org\n", Version, runtime.GOOS, runtime.GOARCH) 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)
return return
} }
warning() warning()
var foreground bool var foreground = false
var interfaceName string //var utp = false
if len(os.Args) < 2 || len(os.Args) > 3 { var interfaceName = ""
if len(os.Args) < 2 || len(os.Args) > 4 {
printUsage() printUsage()
return return
} }
switch os.Args[1] { for _, arg := range os.Args[1:] {
switch arg {
case "-f", "--foreground": case "-f", "--foreground":
foreground = true foreground = true
if len(os.Args) != 3 {
printUsage() //case "-u", "--udptlspipe":
return //utp = true
}
interfaceName = os.Args[2]
default: default:
foreground = false interfaceName = arg
if len(os.Args) != 2 { }
}
if interfaceName == "" {
printUsage() printUsage()
return return
} }
interfaceName = os.Args[1]
}
if !foreground { if !foreground {
foreground = os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" foreground = os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1"
@@ -145,7 +145,7 @@ func main() {
fmt.Sprintf("(%s) ", interfaceName), fmt.Sprintf("(%s) ", interfaceName),
) )
logger.Verbosef("Starting amneziawg-go version %s", Version) logger.Verbosef("Starting wireguard-go version %s", Version)
if err != nil { if err != nil {
logger.Errorf("Failed to create TUN device: %v", err) logger.Errorf("Failed to create TUN device: %v", err)
+7 -7
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package main package main
@@ -12,11 +12,11 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/v3/device" "github.com/amnezia-vpn/amneziawg-go/device"
"github.com/amnezia-vpn/amneziawg-go/v3/ipc" "github.com/amnezia-vpn/amneziawg-go/ipc"
"github.com/amnezia-vpn/amneziawg-go/v3/tun" "github.com/amnezia-vpn/amneziawg-go/tun"
) )
const ( const (
@@ -30,13 +30,13 @@ func main() {
} }
interfaceName := os.Args[1] interfaceName := os.Args[1]
fmt.Fprintln(os.Stderr, "Warning: this is a test program for Windows, mainly used for debugging this Go package. For a real AmneziaWG for Windows client, please visit: https://amnezia.org") 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.")
logger := device.NewLogger( logger := device.NewLogger(
device.LogLevelVerbose, device.LogLevelVerbose,
fmt.Sprintf("(%s) ", interfaceName), fmt.Sprintf("(%s) ", interfaceName),
) )
logger.Verbosef("Starting amneziawg-go version %s", Version) logger.Verbosef("Starting wireguard-go version %s", Version)
tun, err := tun.CreateTUN(interfaceName, 0) tun, err := tun.CreateTUN(interfaceName, 0)
if err == nil { if err == nil {
-77
View File
@@ -1,77 +0,0 @@
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
}
-269
View File
@@ -1,269 +0,0 @@
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"`
HeaderProtectionKey string `yaml:"header_protection_key,omitempty"`
ContentPaddingAddition string `yaml:"content_padding_addition,omitempty"`
RekeyAfterTime string `yaml:"rekey_after_time,omitempty"`
RekeyTimeout string `yaml:"rekey_timeout,omitempty"`
RejectAfterTime string `yaml:"reject_after_time,omitempty"`
KeepaliveTimeout string `yaml:"keepalive_timeout,omitempty"`
MaxHandshakeAttempts string `yaml:"max_handshake_attempts,omitempty"`
RandomTrailers string `yaml:"random_trailers,omitempty"`
DisableCookies string `yaml:"disable_cookies,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)
}
if cfg.HeaderProtectionKey != "" {
b.WriteString("\nheader_protection_key=")
b.WriteString(cfg.HeaderProtectionKey)
}
if cfg.ContentPaddingAddition != "" {
b.WriteString("\ncontent_padding_addition=")
b.WriteString(cfg.ContentPaddingAddition)
}
if cfg.RekeyAfterTime != "" {
b.WriteString("\nrekey_after_time=")
b.WriteString(cfg.RekeyAfterTime)
}
if cfg.RekeyTimeout != "" {
b.WriteString("\nrekey_timeout=")
b.WriteString(cfg.RekeyTimeout)
}
if cfg.RejectAfterTime != "" {
b.WriteString("\nreject_after_time=")
b.WriteString(cfg.RejectAfterTime)
}
if cfg.KeepaliveTimeout != "" {
b.WriteString("\nkeepalive_timeout=")
b.WriteString(cfg.KeepaliveTimeout)
}
if cfg.MaxHandshakeAttempts != "" {
b.WriteString("\nmax_handshake_attempts=")
b.WriteString(cfg.MaxHandshakeAttempts)
}
if cfg.RandomTrailers != "" {
b.WriteString("\nrandom_trailers=")
b.WriteString(cfg.RandomTrailers)
}
if cfg.DisableCookies != "" {
b.WriteString("\ndisable_cookies=")
b.WriteString(cfg.DisableCookies)
}
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)
}
-52
View File
@@ -1,52 +0,0 @@
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("127.0.0.1:0", dialer); err != nil {
t.Fatal(err)
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package ratelimiter package ratelimiter
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package ratelimiter package ratelimiter
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
// Package replay implements an efficient anti-replay algorithm as specified in RFC 6479. // Package replay implements an efficient anti-replay algorithm as specified in RFC 6479.
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package replay package replay
+2 -2
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
// Package rwcancel implements cancelable read/write operations on // Package rwcancel implements cancelable read/write operations on
@@ -64,7 +64,7 @@ func (rw *RWCancel) ReadyRead() bool {
func (rw *RWCancel) ReadyWrite() bool { func (rw *RWCancel) ReadyWrite() bool {
closeFd := int32(rw.closingReader.Fd()) closeFd := int32(rw.closingReader.Fd())
pollFds := []unix.PollFd{{Fd: int32(rw.fd), Events: unix.POLLOUT}, {Fd: closeFd, Events: unix.POLLIN}} pollFds := []unix.PollFd{{Fd: int32(rw.fd), Events: unix.POLLOUT}, {Fd: closeFd, Events: unix.POLLOUT}}
var err error var err error
for { for {
_, err = unix.Poll(pollFds, -1) _, err = unix.Poll(pollFds, -1)
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package tai64n package tai64n
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package tai64n package tai64n
+1 -1
View File
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package tun package tun
+69 -53
View File
@@ -1,86 +1,102 @@
package tun package tun
import ( import "encoding/binary"
"encoding/binary"
"math/bits"
)
// TODO: Explore SIMD and/or other assembly optimizations. // 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 { func checksumNoFold(b []byte, initial uint64) uint64 {
tmp := make([]byte, 8) ac := initial
binary.NativeEndian.PutUint64(tmp, initial)
ac := binary.BigEndian.Uint64(tmp)
var carry uint64
for len(b) >= 128 { for len(b) >= 128 {
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0) ac += uint64(binary.BigEndian.Uint32(b[:4]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry) ac += uint64(binary.BigEndian.Uint32(b[4:8]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry) ac += uint64(binary.BigEndian.Uint32(b[8:12]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry) ac += uint64(binary.BigEndian.Uint32(b[12:16]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[32:40]), carry) ac += uint64(binary.BigEndian.Uint32(b[16:20]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[40:48]), carry) ac += uint64(binary.BigEndian.Uint32(b[20:24]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[48:56]), carry) ac += uint64(binary.BigEndian.Uint32(b[24:28]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[56:64]), carry) ac += uint64(binary.BigEndian.Uint32(b[28:32]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[64:72]), carry) ac += uint64(binary.BigEndian.Uint32(b[32:36]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[72:80]), carry) ac += uint64(binary.BigEndian.Uint32(b[36:40]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[80:88]), carry) ac += uint64(binary.BigEndian.Uint32(b[40:44]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[88:96]), carry) ac += uint64(binary.BigEndian.Uint32(b[44:48]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[96:104]), carry) ac += uint64(binary.BigEndian.Uint32(b[48:52]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[104:112]), carry) ac += uint64(binary.BigEndian.Uint32(b[52:56]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[112:120]), carry) ac += uint64(binary.BigEndian.Uint32(b[56:60]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[120:128]), carry) ac += uint64(binary.BigEndian.Uint32(b[60:64]))
ac += carry 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]))
b = b[128:] b = b[128:]
} }
if len(b) >= 64 { if len(b) >= 64 {
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0) ac += uint64(binary.BigEndian.Uint32(b[:4]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry) ac += uint64(binary.BigEndian.Uint32(b[4:8]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry) ac += uint64(binary.BigEndian.Uint32(b[8:12]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry) ac += uint64(binary.BigEndian.Uint32(b[12:16]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[32:40]), carry) ac += uint64(binary.BigEndian.Uint32(b[16:20]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[40:48]), carry) ac += uint64(binary.BigEndian.Uint32(b[20:24]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[48:56]), carry) ac += uint64(binary.BigEndian.Uint32(b[24:28]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[56:64]), carry) ac += uint64(binary.BigEndian.Uint32(b[28:32]))
ac += carry 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]))
b = b[64:] b = b[64:]
} }
if len(b) >= 32 { if len(b) >= 32 {
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0) ac += uint64(binary.BigEndian.Uint32(b[:4]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry) ac += uint64(binary.BigEndian.Uint32(b[4:8]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[16:24]), carry) ac += uint64(binary.BigEndian.Uint32(b[8:12]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[24:32]), carry) ac += uint64(binary.BigEndian.Uint32(b[12:16]))
ac += carry 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]))
b = b[32:] b = b[32:]
} }
if len(b) >= 16 { if len(b) >= 16 {
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0) ac += uint64(binary.BigEndian.Uint32(b[:4]))
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[8:16]), carry) ac += uint64(binary.BigEndian.Uint32(b[4:8]))
ac += carry ac += uint64(binary.BigEndian.Uint32(b[8:12]))
ac += uint64(binary.BigEndian.Uint32(b[12:16]))
b = b[16:] b = b[16:]
} }
if len(b) >= 8 { if len(b) >= 8 {
ac, carry = bits.Add64(ac, binary.NativeEndian.Uint64(b[:8]), 0) ac += uint64(binary.BigEndian.Uint32(b[:4]))
ac += carry ac += uint64(binary.BigEndian.Uint32(b[4:8]))
b = b[8:] b = b[8:]
} }
if len(b) >= 4 { if len(b) >= 4 {
ac, carry = bits.Add64(ac, uint64(binary.NativeEndian.Uint32(b[:4])), 0) ac += uint64(binary.BigEndian.Uint32(b))
ac += carry
b = b[4:] b = b[4:]
} }
if len(b) >= 2 { if len(b) >= 2 {
ac, carry = bits.Add64(ac, uint64(binary.NativeEndian.Uint16(b[:2])), 0) ac += uint64(binary.BigEndian.Uint16(b))
ac += carry
b = b[2:] b = b[2:]
} }
if len(b) == 1 { if len(b) == 1 {
tmp := binary.NativeEndian.Uint16([]byte{b[0], 0}) ac += uint64(b[0]) << 8
ac, carry = bits.Add64(ac, uint64(tmp), 0)
ac += carry
} }
binary.NativeEndian.PutUint64(tmp, ac) return ac
return binary.BigEndian.Uint64(tmp)
} }
func checksum(b []byte, initial uint64) uint16 { func checksum(b []byte, initial uint64) uint16 {
-63
View File
@@ -1,74 +1,11 @@
package tun package tun
import ( import (
"encoding/binary"
"fmt" "fmt"
"math/rand" "math/rand"
"testing" "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) { func BenchmarkChecksum(b *testing.B) {
lengths := []int{ lengths := []int{
64, 64,
+4 -4
View File
@@ -2,7 +2,7 @@
/* SPDX-License-Identifier: MIT /* SPDX-License-Identifier: MIT
* *
* Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved. * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/ */
package main package main
@@ -13,9 +13,9 @@ import (
"net/http" "net/http"
"net/netip" "net/netip"
"github.com/amnezia-vpn/amneziawg-go/v3/conn" "github.com/amnezia-vpn/amneziawg-go/conn"
"github.com/amnezia-vpn/amneziawg-go/v3/device" "github.com/amnezia-vpn/amneziawg-go/device"
"github.com/amnezia-vpn/amneziawg-go/v3/tun/netstack" "github.com/amnezia-vpn/amneziawg-go/tun/netstack"
) )
func main() { func main() {
Loaded 100 of 114 files, more files were not shown because too many files have changed in this diff. Show more