mirror of
https://github.com/fumiama/WireGold.git
synced 2026-06-05 07:50:24 +08:00
Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80ae21ff10 | ||
|
|
762c5a3692 | ||
|
|
3da0369a5f | ||
|
|
f0a3440dfb | ||
|
|
25c5a5d658 | ||
|
|
a85b102426 | ||
|
|
23d9238464 | ||
|
|
9e642f875a | ||
|
|
b86a65819c | ||
|
|
3f654deacb | ||
|
|
3d2f21725f | ||
|
|
b9df25092d | ||
|
|
eb83834343 | ||
|
|
ee3f4c6fb3 | ||
|
|
df8f6affa3 | ||
|
|
d65fc4dd71 | ||
|
|
8e79f419b8 | ||
|
|
8b28dbcd3c | ||
|
|
b5992574ec | ||
|
|
82c6136782 | ||
|
|
2483e2d3c7 | ||
|
|
1f7a42fd29 | ||
|
|
47c6e8f5cf | ||
|
|
0c2f201bd0 | ||
|
|
6fc45333d8 | ||
|
|
658916268a | ||
|
|
c2dd7b5d05 | ||
|
|
f4fd9b1423 | ||
|
|
60209117b7 | ||
|
|
689dfbc174 | ||
|
|
f0a853e449 | ||
|
|
fa7b9d4f0a | ||
|
|
597fa8d048 | ||
|
|
2c5bfa5c2d | ||
|
|
a2c442557a | ||
|
|
c5f3864837 | ||
|
|
489537d152 | ||
|
|
9f36504635 | ||
|
|
4b60801a0f | ||
|
|
60495227fc | ||
|
|
85a90aeb86 | ||
|
|
a205d889ca | ||
|
|
5c65302d67 | ||
|
|
3377d87d7a | ||
|
|
ac1d325bf0 | ||
|
|
552be0335b | ||
|
|
fae1b768f2 | ||
|
|
956199cf19 | ||
|
|
409f0f270b | ||
|
|
da99140e10 | ||
|
|
3d1bbf57a6 | ||
|
|
7f1c4ea4f0 | ||
|
|
dbe990cac8 | ||
|
|
a52f9aa2c0 | ||
|
|
ac04d25bfc | ||
|
|
78a744c5b0 | ||
|
|
f0776751dd | ||
|
|
d679f45931 | ||
|
|
82937b9b10 | ||
|
|
e6298d3459 | ||
|
|
bbe2c60aa3 | ||
|
|
1ba17fca54 | ||
|
|
9871bde9f1 | ||
|
|
45c8945c68 | ||
|
|
dfc9f1a7c4 | ||
|
|
9942ef2bd0 | ||
|
|
bd5c0092ef | ||
|
|
c7cfd94ae2 | ||
|
|
4a2b6c3f90 | ||
|
|
d5d7a9412f | ||
|
|
e018aee705 | ||
|
|
a0322b7d21 | ||
|
|
d577ae2e16 | ||
|
|
aa6f5ee100 | ||
|
|
b0667d5a45 | ||
|
|
1c258fcaa3 | ||
|
|
b71a0541bd | ||
|
|
ea768f88f9 | ||
|
|
cf2daf9a3e | ||
|
|
bafeb149be | ||
|
|
7e14ca5168 | ||
|
|
fa9abff1a8 | ||
|
|
08688b584b | ||
|
|
ecff222074 | ||
|
|
a4275beced | ||
|
|
aaafcdfa6c | ||
|
|
dd51f9f26f | ||
|
|
574d1ccfc4 | ||
|
|
eb49d35f65 | ||
|
|
60a98e4cae | ||
|
|
0c3f9111f5 | ||
|
|
763b9e3d77 | ||
|
|
9d986bb1d7 | ||
|
|
fc7f1d9744 | ||
|
|
5ff8d27fe4 | ||
|
|
28c388aca9 | ||
|
|
cb2fe9bd21 | ||
|
|
06853c6552 | ||
|
|
58cb7e09a8 | ||
|
|
04a3c9a10b | ||
|
|
4ffacafb23 | ||
|
|
8fa23be251 | ||
|
|
1c665c68fb | ||
|
|
7d25f46813 | ||
|
|
0482f001ec | ||
|
|
1bbec7f8f9 | ||
|
|
1a1327b6e8 | ||
|
|
9a63b3c886 | ||
|
|
39d8d5b755 | ||
|
|
c7bbcb9fb7 | ||
|
|
5d04567ec9 | ||
|
|
739cf863f1 | ||
|
|
17e1f6cac9 | ||
|
|
32af3ce142 |
13
.dockerignore
Normal file
13
.dockerignore
Normal file
@@ -0,0 +1,13 @@
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
.DS_Store
|
||||
|
||||
config.yaml
|
||||
config.yml
|
||||
|
||||
build
|
||||
dist
|
||||
tmp
|
||||
*.log
|
||||
*.out
|
||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -11,19 +11,22 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: ^1.20
|
||||
go-version: ^1.25
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Get dependencies
|
||||
run: go mod tidy
|
||||
|
||||
- name: Enable debug log
|
||||
run: sed -i 's/false/true/g' config/global.go
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
run: go build -ldflags=-checklinkname=0 -v ./...
|
||||
|
||||
- name: Test
|
||||
run: go test $(go list ./...)
|
||||
run: sudo go test -ldflags=-checklinkname=0 $(go list ./...) # ip test needs sudo
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
@@ -32,7 +35,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: ^1.20
|
||||
go-version: ^1.25
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@master
|
||||
@@ -41,3 +44,4 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@master
|
||||
with:
|
||||
version: latest
|
||||
install-mode: goinstall
|
||||
|
||||
109
.github/workflows/docker.yml
vendored
Normal file
109
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
name: docker
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- "**"
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
docker-ci:
|
||||
name: Build Docker image
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build test image
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
load: true
|
||||
tags: wiregold:test
|
||||
build-args: |
|
||||
VERSION=ci-${{ github.sha }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Smoke test help output
|
||||
run: docker run --rm wiregold:test -h
|
||||
|
||||
- name: Smoke test key generation
|
||||
run: docker run --rm wiregold:test -g
|
||||
|
||||
- name: Smoke test preshared key generation
|
||||
run: docker run --rm wiregold:test -pg
|
||||
|
||||
- name: Smoke test missing TUN message
|
||||
run: |
|
||||
set -euo pipefail
|
||||
output="$(docker run --rm wiregold:test 2>&1 || true)"
|
||||
printf '%s\n' "$output"
|
||||
printf '%s' "$output" | grep -F "WireGold requires /dev/net/tun inside the container."
|
||||
|
||||
docker-publish:
|
||||
name: Publish GHCR image
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker-ci
|
||||
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha
|
||||
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
labels: |
|
||||
org.opencontainers.image.title=WireGold
|
||||
org.opencontainers.image.description=Container image for WireGold, a pure-Go Layer 3 VPN inspired by WireGuard.
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.version }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
79
.github/workflows/release.yml
vendored
79
.github/workflows/release.yml
vendored
@@ -6,52 +6,69 @@ on:
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
CGO_ENABLED: 0
|
||||
LDFLAGS: -s -w -checklinkname=0
|
||||
VERSION_PKG: github.com/fumiama/WireGold/config
|
||||
|
||||
jobs:
|
||||
my-job:
|
||||
name: Build WireGold on Push Tag 🚀
|
||||
build:
|
||||
name: Build ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { name: linux-x64, goos: linux, goarch: amd64 }
|
||||
- { name: linux-x86, goos: linux, goarch: "386" }
|
||||
- { name: windows-x64, goos: windows, goarch: amd64, ext: .exe }
|
||||
- { name: windows-x86, goos: windows, goarch: "386", ext: .exe }
|
||||
- { name: linux-arm64, goos: linux, goarch: arm64, goarm: "7" }
|
||||
- { name: linux-armv6, goos: linux, goarch: arm, goarm: "6" }
|
||||
- { name: linux-mips, goos: linux, goarch: mips }
|
||||
- { name: linux-mips-softfloat, goos: linux, goarch: mips, gomips: softfloat }
|
||||
- { name: linux-mipsel, goos: linux, goarch: mipsle }
|
||||
- { name: linux-mipsel-softfloat, goos: linux, goarch: mipsle, gomips: softfloat }
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
GOMIPS: ${{ matrix.gomips }}
|
||||
steps:
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: ^1.20
|
||||
go-version: ^1.25
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
- name: Check out code
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Cache Go
|
||||
id: cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
# A list of files, directories, and wildcard patterns to cache and restore
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }}
|
||||
|
||||
- name: Tidy Go modules
|
||||
run: go mod tidy
|
||||
|
||||
- name: Build linux-x64
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/wg-linux-x64 -trimpath
|
||||
- name: Build linux-x86
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags="-s -w" -o artifacts/wg-linux-x86 -trimpath
|
||||
- name: Build windows-x64
|
||||
run: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o artifacts/wg-windows-x64.exe -trimpath
|
||||
- name: Build windows-x86
|
||||
run: CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w" -o artifacts/wg-windows-x86.exe -trimpath
|
||||
- name: Build arm64
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GOARM=7 go build -ldflags="-s -w" -o artifacts/wg-linux-arm64 -trimpath
|
||||
- name: Build armv6
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-s -w" -o artifacts/wg-linux-armv6 -trimpath
|
||||
- name: Build mips
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags="-s -w" -o artifacts/wg-linux-mips -trimpath
|
||||
- name: Build mips-softfloat
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags="-s -w" -o artifacts/wg-linux-mips-softfloat -trimpath
|
||||
- name: Build mipsel
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags="-s -w" -o artifacts/wg-linux-mipsel -trimpath
|
||||
- name: Build mipsel-softfloat
|
||||
run: CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags="-s -w" -o artifacts/wg-linux-mipsel-softfloat -trimpath
|
||||
- name: Build
|
||||
run: go build -ldflags="${LDFLAGS} -X ${VERSION_PKG}.Version=${GITHUB_REF_NAME#v}" -o wg-${{ matrix.name }}${{ matrix.ext }} -trimpath
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wg-${{ matrix.name }}
|
||||
path: wg-${{ matrix.name }}${{ matrix.ext }}
|
||||
|
||||
release:
|
||||
name: Upload Release 🚀
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
@@ -60,4 +77,4 @@ jobs:
|
||||
file: artifacts/wg-*
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
file_glob: true
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,5 +15,5 @@
|
||||
# vendor/
|
||||
|
||||
.DS_Store
|
||||
config.yaml
|
||||
/*.yaml
|
||||
WireGold
|
||||
|
||||
@@ -4,8 +4,8 @@ linters-settings:
|
||||
ignoretests: true
|
||||
exclude-functions:
|
||||
- (*os.File).Write
|
||||
- (*github.com/fumiama/WireGold/helper.Writer).Write
|
||||
- (*github.com/fumiama/WireGold/helper.Writer).WriteByte
|
||||
- (*github.com/fumiama/WireGold/internal/bin.Writer).Write
|
||||
- (*github.com/fumiama/WireGold/internal/bin.Writer).WriteByte
|
||||
- (*github.com/fumiama/WireGold/upper/services/tunnel.Tunnel).Write
|
||||
- (*github.com/fumiama/WireGold/upper/services/tunnel.Tunnel).Read
|
||||
|
||||
@@ -24,7 +24,7 @@ linters:
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- exportloopref
|
||||
#- exportloopref
|
||||
- exhaustive
|
||||
#- funlen
|
||||
#- goconst
|
||||
|
||||
43
Dockerfile
Normal file
43
Dockerfile
Normal file
@@ -0,0 +1,43 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
FROM --platform=$TARGETPLATFORM golang:1.25.0-bookworm AS build
|
||||
|
||||
ARG VERSION=dev
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
go build -trimpath -ldflags="-s -w -checklinkname=0 -X github.com/fumiama/WireGold/config.Version=${VERSION}" \
|
||||
-o /out/wg .
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
LABEL org.opencontainers.image.title="WireGold" \
|
||||
org.opencontainers.image.description="Container image for WireGold, a pure-Go Layer 3 VPN inspired by WireGuard." \
|
||||
org.opencontainers.image.source="https://github.com/fumiama/WireGold"
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates iproute2 tini \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /config
|
||||
|
||||
COPY --from=build /out/wg /usr/local/bin/wg
|
||||
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
RUN chmod +x /usr/local/bin/wg /usr/local/bin/docker-entrypoint.sh \
|
||||
&& mkdir -p /config
|
||||
|
||||
VOLUME ["/config"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
|
||||
CMD ["-c", "/config/config.yaml"]
|
||||
208
README.md
208
README.md
@@ -4,16 +4,38 @@
|
||||
</a><br>
|
||||
<h1>WireGold</h1>
|
||||
Wire Golang Guard = WireGold<br><br>
|
||||
|
||||
English | [中文](README_ZH.md)
|
||||
</div>
|
||||
|
||||
## Usage
|
||||
> If you are running in windows, remember to select the `wintun.dll` of your arch in `lower/wintun` and place it alongside the compiled exe
|
||||
## Overview
|
||||
|
||||
WireGold is a pure Go Layer 3 VPN inspired by WireGuard.
|
||||
|
||||
### Features
|
||||
|
||||
- **Encryption**: XChaCha20-Poly1305 (AEAD) + Curve25519 key exchange + BLAKE2B integrity check
|
||||
- **Transport**: UDP / UDP-Lite / TCP / Raw IP / ICMP
|
||||
- **Encoding**: Optional Base16384 encoding to traverse text-only filters
|
||||
- **Anti-censorship**: XOR mask header obfuscation + randomized MTU scaling + optional double-send
|
||||
- **Compression**: Optional Zstd payload compression
|
||||
- **NAT traversal**: Built-in Hello/Query/Notify protocol for keepalive and hole punching
|
||||
- **Routing**: Multi-hop forwarding with TTL decrement and routing table management
|
||||
- **Key rotation**: 32 preshared key slots with random index selection per packet
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
> On Windows, place the `wintun.dll` matching your architecture (from `lower/wintun/`) alongside the executable.
|
||||
|
||||
> For high-latency lossy links, consider pairing with [UDPspeeder](https://github.com/wangyu-/UDPspeeder).
|
||||
|
||||
> It is highly recommanded to use [UDPspeeder](https://github.com/wangyu-/UDPspeeder) together if you are using a High-latency Lossy Link
|
||||
```bash
|
||||
wg [-c config.yaml] [-d|w] [-g] [-h] [-p] [-l log.txt]
|
||||
```
|
||||
#### Instructions
|
||||
|
||||
#### Flags
|
||||
|
||||
```bash
|
||||
-c string
|
||||
specify conf file (default "config.yaml")
|
||||
@@ -28,27 +50,30 @@ wg [-c config.yaml] [-d|w] [-g] [-h] [-p] [-l log.txt]
|
||||
-w only show logs above warn level
|
||||
```
|
||||
|
||||
## Config file example
|
||||
## Configuration
|
||||
|
||||
- **macos mojave**: max mtu (under ipv4 endpoint) is `9159`
|
||||
- **ipv6 endpoint**: the recommand mtu is `1280~1500` to prevent the big segments from being dropped
|
||||
- **macOS Mojave**: max MTU (IPv4 endpoint) is `9159`
|
||||
- **IPv6 endpoint**: recommended MTU `1280–1500` to avoid oversized segment drops
|
||||
- **ICMP / Raw IP endpoint**: use bare IP address without port (e.g. `0.0.0.0`), requires root/admin privileges
|
||||
|
||||
```yaml
|
||||
IP: 192.168.233.1
|
||||
SubNet: 192.168.233.0/24
|
||||
PrivateKey: 暲菉斂狧污爉窫擸紈卆帞蔩慈睠庮扝憚瞼縀
|
||||
Network: udp # udp (default), udplite, tcp, ip, icmp
|
||||
EndPoint: 0.0.0.0:56789
|
||||
MTU: 1504
|
||||
SpeedLoop: 4096
|
||||
MaxTTL: 64
|
||||
Mask: 0x1234567890abcdef
|
||||
Base14: true
|
||||
Peers:
|
||||
-
|
||||
IP: "192.168.233.2"
|
||||
SubNet: 192.168.233.0/24
|
||||
PublicKey: 徯萃嵾爻燸攗窍褃冔蒔犡緇袿屿組待族砇嘀
|
||||
PresharedKey: 瀸敀爅崾嘊嵜紼樴稍毯攣矐訷蟷扛嬋庩崛昀
|
||||
EndPoint: 1.2.3.4:56789
|
||||
AllowedIPs: ["192.168.233.2/32", "x192.168.233.3/32"] # allow trans to 192.168.233.3, but don not create route
|
||||
AllowedIPs: ["192.168.233.2/32", "x192.168.233.3/32"] # accept packets from 192.168.233.3, but don not create route
|
||||
KeepAliveSeconds: 0
|
||||
QueryList: ["192.168.233.3"]
|
||||
MTU: 1400
|
||||
@@ -58,12 +83,173 @@ Peers:
|
||||
AllowTrans: true
|
||||
-
|
||||
IP: "192.168.233.3"
|
||||
SubNet: 192.168.233.0/24
|
||||
PublicKey: 牢喨粷詸衭譛浾蘹櫠砙杹蟫瑳叩刋橋経挵蘀
|
||||
PresharedKey: 竅琚喫従痸告烈兇厕趭萨假蔛瀇譄施烸蝫瘀
|
||||
EndPoint: ""
|
||||
AllowedIPs: ["192.168.233.3/32"]
|
||||
AllowedIPs: ["192.168.233.3/32", "y192.168.66.1/32"] # add route to 192.168.66.1 into inner route table but do not add it to system one
|
||||
MTU: 752
|
||||
DoublePacket: true
|
||||
KeepAliveSeconds: 0
|
||||
AllowTrans: false
|
||||
```
|
||||
|
||||
### Configuration Reference
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `Network` | Transport protocol: `udp` (default), `udplite`, `tcp`, `ip`, `icmp` |
|
||||
| `MaxTTL` | Initial TTL for outgoing packets; default `64` |
|
||||
| `SpeedLoop` | Log receive throughput statistics every N packets; default `4096` |
|
||||
| `AllowedIPs` | Prefix `x` to accept packets from the subnet without creating a system route; prefix `y` to add an internal route table entry only |
|
||||
| `Mask` | XOR mask for header obfuscation |
|
||||
| `Base14` | Enable Base16384 encoding |
|
||||
| `MTURandomRange` | Randomly shrink MTU by up to this value (never grows), adding traffic fingerprint randomness |
|
||||
| `DoublePacket` | Send every packet twice to counter heavy packet loss |
|
||||
| `KeepAliveSeconds` | NAT keepalive interval in seconds; 0 disables keepalive |
|
||||
| `QueryList` | Peer IPs to query for NAT traversal |
|
||||
| `UseZstd` | Enable Zstd compression |
|
||||
| `AllowTrans` | Allow this peer to relay traffic for other peers |
|
||||
|
||||
## Benchmark (localhost)
|
||||
|
||||
> MacBook Air M1, battery mode
|
||||
|
||||
<details>
|
||||
<summary>UDP MTU 4096</summary>
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/fumiama/WireGold/upper/services/tunnel
|
||||
cpu: Apple M1
|
||||
BenchmarkTunnelUDP/1024-plain-nob14-8 4938 228283 ns/op 4.49 MB/s 3642671 B/op 149 allocs/op
|
||||
BenchmarkTunnelUDP/1024-normal-nob14-8 5100 234118 ns/op 4.37 MB/s 3642409 B/op 147 allocs/op
|
||||
BenchmarkTunnelUDP/1024-plain-b14-8 4528 249429 ns/op 4.11 MB/s 3825461 B/op 179 allocs/op
|
||||
BenchmarkTunnelUDP/1024-normal-b14-8 4885 242048 ns/op 4.23 MB/s 3818262 B/op 175 allocs/op
|
||||
BenchmarkTunnelUDP/1024-preshared-nob14-8 4833 242460 ns/op 4.22 MB/s 3632537 B/op 144 allocs/op
|
||||
BenchmarkTunnelUDP/1024-preshared-b14-8 4348 239630 ns/op 4.27 MB/s 3820118 B/op 174 allocs/op
|
||||
BenchmarkTunnelUDP/2048-plain-nob14-8 4766 280419 ns/op 7.30 MB/s 3656588 B/op 148 allocs/op
|
||||
BenchmarkTunnelUDP/2048-normal-nob14-8 4353 250150 ns/op 8.19 MB/s 3639053 B/op 145 allocs/op
|
||||
BenchmarkTunnelUDP/2048-plain-b14-8 4136 278223 ns/op 7.36 MB/s 3848032 B/op 178 allocs/op
|
||||
BenchmarkTunnelUDP/2048-normal-b14-8 4264 268694 ns/op 7.62 MB/s 3842609 B/op 176 allocs/op
|
||||
BenchmarkTunnelUDP/2048-preshared-nob14-8 4154 262575 ns/op 7.80 MB/s 3640443 B/op 144 allocs/op
|
||||
BenchmarkTunnelUDP/2048-preshared-b14-8 3932 287082 ns/op 7.13 MB/s 3846167 B/op 176 allocs/op
|
||||
BenchmarkTunnelUDP/3072-plain-nob14-8 4006 267281 ns/op 11.49 MB/s 3690985 B/op 164 allocs/op
|
||||
BenchmarkTunnelUDP/3072-normal-nob14-8 3942 271832 ns/op 11.30 MB/s 3670827 B/op 162 allocs/op
|
||||
BenchmarkTunnelUDP/3072-plain-b14-8 3529 291120 ns/op 10.55 MB/s 3993371 B/op 211 allocs/op
|
||||
BenchmarkTunnelUDP/3072-normal-b14-8 3614 298778 ns/op 10.28 MB/s 3994267 B/op 211 allocs/op
|
||||
BenchmarkTunnelUDP/3072-preshared-nob14-8 4036 297819 ns/op 10.31 MB/s 3674026 B/op 162 allocs/op
|
||||
BenchmarkTunnelUDP/3072-preshared-b14-8 3705 300820 ns/op 10.21 MB/s 3989965 B/op 210 allocs/op
|
||||
BenchmarkTunnelUDP/4096-plain-nob14-8 2604 398308 ns/op 10.28 MB/s 7389986 B/op 320 allocs/op
|
||||
BenchmarkTunnelUDP/4096-normal-nob14-8 2744 399739 ns/op 10.25 MB/s 7348911 B/op 316 allocs/op
|
||||
BenchmarkTunnelUDP/4096-plain-b14-8 2788 430813 ns/op 9.51 MB/s 7965100 B/op 410 allocs/op
|
||||
BenchmarkTunnelUDP/4096-normal-b14-8 2620 432984 ns/op 9.46 MB/s 7957374 B/op 407 allocs/op
|
||||
BenchmarkTunnelUDP/4096-preshared-nob14-8 2750 395736 ns/op 10.35 MB/s 7348747 B/op 315 allocs/op
|
||||
BenchmarkTunnelUDP/4096-preshared-b14-8 2628 431785 ns/op 9.49 MB/s 7961597 B/op 407 allocs/op
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>UDP MTU 1024</summary>
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/fumiama/WireGold/upper/services/tunnel
|
||||
cpu: Apple M1
|
||||
BenchmarkTunnelUDPSmallMTU/1024-plain-nob14-8 4770 256794 ns/op 3.99 MB/s 3715458 B/op 193 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-normal-nob14-8 4945 242538 ns/op 4.22 MB/s 3681420 B/op 188 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-plain-b14-8 4137 269202 ns/op 3.80 MB/s 4101089 B/op 254 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-normal-b14-8 4592 253461 ns/op 4.04 MB/s 4109262 B/op 253 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-preshared-nob14-8 4764 243752 ns/op 4.20 MB/s 3675691 B/op 186 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-preshared-b14-8 4086 282682 ns/op 3.62 MB/s 4107240 B/op 253 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-plain-nob14-8 4728 252759 ns/op 8.10 MB/s 3762231 B/op 234 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-normal-nob14-8 4245 257036 ns/op 7.97 MB/s 3729842 B/op 232 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-plain-b14-8 3615 308642 ns/op 6.64 MB/s 4469625 B/op 342 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-normal-b14-8 3624 311780 ns/op 6.57 MB/s 4487346 B/op 345 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-preshared-nob14-8 3999 260043 ns/op 7.88 MB/s 3723444 B/op 231 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-preshared-b14-8 3558 315744 ns/op 6.49 MB/s 4476565 B/op 343 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-plain-nob14-8 3814 265654 ns/op 11.56 MB/s 3802900 B/op 280 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-normal-nob14-8 4380 291992 ns/op 10.52 MB/s 3760254 B/op 276 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-plain-b14-8 3340 338760 ns/op 9.07 MB/s 4849826 B/op 434 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-normal-b14-8 3302 345620 ns/op 8.89 MB/s 4852322 B/op 434 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-preshared-nob14-8 4424 265290 ns/op 11.58 MB/s 3761816 B/op 277 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-preshared-b14-8 3148 344490 ns/op 8.92 MB/s 4849613 B/op 434 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-plain-nob14-8 2586 399489 ns/op 10.25 MB/s 7570823 B/op 467 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-normal-nob14-8 2576 402297 ns/op 10.18 MB/s 7504731 B/op 464 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-plain-b14-8 2240 484812 ns/op 8.45 MB/s 9081331 B/op 696 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-normal-b14-8 2240 504749 ns/op 8.11 MB/s 9069168 B/op 693 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-preshared-nob14-8 2594 392716 ns/op 10.43 MB/s 7480678 B/op 460 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-preshared-b14-8 2234 506134 ns/op 8.09 MB/s 9066223 B/op 691 allocs/op
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TCP MTU 4096</summary>
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/fumiama/WireGold/upper/services/tunnel
|
||||
cpu: Apple M1
|
||||
BenchmarkTunnelTCP/1024-plain-nob14-8 4627 246837 ns/op 4.15 MB/s 3684040 B/op 201 allocs/op
|
||||
BenchmarkTunnelTCP/1024-normal-nob14-8 4833 257150 ns/op 3.98 MB/s 3682260 B/op 199 allocs/op
|
||||
BenchmarkTunnelTCP/1024-plain-b14-8 4396 272838 ns/op 3.75 MB/s 3850134 B/op 231 allocs/op
|
||||
BenchmarkTunnelTCP/1024-normal-b14-8 4104 252293 ns/op 4.06 MB/s 3844674 B/op 226 allocs/op
|
||||
BenchmarkTunnelTCP/1024-preshared-nob14-8 4530 264767 ns/op 3.87 MB/s 3680243 B/op 197 allocs/op
|
||||
BenchmarkTunnelTCP/1024-preshared-b14-8 4231 287111 ns/op 3.57 MB/s 3847164 B/op 227 allocs/op
|
||||
BenchmarkTunnelTCP/2048-plain-nob14-8 4275 276425 ns/op 7.41 MB/s 3698728 B/op 200 allocs/op
|
||||
BenchmarkTunnelTCP/2048-normal-nob14-8 4033 261234 ns/op 7.84 MB/s 3701433 B/op 200 allocs/op
|
||||
BenchmarkTunnelTCP/2048-plain-b14-8 3680 303246 ns/op 6.75 MB/s 3875541 B/op 231 allocs/op
|
||||
BenchmarkTunnelTCP/2048-normal-b14-8 3626 288219 ns/op 7.11 MB/s 3878505 B/op 230 allocs/op
|
||||
BenchmarkTunnelTCP/2048-preshared-nob14-8 3868 287679 ns/op 7.12 MB/s 3696931 B/op 200 allocs/op
|
||||
BenchmarkTunnelTCP/2048-preshared-b14-8 3586 305008 ns/op 6.71 MB/s 3878416 B/op 230 allocs/op
|
||||
BenchmarkTunnelTCP/3072-plain-nob14-8 3666 298452 ns/op 10.29 MB/s 3767509 B/op 246 allocs/op
|
||||
BenchmarkTunnelTCP/3072-normal-nob14-8 3450 304848 ns/op 10.08 MB/s 3761811 B/op 246 allocs/op
|
||||
BenchmarkTunnelTCP/3072-plain-b14-8 3549 315641 ns/op 9.73 MB/s 4032830 B/op 291 allocs/op
|
||||
BenchmarkTunnelTCP/3072-normal-b14-8 3440 327234 ns/op 9.39 MB/s 4038470 B/op 292 allocs/op
|
||||
BenchmarkTunnelTCP/3072-preshared-nob14-8 3522 302663 ns/op 10.15 MB/s 3760304 B/op 245 allocs/op
|
||||
BenchmarkTunnelTCP/3072-preshared-b14-8 3390 326384 ns/op 9.41 MB/s 4040489 B/op 293 allocs/op
|
||||
BenchmarkTunnelTCP/4096-plain-nob14-8 2431 435457 ns/op 9.41 MB/s 7515476 B/op 480 allocs/op
|
||||
BenchmarkTunnelTCP/4096-normal-nob14-8 2500 433178 ns/op 9.46 MB/s 7511114 B/op 478 allocs/op
|
||||
BenchmarkTunnelTCP/4096-plain-b14-8 2337 457177 ns/op 8.96 MB/s 8033760 B/op 568 allocs/op
|
||||
BenchmarkTunnelTCP/4096-normal-b14-8 2374 465704 ns/op 8.80 MB/s 8040812 B/op 567 allocs/op
|
||||
BenchmarkTunnelTCP/4096-preshared-nob14-8 2532 436310 ns/op 9.39 MB/s 7510565 B/op 477 allocs/op
|
||||
BenchmarkTunnelTCP/4096-preshared-b14-8 2360 459261 ns/op 8.92 MB/s 8037878 B/op 566 allocs/op
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TCP MTU 1024</summary>
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/fumiama/WireGold/upper/services/tunnel
|
||||
cpu: Apple M1
|
||||
BenchmarkTunnelTCPSmallMTU/1024-plain-nob14-8 3318 312084 ns/op 3.28 MB/s 3797015 B/op 307 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-normal-nob14-8 4102 303641 ns/op 3.37 MB/s 3795618 B/op 308 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-plain-b14-8 3746 314102 ns/op 3.26 MB/s 4147318 B/op 368 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-normal-b14-8 3609 315252 ns/op 3.25 MB/s 4152014 B/op 368 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-preshared-nob14-8 3826 300693 ns/op 3.41 MB/s 3793725 B/op 304 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-preshared-b14-8 3628 327852 ns/op 3.12 MB/s 4150869 B/op 367 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-plain-nob14-8 3553 315709 ns/op 6.49 MB/s 3945193 B/op 426 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-normal-nob14-8 3254 329794 ns/op 6.21 MB/s 3933224 B/op 427 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-plain-b14-8 3222 357250 ns/op 5.73 MB/s 4538189 B/op 529 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-normal-b14-8 3080 359401 ns/op 5.70 MB/s 4555108 B/op 535 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-preshared-nob14-8 3463 320078 ns/op 6.40 MB/s 3936771 B/op 426 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-preshared-b14-8 2990 363645 ns/op 5.63 MB/s 4555897 B/op 535 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-plain-nob14-8 3228 336736 ns/op 9.12 MB/s 4090750 B/op 550 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-normal-nob14-8 3076 347067 ns/op 8.85 MB/s 4084480 B/op 554 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-plain-b14-8 2798 395353 ns/op 7.77 MB/s 4952186 B/op 700 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-normal-b14-8 2725 403959 ns/op 7.60 MB/s 4965324 B/op 705 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-preshared-nob14-8 3366 344086 ns/op 8.93 MB/s 4080821 B/op 549 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-preshared-b14-8 2797 403142 ns/op 7.62 MB/s 4962100 B/op 703 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-plain-nob14-8 2360 490867 ns/op 8.34 MB/s 7940290 B/op 871 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-normal-nob14-8 2223 486839 ns/op 8.41 MB/s 7927235 B/op 872 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-plain-b14-8 2002 557560 ns/op 7.35 MB/s 9201342 B/op 1087 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-normal-b14-8 1868 564007 ns/op 7.26 MB/s 9216972 B/op 1091 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-preshared-nob14-8 2263 491698 ns/op 8.33 MB/s 7925404 B/op 869 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-preshared-b14-8 2050 559663 ns/op 7.32 MB/s 9211292 B/op 1086 allocs/op
|
||||
```
|
||||
</details>
|
||||
|
||||
254
README_ZH.md
Normal file
254
README_ZH.md
Normal file
@@ -0,0 +1,254 @@
|
||||
<div align="center">
|
||||
<a href="https://crypko.ai/crypko/G39ZPfer7g6rz/">
|
||||
<img src=".github/Maria.png" width = "400" alt="WireGold-Maria">
|
||||
</a><br>
|
||||
<h1>WireGold</h1>
|
||||
Wire Golang Guard = WireGold<br><br>
|
||||
|
||||
[English](README.md) | 中文
|
||||
</div>
|
||||
|
||||
## 概述
|
||||
|
||||
WireGold 是一个纯 Go 实现的第 3 层 VPN,灵感来自 WireGuard。
|
||||
|
||||
### 主要特性
|
||||
|
||||
- **加密**: XChaCha20-Poly1305 (AEAD) + Curve25519 密钥交换 + BLAKE2B 完整性校验
|
||||
- **传输**: 支持 UDP / UDP-Lite / TCP / Raw IP / ICMP 多种底层传输
|
||||
- **编码**: 可选 Base16384 编码以穿越文本过滤
|
||||
- **抗审查**: XOR 掩码混淆报头 + 随机 MTU 放缩 + 可选双倍发包
|
||||
- **压缩**: 可选 Zstd 数据压缩
|
||||
- **NAT 穿透**: 内置 Hello/Query/Notify 协议实现 NAT 保活与穿透
|
||||
- **路由转发**: 支持多跳转发 (TTL 递减) 与路由表管理
|
||||
- **密钥集**: 支持 32 组预共享密钥混合,随机选择密钥索引
|
||||
|
||||
## 使用方法
|
||||
|
||||
> Windows 用户需将对应架构的 `wintun.dll` (位于 `lower/wintun/`) 放在可执行文件同目录下
|
||||
|
||||
> 高延迟有损链路建议配合 [UDPspeeder](https://github.com/wangyu-/UDPspeeder) 使用
|
||||
|
||||
```bash
|
||||
wg [-c config.yaml] [-d|w] [-g] [-h] [-p] [-l log.txt]
|
||||
```
|
||||
|
||||
#### 参数说明
|
||||
|
||||
```bash
|
||||
-c string
|
||||
指定配置文件 (默认 "config.yaml")
|
||||
-d 输出调试日志
|
||||
-g 生成密钥对
|
||||
-h 显示帮助
|
||||
-l string
|
||||
将日志写入文件 (默认 "-")
|
||||
-p 显示本机公钥
|
||||
-pg
|
||||
生成预共享密钥
|
||||
-w 仅显示 warn 及以上级别日志
|
||||
```
|
||||
|
||||
## 配置文件示例
|
||||
|
||||
- **macOS Mojave**: 最大 MTU (IPv4 endpoint) 为 `9159`
|
||||
- **IPv6 endpoint**: 推荐 MTU `1280~1500`,避免大分片被丢弃
|
||||
- **ICMP / Raw IP endpoint**: 使用裸 IP 地址,无需端口号 (如 `0.0.0.0`)。需要 root/管理员权限
|
||||
|
||||
```yaml
|
||||
IP: 192.168.233.1
|
||||
SubNet: 192.168.233.0/24
|
||||
PrivateKey: 暲菉斂狧污爉窫擸紈卆帞蔩慈睠庮扝憚瞼縀
|
||||
Network: udp # udp (默认), udplite, tcp, ip, icmp
|
||||
EndPoint: 0.0.0.0:56789
|
||||
MTU: 1504
|
||||
SpeedLoop: 4096
|
||||
MaxTTL: 64
|
||||
Mask: 0x1234567890abcdef
|
||||
Base14: true
|
||||
Peers:
|
||||
-
|
||||
IP: "192.168.233.2"
|
||||
PublicKey: 徯萃嵾爻燸攗窍褃冔蒔犡緇袿屿組待族砇嘀
|
||||
PresharedKey: 瀸敀爅崾嘊嵜紼樴稍毯攣矐訷蟷扛嬋庩崛昀
|
||||
EndPoint: 1.2.3.4:56789
|
||||
AllowedIPs: ["192.168.233.2/32", "x192.168.233.3/32"]
|
||||
KeepAliveSeconds: 0
|
||||
QueryList: ["192.168.233.3"]
|
||||
MTU: 1400
|
||||
MTURandomRange: 128
|
||||
UseZstd: true
|
||||
QuerySeconds: 10
|
||||
AllowTrans: true
|
||||
-
|
||||
IP: "192.168.233.3"
|
||||
PublicKey: 牢喨粷詸衭譛浾蘹櫠砙杹蟫瑳叩刋橋経挵蘀
|
||||
PresharedKey: 竅琚喫従痸告烈兇厕趭萨假蔛瀇譄施烸蝫瘀
|
||||
EndPoint: ""
|
||||
AllowedIPs: ["192.168.233.3/32", "y192.168.66.1/32"]
|
||||
MTU: 752
|
||||
DoublePacket: true
|
||||
KeepAliveSeconds: 0
|
||||
AllowTrans: false
|
||||
```
|
||||
|
||||
### 配置字段说明
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `Network` | 传输协议: `udp` (默认), `udplite`, `tcp`, `ip`, `icmp` |
|
||||
| `MaxTTL` | 发包初始 TTL,默认 `64` |
|
||||
| `SpeedLoop` | 每收到 N 个包时输出一次吞吐统计,默认 `4096` |
|
||||
| `AllowedIPs` | 前缀 `x` 表示只接受该网段报文但不建系统路由;前缀 `y` 表示只添加内部路由表条目 |
|
||||
| `Mask` | XOR 掩码,用于混淆报头 |
|
||||
| `Base14` | 启用 Base16384 编码 |
|
||||
| `MTURandomRange` | 随机缩小 MTU 的范围 (只减不增),增加流量特征随机性 |
|
||||
| `DoublePacket` | 双倍发包以对抗强丢包链路 |
|
||||
| `KeepAliveSeconds` | NAT 保活间隔 (秒),0 为不保活 |
|
||||
| `QueryList` | NAT 穿透时查询的对端 IP 列表 |
|
||||
| `UseZstd` | 启用 Zstd 压缩 |
|
||||
| `AllowTrans` | 是否允许为其他 Peer 转发流量 |
|
||||
|
||||
## 本地基准测试
|
||||
|
||||
> Mac Book Air M1,电池供电模式
|
||||
|
||||
<details>
|
||||
<summary>UDP MTU 4096</summary>
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/fumiama/WireGold/upper/services/tunnel
|
||||
cpu: Apple M1
|
||||
BenchmarkTunnelUDP/1024-plain-nob14-8 4938 228283 ns/op 4.49 MB/s 3642671 B/op 149 allocs/op
|
||||
BenchmarkTunnelUDP/1024-normal-nob14-8 5100 234118 ns/op 4.37 MB/s 3642409 B/op 147 allocs/op
|
||||
BenchmarkTunnelUDP/1024-plain-b14-8 4528 249429 ns/op 4.11 MB/s 3825461 B/op 179 allocs/op
|
||||
BenchmarkTunnelUDP/1024-normal-b14-8 4885 242048 ns/op 4.23 MB/s 3818262 B/op 175 allocs/op
|
||||
BenchmarkTunnelUDP/1024-preshared-nob14-8 4833 242460 ns/op 4.22 MB/s 3632537 B/op 144 allocs/op
|
||||
BenchmarkTunnelUDP/1024-preshared-b14-8 4348 239630 ns/op 4.27 MB/s 3820118 B/op 174 allocs/op
|
||||
BenchmarkTunnelUDP/2048-plain-nob14-8 4766 280419 ns/op 7.30 MB/s 3656588 B/op 148 allocs/op
|
||||
BenchmarkTunnelUDP/2048-normal-nob14-8 4353 250150 ns/op 8.19 MB/s 3639053 B/op 145 allocs/op
|
||||
BenchmarkTunnelUDP/2048-plain-b14-8 4136 278223 ns/op 7.36 MB/s 3848032 B/op 178 allocs/op
|
||||
BenchmarkTunnelUDP/2048-normal-b14-8 4264 268694 ns/op 7.62 MB/s 3842609 B/op 176 allocs/op
|
||||
BenchmarkTunnelUDP/2048-preshared-nob14-8 4154 262575 ns/op 7.80 MB/s 3640443 B/op 144 allocs/op
|
||||
BenchmarkTunnelUDP/2048-preshared-b14-8 3932 287082 ns/op 7.13 MB/s 3846167 B/op 176 allocs/op
|
||||
BenchmarkTunnelUDP/3072-plain-nob14-8 4006 267281 ns/op 11.49 MB/s 3690985 B/op 164 allocs/op
|
||||
BenchmarkTunnelUDP/3072-normal-nob14-8 3942 271832 ns/op 11.30 MB/s 3670827 B/op 162 allocs/op
|
||||
BenchmarkTunnelUDP/3072-plain-b14-8 3529 291120 ns/op 10.55 MB/s 3993371 B/op 211 allocs/op
|
||||
BenchmarkTunnelUDP/3072-normal-b14-8 3614 298778 ns/op 10.28 MB/s 3994267 B/op 211 allocs/op
|
||||
BenchmarkTunnelUDP/3072-preshared-nob14-8 4036 297819 ns/op 10.31 MB/s 3674026 B/op 162 allocs/op
|
||||
BenchmarkTunnelUDP/3072-preshared-b14-8 3705 300820 ns/op 10.21 MB/s 3989965 B/op 210 allocs/op
|
||||
BenchmarkTunnelUDP/4096-plain-nob14-8 2604 398308 ns/op 10.28 MB/s 7389986 B/op 320 allocs/op
|
||||
BenchmarkTunnelUDP/4096-normal-nob14-8 2744 399739 ns/op 10.25 MB/s 7348911 B/op 316 allocs/op
|
||||
BenchmarkTunnelUDP/4096-plain-b14-8 2788 430813 ns/op 9.51 MB/s 7965100 B/op 410 allocs/op
|
||||
BenchmarkTunnelUDP/4096-normal-b14-8 2620 432984 ns/op 9.46 MB/s 7957374 B/op 407 allocs/op
|
||||
BenchmarkTunnelUDP/4096-preshared-nob14-8 2750 395736 ns/op 10.35 MB/s 7348747 B/op 315 allocs/op
|
||||
BenchmarkTunnelUDP/4096-preshared-b14-8 2628 431785 ns/op 9.49 MB/s 7961597 B/op 407 allocs/op
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>UDP MTU 1024</summary>
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/fumiama/WireGold/upper/services/tunnel
|
||||
cpu: Apple M1
|
||||
BenchmarkTunnelUDPSmallMTU/1024-plain-nob14-8 4770 256794 ns/op 3.99 MB/s 3715458 B/op 193 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-normal-nob14-8 4945 242538 ns/op 4.22 MB/s 3681420 B/op 188 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-plain-b14-8 4137 269202 ns/op 3.80 MB/s 4101089 B/op 254 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-normal-b14-8 4592 253461 ns/op 4.04 MB/s 4109262 B/op 253 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-preshared-nob14-8 4764 243752 ns/op 4.20 MB/s 3675691 B/op 186 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/1024-preshared-b14-8 4086 282682 ns/op 3.62 MB/s 4107240 B/op 253 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-plain-nob14-8 4728 252759 ns/op 8.10 MB/s 3762231 B/op 234 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-normal-nob14-8 4245 257036 ns/op 7.97 MB/s 3729842 B/op 232 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-plain-b14-8 3615 308642 ns/op 6.64 MB/s 4469625 B/op 342 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-normal-b14-8 3624 311780 ns/op 6.57 MB/s 4487346 B/op 345 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-preshared-nob14-8 3999 260043 ns/op 7.88 MB/s 3723444 B/op 231 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/2048-preshared-b14-8 3558 315744 ns/op 6.49 MB/s 4476565 B/op 343 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-plain-nob14-8 3814 265654 ns/op 11.56 MB/s 3802900 B/op 280 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-normal-nob14-8 4380 291992 ns/op 10.52 MB/s 3760254 B/op 276 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-plain-b14-8 3340 338760 ns/op 9.07 MB/s 4849826 B/op 434 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-normal-b14-8 3302 345620 ns/op 8.89 MB/s 4852322 B/op 434 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-preshared-nob14-8 4424 265290 ns/op 11.58 MB/s 3761816 B/op 277 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/3072-preshared-b14-8 3148 344490 ns/op 8.92 MB/s 4849613 B/op 434 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-plain-nob14-8 2586 399489 ns/op 10.25 MB/s 7570823 B/op 467 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-normal-nob14-8 2576 402297 ns/op 10.18 MB/s 7504731 B/op 464 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-plain-b14-8 2240 484812 ns/op 8.45 MB/s 9081331 B/op 696 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-normal-b14-8 2240 504749 ns/op 8.11 MB/s 9069168 B/op 693 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-preshared-nob14-8 2594 392716 ns/op 10.43 MB/s 7480678 B/op 460 allocs/op
|
||||
BenchmarkTunnelUDPSmallMTU/4096-preshared-b14-8 2234 506134 ns/op 8.09 MB/s 9066223 B/op 691 allocs/op
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TCP MTU 4096</summary>
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/fumiama/WireGold/upper/services/tunnel
|
||||
cpu: Apple M1
|
||||
BenchmarkTunnelTCP/1024-plain-nob14-8 4627 246837 ns/op 4.15 MB/s 3684040 B/op 201 allocs/op
|
||||
BenchmarkTunnelTCP/1024-normal-nob14-8 4833 257150 ns/op 3.98 MB/s 3682260 B/op 199 allocs/op
|
||||
BenchmarkTunnelTCP/1024-plain-b14-8 4396 272838 ns/op 3.75 MB/s 3850134 B/op 231 allocs/op
|
||||
BenchmarkTunnelTCP/1024-normal-b14-8 4104 252293 ns/op 4.06 MB/s 3844674 B/op 226 allocs/op
|
||||
BenchmarkTunnelTCP/1024-preshared-nob14-8 4530 264767 ns/op 3.87 MB/s 3680243 B/op 197 allocs/op
|
||||
BenchmarkTunnelTCP/1024-preshared-b14-8 4231 287111 ns/op 3.57 MB/s 3847164 B/op 227 allocs/op
|
||||
BenchmarkTunnelTCP/2048-plain-nob14-8 4275 276425 ns/op 7.41 MB/s 3698728 B/op 200 allocs/op
|
||||
BenchmarkTunnelTCP/2048-normal-nob14-8 4033 261234 ns/op 7.84 MB/s 3701433 B/op 200 allocs/op
|
||||
BenchmarkTunnelTCP/2048-plain-b14-8 3680 303246 ns/op 6.75 MB/s 3875541 B/op 231 allocs/op
|
||||
BenchmarkTunnelTCP/2048-normal-b14-8 3626 288219 ns/op 7.11 MB/s 3878505 B/op 230 allocs/op
|
||||
BenchmarkTunnelTCP/2048-preshared-nob14-8 3868 287679 ns/op 7.12 MB/s 3696931 B/op 200 allocs/op
|
||||
BenchmarkTunnelTCP/2048-preshared-b14-8 3586 305008 ns/op 6.71 MB/s 3878416 B/op 230 allocs/op
|
||||
BenchmarkTunnelTCP/3072-plain-nob14-8 3666 298452 ns/op 10.29 MB/s 3767509 B/op 246 allocs/op
|
||||
BenchmarkTunnelTCP/3072-normal-nob14-8 3450 304848 ns/op 10.08 MB/s 3761811 B/op 246 allocs/op
|
||||
BenchmarkTunnelTCP/3072-plain-b14-8 3549 315641 ns/op 9.73 MB/s 4032830 B/op 291 allocs/op
|
||||
BenchmarkTunnelTCP/3072-normal-b14-8 3440 327234 ns/op 9.39 MB/s 4038470 B/op 292 allocs/op
|
||||
BenchmarkTunnelTCP/3072-preshared-nob14-8 3522 302663 ns/op 10.15 MB/s 3760304 B/op 245 allocs/op
|
||||
BenchmarkTunnelTCP/3072-preshared-b14-8 3390 326384 ns/op 9.41 MB/s 4040489 B/op 293 allocs/op
|
||||
BenchmarkTunnelTCP/4096-plain-nob14-8 2431 435457 ns/op 9.41 MB/s 7515476 B/op 480 allocs/op
|
||||
BenchmarkTunnelTCP/4096-normal-nob14-8 2500 433178 ns/op 9.46 MB/s 7511114 B/op 478 allocs/op
|
||||
BenchmarkTunnelTCP/4096-plain-b14-8 2337 457177 ns/op 8.96 MB/s 8033760 B/op 568 allocs/op
|
||||
BenchmarkTunnelTCP/4096-normal-b14-8 2374 465704 ns/op 8.80 MB/s 8040812 B/op 567 allocs/op
|
||||
BenchmarkTunnelTCP/4096-preshared-nob14-8 2532 436310 ns/op 9.39 MB/s 7510565 B/op 477 allocs/op
|
||||
BenchmarkTunnelTCP/4096-preshared-b14-8 2360 459261 ns/op 8.92 MB/s 8037878 B/op 566 allocs/op
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>TCP MTU 1024</summary>
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/fumiama/WireGold/upper/services/tunnel
|
||||
cpu: Apple M1
|
||||
BenchmarkTunnelTCPSmallMTU/1024-plain-nob14-8 3318 312084 ns/op 3.28 MB/s 3797015 B/op 307 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-normal-nob14-8 4102 303641 ns/op 3.37 MB/s 3795618 B/op 308 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-plain-b14-8 3746 314102 ns/op 3.26 MB/s 4147318 B/op 368 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-normal-b14-8 3609 315252 ns/op 3.25 MB/s 4152014 B/op 368 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-preshared-nob14-8 3826 300693 ns/op 3.41 MB/s 3793725 B/op 304 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/1024-preshared-b14-8 3628 327852 ns/op 3.12 MB/s 4150869 B/op 367 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-plain-nob14-8 3553 315709 ns/op 6.49 MB/s 3945193 B/op 426 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-normal-nob14-8 3254 329794 ns/op 6.21 MB/s 3933224 B/op 427 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-plain-b14-8 3222 357250 ns/op 5.73 MB/s 4538189 B/op 529 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-normal-b14-8 3080 359401 ns/op 5.70 MB/s 4555108 B/op 535 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-preshared-nob14-8 3463 320078 ns/op 6.40 MB/s 3936771 B/op 426 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/2048-preshared-b14-8 2990 363645 ns/op 5.63 MB/s 4555897 B/op 535 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-plain-nob14-8 3228 336736 ns/op 9.12 MB/s 4090750 B/op 550 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-normal-nob14-8 3076 347067 ns/op 8.85 MB/s 4084480 B/op 554 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-plain-b14-8 2798 395353 ns/op 7.77 MB/s 4952186 B/op 700 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-normal-b14-8 2725 403959 ns/op 7.60 MB/s 4965324 B/op 705 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-preshared-nob14-8 3366 344086 ns/op 8.93 MB/s 4080821 B/op 549 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/3072-preshared-b14-8 2797 403142 ns/op 7.62 MB/s 4962100 B/op 703 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-plain-nob14-8 2360 490867 ns/op 8.34 MB/s 7940290 B/op 871 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-normal-nob14-8 2223 486839 ns/op 8.41 MB/s 7927235 B/op 872 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-plain-b14-8 2002 557560 ns/op 7.35 MB/s 9201342 B/op 1087 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-normal-b14-8 1868 564007 ns/op 7.26 MB/s 9216972 B/op 1091 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-preshared-nob14-8 2263 491698 ns/op 8.33 MB/s 7925404 B/op 869 allocs/op
|
||||
BenchmarkTunnelTCPSmallMTU/4096-preshared-b14-8 2050 559663 ns/op 7.32 MB/s 9211292 B/op 1086 allocs/op
|
||||
```
|
||||
</details>
|
||||
@@ -2,37 +2,30 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// EndPoint 一个终结点的信息
|
||||
type EndPoint struct {
|
||||
Host string `yaml:"Host"`
|
||||
Port int64 `yaml:"Port"`
|
||||
Poly uint64 `yaml:"Poly"` // Poly 是 port 随机切换算法的生成多项式, 0 为禁用
|
||||
ReconnectSeconds int64 `yaml:"ReconnectSeconds"` // ReconnectSeconds 断开重连间隔, 每次到时即向对端通报并切换到新的端口, 0 为禁用
|
||||
FECMethod string `yaml:"FECMethod"` // FECMethod 可选 1/2 2/3
|
||||
}
|
||||
|
||||
// Config WireGold 配置文件
|
||||
type Config struct {
|
||||
IP string `yaml:"IP"`
|
||||
SubNet string `yaml:"SubNet"`
|
||||
PrivateKey string `yaml:"PrivateKey"`
|
||||
Network string `yaml:"Network"` // Network udp, tcp or ws (WIP)
|
||||
EndPoint string `yaml:"EndPoint"`
|
||||
MTU int64 `yaml:"MTU"`
|
||||
MTU int64 `yaml:"MTU"` // MTU of nic (will minus packet header len)
|
||||
SpeedLoop uint16 `yaml:"SpeedLoop"`
|
||||
Mask uint64 `yaml:"Mask"` // Mask 是异或报文所用掩码, 必须保证各端统一
|
||||
Mask uint64 `yaml:"Mask"` // Mask 是异或报文所用掩码, 必须保证各端统一
|
||||
Base14 bool `yaml:"Base14"` // Base14 是否将最终报文进行 base16384 编码后再发送
|
||||
MaxTTL uint8 `yaml:"MaxTTL"` // MaxTTL 默认 64
|
||||
Peers []Peer `yaml:"Peers"`
|
||||
}
|
||||
|
||||
// Peer 对端信息
|
||||
type Peer struct {
|
||||
IP string `yaml:"IP"`
|
||||
SubNet string `yaml:"SubNet"`
|
||||
PublicKey string `yaml:"PublicKey"`
|
||||
PresharedKey string `yaml:"PresharedKey"`
|
||||
EndPoint string `yaml:"EndPoint"`
|
||||
@@ -42,7 +35,8 @@ type Peer struct {
|
||||
QuerySeconds int64 `yaml:"QuerySeconds"`
|
||||
AllowTrans bool `yaml:"AllowTrans"`
|
||||
UseZstd bool `yaml:"UseZstd"`
|
||||
MTU int64 `yaml:"MTU"`
|
||||
DoublePacket bool `yaml:"DoublePacket"`
|
||||
MTU int64 `yaml:"MTU"` // MTU of PDU passed to p2p
|
||||
MTURandomRange int64 `yaml:"MTURandomRange"`
|
||||
}
|
||||
|
||||
@@ -50,11 +44,11 @@ type Peer struct {
|
||||
func Parse(path string) (c Config) {
|
||||
file, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatal("open config file failed:", err)
|
||||
logrus.Fatal("open config file failed:", err)
|
||||
}
|
||||
err = yaml.NewDecoder(bytes.NewReader(file)).Decode(&c)
|
||||
if err != nil {
|
||||
log.Fatal("invalid config file:", err)
|
||||
logrus.Fatal("invalid config file:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
16
config/global.go
Normal file
16
config/global.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package config
|
||||
|
||||
import "time"
|
||||
|
||||
// ShowDebugLog turn on to print verbose logs.
|
||||
const ShowDebugLog = false
|
||||
|
||||
// Version will show in help message to distinguish different builds.
|
||||
// Use -ldflags="-X github.com/fumiama/WireGold/config.Version=x.y.z" to override.
|
||||
var Version = "dev"
|
||||
|
||||
func init() {
|
||||
if Version == "dev" {
|
||||
Version = "dev-" + time.Now().Format(time.DateOnly)
|
||||
}
|
||||
}
|
||||
42
docker-entrypoint.sh
Normal file
42
docker-entrypoint.sh
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
set -- /usr/local/bin/wg -c /config/config.yaml
|
||||
elif [ "${1#-}" != "$1" ]; then
|
||||
set -- /usr/local/bin/wg "$@"
|
||||
fi
|
||||
|
||||
if [ "${1:-}" = "/usr/local/bin/wg" ]; then
|
||||
need_config=1
|
||||
config_path=/config/config.yaml
|
||||
prev=
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-g|-pg|-h)
|
||||
need_config=0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$prev" = "-c" ]; then
|
||||
config_path="$arg"
|
||||
fi
|
||||
prev="$arg"
|
||||
done
|
||||
|
||||
if [ "$need_config" -eq 1 ] && [ ! -c /dev/net/tun ]; then
|
||||
echo "WireGold requires /dev/net/tun inside the container." >&2
|
||||
echo "Run with: --device /dev/net/tun --cap-add NET_ADMIN" >&2
|
||||
echo "If Network is icmp or ip, add --cap-add NET_RAW as well." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$need_config" -eq 1 ] && [ ! -f "$config_path" ]; then
|
||||
echo "WireGold config not found: $config_path" >&2
|
||||
echo "Mount your config into /config or override -c." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
21
go.mod
21
go.mod
@@ -1,21 +1,24 @@
|
||||
module github.com/fumiama/WireGold
|
||||
|
||||
go 1.20
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1
|
||||
github.com/fumiama/blake2b-simd v0.0.0-20220412110131-4481822068bb
|
||||
github.com/fumiama/go-base16384 v1.7.0
|
||||
github.com/FloatTech/ttl v0.0.0-20260412050038-bd89b9d66fcd
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
|
||||
github.com/fumiama/blake2b-simd v0.0.0-20250228045919-a5dcaba5419a
|
||||
github.com/fumiama/go-base16384 v1.7.1
|
||||
github.com/fumiama/go-x25519 v1.0.0
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5
|
||||
github.com/fumiama/water v0.0.0-20211231134027-da391938d6ac
|
||||
github.com/klauspost/compress v1.17.9
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
golang.org/x/crypto v0.25.0
|
||||
github.com/klauspost/compress v1.18.5
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
golang.org/x/crypto v0.50.0
|
||||
golang.org/x/net v0.53.0
|
||||
golang.org/x/sys v0.43.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fumiama/wintun v0.0.0-20211229152851-8bc97c8034c0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
)
|
||||
|
||||
44
go.sum
44
go.sum
@@ -1,43 +1,49 @@
|
||||
github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1 h1:g4pTnDJUW4VbJ9NvoRfUvdjDrHz/6QhfN/LoIIpICbo=
|
||||
github.com/FloatTech/ttl v0.0.0-20230307105452-d6f7b2b647d1/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/FloatTech/ttl v0.0.0-20260412050038-bd89b9d66fcd h1:Dt86dtJ5hPFvDIA8cAFYfO+Vc8ikoGf32ndEhbwjYXM=
|
||||
github.com/FloatTech/ttl v0.0.0-20260412050038-bd89b9d66fcd/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fumiama/blake2b-simd v0.0.0-20220412110131-4481822068bb h1:Gd7gIiR68ErTWtxnl/PCycMog6IRdK1ApxrvoAy0lFY=
|
||||
github.com/fumiama/blake2b-simd v0.0.0-20220412110131-4481822068bb/go.mod h1:Olmv2uLdFllRsvwhzOvG/O/Nvgzg0ViokUL4+hiaRSE=
|
||||
github.com/fumiama/go-base16384 v1.7.0 h1:6fep7XPQWxRlh4Hu+KsdH+6+YdUp+w6CwRXtMWSsXCA=
|
||||
github.com/fumiama/go-base16384 v1.7.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/blake2b-simd v0.0.0-20250228045919-a5dcaba5419a h1:hhCWoHNGDATjUitm8rKwrf5xRnuxO7P6UHIusLXuqag=
|
||||
github.com/fumiama/blake2b-simd v0.0.0-20250228045919-a5dcaba5419a/go.mod h1:Olmv2uLdFllRsvwhzOvG/O/Nvgzg0ViokUL4+hiaRSE=
|
||||
github.com/fumiama/go-base16384 v1.7.1 h1:1P1x6FWRvd7PtbH4idDAGWAjKKcVxggxlROYKRXbw58=
|
||||
github.com/fumiama/go-base16384 v1.7.1/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/go-x25519 v1.0.0 h1:hiGg9EhseVmGCc8T1jECVkj8Keu/aJ1ZK05RM8Vuavo=
|
||||
github.com/fumiama/go-x25519 v1.0.0/go.mod h1:8VOhfyGZzw4IUs4nCjQFqW9cA3V/QpSCtP3fo2dLNg4=
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 h1:j9o0XVvdAeLwrBYMnh0SerrMc9CgNU6AGszbsvFzoc0=
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5/go.mod h1:FOjdw7KdCbK2eH3gRPhwFNCoXKpu9sN5vPH4El/8e0c=
|
||||
github.com/fumiama/water v0.0.0-20211231134027-da391938d6ac h1:A/5A0rODsg+EQHH61Ew5mMUtDpRXaSNqHhPvW+fN4C4=
|
||||
github.com/fumiama/water v0.0.0-20211231134027-da391938d6ac/go.mod h1:BBnNY9PwK+UUn4trAU+H0qsMEypm7+3Bj1bVFuJItlo=
|
||||
github.com/fumiama/wintun v0.0.0-20211229152851-8bc97c8034c0 h1:WfrSFlIlCAtg6Rt2IGna0HhJYSDE45YVHiYqO4wwsEw=
|
||||
github.com/fumiama/wintun v0.0.0-20211229152851-8bc97c8034c0/go.mod h1:dPOG7Af/ArO62RgBz2JJTNFByBn/IXWLo/1kZKcLSe8=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
89
gold/head/box.go
Normal file
89
gold/head/box.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package head
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"unsafe"
|
||||
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/internal/algo"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
)
|
||||
|
||||
// PreCRC64 calculate crc64 checksum without idxdatsz.
|
||||
func (p *Packet) PreCRC64() (crc uint64) {
|
||||
w := bin.SelectWriter()
|
||||
// 固定 TTL 为 0, flag 为空 计算
|
||||
if bin.IsLittleEndian {
|
||||
ttl := p.TTL
|
||||
f := p.Proto
|
||||
p.TTL = 0
|
||||
p.Proto &= protobit
|
||||
w.Write((*[PacketHeadNoCRCLen]byte)(
|
||||
(unsafe.Pointer)(p),
|
||||
)[:])
|
||||
p.TTL = ttl
|
||||
p.Proto = f
|
||||
} else {
|
||||
w.WriteUInt32(p.idxdatsz)
|
||||
w.WriteUInt32(uint32(p.randn))
|
||||
w.WriteUInt16(uint16(p.Proto & protobit)) // TTL, flags is set to 0
|
||||
w.WriteUInt16(p.SrcPort)
|
||||
w.WriteUInt16(p.DstPort)
|
||||
w.WriteUInt16(p.Offset)
|
||||
w.Write(p.src[:])
|
||||
w.Write(p.dst[:])
|
||||
}
|
||||
w.P(func(b *pbuf.Buffer) {
|
||||
crc = algo.MD5Hash8(b.Bytes()[PacketHeadPreCRCIdx:])
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugf(
|
||||
"[box] calc pre-crc64 %016x, dat %s", crc,
|
||||
hex.EncodeToString(b.Bytes()[PacketHeadPreCRCIdx:]),
|
||||
)
|
||||
}
|
||||
})
|
||||
w.Destroy()
|
||||
return
|
||||
}
|
||||
|
||||
// WriteHeaderTo write header bytes to buf
|
||||
// with crc64 checksum.
|
||||
func (p *Packet) WriteHeaderTo(buf *bytes.Buffer) {
|
||||
// 固定 TTL 为 0 计算
|
||||
if bin.IsLittleEndian {
|
||||
buf.Write((*[PacketHeadNoCRCLen]byte)(
|
||||
(unsafe.Pointer)(p),
|
||||
)[:])
|
||||
b := make([]byte, buf.Len())
|
||||
copy(b, buf.Bytes())
|
||||
ClearTTL(b)
|
||||
p.md5h8 = algo.MD5Hash8(b)
|
||||
_ = binary.Write(buf, binary.LittleEndian, p.md5h8)
|
||||
return
|
||||
}
|
||||
w := bin.SelectWriter()
|
||||
w.WriteUInt32(p.idxdatsz)
|
||||
w.WriteUInt32(uint32(p.randn))
|
||||
w.WriteUInt16((uint16(p.TTL) << 8) | uint16(p.Proto))
|
||||
w.WriteUInt16(p.SrcPort)
|
||||
w.WriteUInt16(p.DstPort)
|
||||
w.WriteUInt16(p.Offset)
|
||||
w.Write(p.src[:])
|
||||
w.Write(p.dst[:])
|
||||
w.P(func(buf *pbuf.Buffer) {
|
||||
b := make([]byte, buf.Len())
|
||||
copy(b, buf.Bytes())
|
||||
ClearTTL(b)
|
||||
p.md5h8 = algo.MD5Hash8(b)
|
||||
})
|
||||
w.WriteUInt64(p.md5h8)
|
||||
w.P(func(b *pbuf.Buffer) {
|
||||
_, _ = buf.ReadFrom(b)
|
||||
})
|
||||
w.Destroy()
|
||||
}
|
||||
249
gold/head/builder.go
Normal file
249
gold/head/builder.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package head
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/internal/algo"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
type (
|
||||
HeaderBuilder PacketItem
|
||||
DataBuilder PacketItem
|
||||
PacketBuilder PacketItem
|
||||
)
|
||||
|
||||
func NewPacketBuilder() *HeaderBuilder {
|
||||
p := selectPacket()
|
||||
p.P(func(ub *PacketBuf) {
|
||||
err := binary.Read(
|
||||
rand.Reader, binary.LittleEndian, &ub.DAT.randn,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
return (*HeaderBuilder)(p)
|
||||
}
|
||||
|
||||
func (pb *HeaderBuilder) p(f func(*PacketBuf)) *HeaderBuilder {
|
||||
(*PacketItem)(pb).P(f)
|
||||
return pb
|
||||
}
|
||||
|
||||
func (pb *HeaderBuilder) Proto(proto uint8) *HeaderBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
ub.DAT.Proto |= FlagsProto(proto) & protobit
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *HeaderBuilder) TTL(ttl uint8) *HeaderBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
ub.DAT.TTL = ttl
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *HeaderBuilder) Src(ip net.IP, p uint16) *HeaderBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
copy(ub.DAT.src[:], ip.To4())
|
||||
ub.DAT.SrcPort = p
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *HeaderBuilder) Dst(ip net.IP, p uint16) *HeaderBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
copy(ub.DAT.dst[:], ip.To4())
|
||||
ub.DAT.DstPort = p
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *HeaderBuilder) With(data []byte) *DataBuilder {
|
||||
return (*DataBuilder)(pb.p(func(ub *PacketBuf) {
|
||||
// header crc64 except idxdatasz
|
||||
ub.DAT.md5h8 = ub.DAT.PreCRC64()
|
||||
// plain data
|
||||
ub.Buffer.Write(data)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln(file.Header(), strconv.FormatUint(ub.DAT.md5h8, 16), "build with data", file.ToLimitHexString(data, 64))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func (pb *DataBuilder) p(f func(*PacketBuf)) *DataBuilder {
|
||||
(*PacketItem)(pb).P(f)
|
||||
return pb
|
||||
}
|
||||
|
||||
func (pb *DataBuilder) Zstd() *DataBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
data := algo.EncodeZstd(ub.Bytes())
|
||||
ub.Reset()
|
||||
ub.Write(data)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln(file.Header(), strconv.FormatUint(ub.DAT.md5h8, 16), "data after zstd", file.ToLimitHexString(ub.Bytes(), 64))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *DataBuilder) Hash() *DataBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
ub.DAT.hashrem = int64(algo.Blake2bHash8(
|
||||
ub.DAT.md5h8, ub.Bytes(),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *DataBuilder) tea(typ uint8) *DataBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
ub.DAT.idxdatsz |= (uint32(typ) << 27)
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *DataBuilder) additional(additional uint16) *DataBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
ub.DAT.idxdatsz |= (uint32(additional&0x07ff) << 16)
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *DataBuilder) Seal(aead cipher.AEAD, teatyp uint8, additional uint16) *PacketBuilder {
|
||||
return (*PacketBuilder)(pb.tea(teatyp).additional(additional).
|
||||
p(func(ub *PacketBuf) {
|
||||
// encrypted data: chacha20(hash + plain)
|
||||
w := bin.SelectWriter()
|
||||
w.WriteUInt64(uint64(ub.DAT.hashrem))
|
||||
w.Write(ub.Bytes())
|
||||
w.P(func(b *pbuf.Buffer) {
|
||||
data := algo.EncodeAEAD(aead, additional, b.Bytes())
|
||||
ub.Reset()
|
||||
ub.Write(data)
|
||||
})
|
||||
w.Destroy()
|
||||
}))
|
||||
}
|
||||
|
||||
func (pb *DataBuilder) Plain(teatyp uint8, additional uint16) *PacketBuilder {
|
||||
return (*PacketBuilder)(pb.tea(teatyp).additional(additional).
|
||||
p(func(ub *PacketBuf) {
|
||||
w := bin.SelectWriter()
|
||||
w.WriteUInt64(uint64(ub.DAT.hashrem))
|
||||
w.Write(ub.Bytes())
|
||||
w.P(func(b *pbuf.Buffer) {
|
||||
ub.Reset()
|
||||
_, _ = ub.ReadFrom(b)
|
||||
})
|
||||
w.Destroy()
|
||||
}))
|
||||
}
|
||||
|
||||
func (pb *DataBuilder) Trans(teatyp uint8, additional uint16) *PacketBuilder {
|
||||
return (*PacketBuilder)(pb.tea(teatyp).additional(additional))
|
||||
}
|
||||
|
||||
func (pb *PacketBuilder) copy() *PacketBuilder {
|
||||
return (*PacketBuilder)((*PacketItem)(pb).Copy())
|
||||
}
|
||||
|
||||
func (pb *PacketBuilder) p(f func(*PacketBuf)) *PacketBuilder {
|
||||
(*PacketItem)(pb).P(f)
|
||||
return pb
|
||||
}
|
||||
|
||||
// datasize fill encrypted datasize by calling data.Len().
|
||||
func (pb *PacketBuilder) datasize() *PacketBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
l := uint32(ub.Len()) & 0xffff
|
||||
ub.DAT.idxdatsz |= l
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *PacketBuilder) noFrag(on bool) *PacketBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
if on {
|
||||
ub.DAT.Proto |= nofragbit
|
||||
} else {
|
||||
ub.DAT.Proto &= ^nofragbit
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *PacketBuilder) hasMore(on bool) *PacketBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
if on {
|
||||
ub.DAT.Proto |= hasmorebit
|
||||
} else {
|
||||
ub.DAT.Proto &= ^hasmorebit
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (pb *PacketBuilder) offset(off uint16) *PacketBuilder {
|
||||
return pb.p(func(ub *PacketBuf) {
|
||||
ub.DAT.Offset = off
|
||||
})
|
||||
}
|
||||
|
||||
// Split mtu based on the total len, which includes
|
||||
// header and body and padding after outer xor.
|
||||
func (pb *PacketBuilder) Split(mtu int, nofrag bool) (pbs []PacketBytes) {
|
||||
pb.datasize().p(func(ub *PacketBuf) {
|
||||
bodylen := ub.Len()
|
||||
datalen := bodylen + int(PacketHeadLen)
|
||||
udplen := algo.EncodeXORLen(datalen)
|
||||
if udplen <= mtu { // can be sent in a single packet
|
||||
pbs = []PacketBytes{
|
||||
pbuf.BufferItemToBytes((*PacketItem)(
|
||||
pb.copy().noFrag(nofrag).hasMore(false).offset(0),
|
||||
)).Ignore(),
|
||||
}
|
||||
return
|
||||
}
|
||||
if nofrag { // drop oversized packet
|
||||
return
|
||||
}
|
||||
pb.noFrag(false).hasMore(true)
|
||||
datalim := mtu - 9 - int(PacketHeadLen)
|
||||
n := bodylen / datalim
|
||||
r := bodylen % datalim
|
||||
if r > 0 {
|
||||
n++
|
||||
}
|
||||
pbs = make([]PacketBytes, n)
|
||||
for i := 0; i < n; i++ {
|
||||
a, b := i*datalim, (i+1)*datalim
|
||||
if b > bodylen {
|
||||
b = bodylen
|
||||
}
|
||||
pbs[i] = pbuf.BufferItemToBytes((*PacketItem)(
|
||||
pb.copy().offset(uint16(i*datalim)),
|
||||
)).Ignore().Slice(a, b)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Destroy call this once no one use it.
|
||||
func (pb *PacketBuilder) Destroy() {
|
||||
(*PacketItem)(pb).ManualDestroy()
|
||||
}
|
||||
|
||||
func BuildPacketFromBytes(pb PacketBytes) pbuf.Bytes {
|
||||
w := bin.SelectWriter()
|
||||
pb.B(func(_ []byte, p *Packet) {
|
||||
w.P(func(b *pbuf.Buffer) {
|
||||
p.WriteHeaderTo(&b.Buffer)
|
||||
})
|
||||
})
|
||||
pb.V(func(b []byte) {
|
||||
w.Write(b)
|
||||
})
|
||||
return w.ToBytes()
|
||||
}
|
||||
37
gold/head/flags.go
Normal file
37
gold/head/flags.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package head
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
hasmorebit FlagsProto = 0x20 << iota
|
||||
nofragbit
|
||||
topbit
|
||||
)
|
||||
|
||||
const (
|
||||
impossiblebit = hasmorebit | nofragbit
|
||||
flagsbit = topbit | impossiblebit
|
||||
protobit = ^flagsbit
|
||||
)
|
||||
|
||||
type FlagsProto uint8
|
||||
|
||||
func (pf FlagsProto) String() string {
|
||||
return fmt.Sprintf("%02x", uint8(pf))
|
||||
}
|
||||
|
||||
func (pf FlagsProto) IsValid() bool {
|
||||
return pf&topbit == 0 &&
|
||||
pf&impossiblebit != impossiblebit &&
|
||||
pf.Proto() < ProtoTop
|
||||
}
|
||||
|
||||
func (pf FlagsProto) HasMore() bool {
|
||||
return pf&hasmorebit != 0
|
||||
}
|
||||
|
||||
func (pf FlagsProto) NoFrag() bool {
|
||||
return pf&nofragbit != 0
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package head
|
||||
|
||||
// Notify 是 map[peerip]endpoint
|
||||
type Notify = map[string]string
|
||||
|
||||
// Query 是 peerips 组成的数组
|
||||
type Query = []string
|
||||
@@ -1,203 +1,78 @@
|
||||
package head
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"hash/crc64"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
blake2b "github.com/fumiama/blake2b-simd"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/fumiama/orbyte"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
)
|
||||
|
||||
type PacketFlags uint16
|
||||
const (
|
||||
// PacketHeadPreCRCIdx skip idxdatsz, which will be set at Seal().
|
||||
PacketHeadPreCRCIdx = unsafe.Offsetof(Packet{}.randn)
|
||||
// PacketHeadNoCRCLen without final crc
|
||||
PacketHeadNoCRCLen = unsafe.Offsetof(Packet{}.md5h8)
|
||||
PacketHeadLen = unsafe.Offsetof(Packet{}.hashrem)
|
||||
)
|
||||
|
||||
func (pf PacketFlags) IsValid() bool {
|
||||
return pf&0x8000 == 0
|
||||
}
|
||||
var (
|
||||
ErrBadCRCChecksum = errors.New("bad crc checksum")
|
||||
ErrDataLenLEHeader = errors.New("data len <= header len")
|
||||
ErrInvalidOffset = errors.New("invalid offset")
|
||||
)
|
||||
|
||||
func (pf PacketFlags) DontFrag() bool {
|
||||
return pf&0x4000 == 0x4000
|
||||
}
|
||||
|
||||
func (pf PacketFlags) NoFrag() bool {
|
||||
return pf == 0x4000
|
||||
}
|
||||
|
||||
func (pf PacketFlags) IsSingle() bool {
|
||||
return pf == 0
|
||||
}
|
||||
|
||||
func (pf PacketFlags) ZeroOffset() bool {
|
||||
return pf&0x1fff == 0
|
||||
}
|
||||
|
||||
func (pf PacketFlags) Offset() uint16 {
|
||||
return uint16(pf << 3)
|
||||
}
|
||||
|
||||
// Flags extract flags from raw data
|
||||
func Flags(data []byte) PacketFlags {
|
||||
return PacketFlags(binary.LittleEndian.Uint16(data[10:12]))
|
||||
}
|
||||
type (
|
||||
PacketBuf = pbuf.UserBuffer[Packet]
|
||||
PacketItem = orbyte.Item[PacketBuf]
|
||||
PacketBytes = pbuf.UserBytes[Packet]
|
||||
)
|
||||
|
||||
// Packet 是发送和接收的最小单位
|
||||
type Packet struct {
|
||||
// idxdatsz len(Data)
|
||||
// idxdatsz
|
||||
//
|
||||
// idx
|
||||
// 高 5 位指定加密所用 key index
|
||||
// 高 5-16 位是递增值, 用于 xchacha20 验证 additionalData
|
||||
//
|
||||
// datsz
|
||||
// 不得超过 65507-head 字节
|
||||
idxdatsz uint32
|
||||
// Proto 详见 head
|
||||
Proto uint8
|
||||
// randn
|
||||
// 在发送报文时填入随机值.
|
||||
randn int32
|
||||
// Proto 高3位为标志(xDM),低5位为协议类型
|
||||
Proto FlagsProto
|
||||
// TTL is time to live
|
||||
TTL uint8
|
||||
// SrcPort 源端口
|
||||
SrcPort uint16
|
||||
// DstPort 目的端口
|
||||
DstPort uint16
|
||||
// Flags 高3位为标志(xDM),低13位为分片偏移
|
||||
Flags PacketFlags
|
||||
// Src 源 ip (ipv4)
|
||||
Src net.IP
|
||||
// Dst 目的 ip (ipv4)
|
||||
Dst net.IP
|
||||
// Hash 使用 BLAKE2 生成加密前 Packet 的摘要
|
||||
// 生成时 Hash 全 0
|
||||
// Offset 分片偏移量
|
||||
Offset uint16
|
||||
// src 源 ip (ipv4)
|
||||
src [4]byte
|
||||
// dst 目的 ip (ipv4)
|
||||
dst [4]byte
|
||||
// md5h8 发送时记录包头字段除自身外的 checksum 值.
|
||||
//
|
||||
// 可以认为在一定时间内唯一 (现已更改算法为 md5 但名字未变)。
|
||||
md5h8 uint64
|
||||
|
||||
// 以下字段为包体, 与 data 一起加密
|
||||
|
||||
// hashrem 使用 BLAKE2B 生成加密前 packet data+crc64 的摘要,
|
||||
// 取其前 8 字节, 小端序读写. 接收时记录剩余字节数.
|
||||
//
|
||||
// https://github.com/fumiama/blake2b-simd
|
||||
Hash [32]byte
|
||||
// crc64 包头字段的 checksum 值,可以认为在一定时间内唯一
|
||||
crc64 uint64
|
||||
// Data 承载的数据
|
||||
Data []byte
|
||||
// 记录还有多少字节未到达
|
||||
rembytes int
|
||||
}
|
||||
hashrem int64
|
||||
|
||||
// NewPacket 生成一个新包
|
||||
func NewPacket(proto uint8, srcPort uint16, dst net.IP, dstPort uint16, data []byte) (p *Packet) {
|
||||
// logrus.Debugln("[packet] new: [proto:", proto, ", srcport:", srcPort, ", dstport:", dstPort, ", dst:", dst, ", data:", data)
|
||||
p = SelectPacket()
|
||||
p.Proto = proto
|
||||
p.TTL = 16
|
||||
p.SrcPort = srcPort
|
||||
p.DstPort = dstPort
|
||||
p.Dst = dst
|
||||
p.Data = data
|
||||
return
|
||||
}
|
||||
|
||||
// Unmarshal 将 data 的数据解码到自身
|
||||
func (p *Packet) Unmarshal(data []byte) (complete bool, err error) {
|
||||
if len(data) < 60 {
|
||||
err = errors.New("data len < 60")
|
||||
return
|
||||
}
|
||||
p.crc64 = binary.LittleEndian.Uint64(data[52:60])
|
||||
if crc64.Checksum(data[:52], crc64.MakeTable(crc64.ISO)) != p.crc64 {
|
||||
err = errors.New("bad crc checksum")
|
||||
return
|
||||
}
|
||||
|
||||
sz := p.idxdatsz & 0x0000ffff
|
||||
if sz == 0 && len(p.Data) == 0 {
|
||||
p.idxdatsz = binary.LittleEndian.Uint32(data[:4])
|
||||
sz = p.idxdatsz & 0x0000ffff
|
||||
if int(sz)+52 == len(data) {
|
||||
p.Data = data[52:]
|
||||
p.rembytes = 0
|
||||
} else {
|
||||
p.Data = make([]byte, sz)
|
||||
p.rembytes = int(sz)
|
||||
}
|
||||
pt := binary.LittleEndian.Uint16(data[4:6])
|
||||
p.Proto = uint8(pt)
|
||||
p.TTL = uint8(pt >> 8)
|
||||
p.SrcPort = binary.LittleEndian.Uint16(data[6:8])
|
||||
p.DstPort = binary.LittleEndian.Uint16(data[8:10])
|
||||
}
|
||||
|
||||
flags := PacketFlags(binary.LittleEndian.Uint16(data[10:12]))
|
||||
|
||||
if flags.ZeroOffset() {
|
||||
p.Flags = flags
|
||||
p.Src = make(net.IP, 4)
|
||||
copy(p.Src, data[12:16])
|
||||
p.Dst = make(net.IP, 4)
|
||||
copy(p.Dst, data[16:20])
|
||||
copy(p.Hash[:], data[20:52])
|
||||
}
|
||||
|
||||
if p.rembytes > 0 {
|
||||
p.rembytes -= copy(p.Data[flags.Offset():], data[60:])
|
||||
logrus.Debugln("[packet] copied frag", hex.EncodeToString(p.Hash[:]), "rembytes:", p.rembytes)
|
||||
}
|
||||
|
||||
complete = p.rembytes == 0
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Marshal 将自身数据编码为 []byte
|
||||
// offset 必须为 8 的倍数,表示偏移的 8 位
|
||||
func (p *Packet) Marshal(src net.IP, teatype uint8, additional uint16, datasz uint32, offset uint16, dontfrag, hasmore bool) ([]byte, func()) {
|
||||
p.TTL--
|
||||
if p.TTL == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if src != nil {
|
||||
p.idxdatsz = (uint32(teatype) << 27) | (uint32(additional&0x07ff) << 16) | datasz&0xffff
|
||||
p.Src = src
|
||||
offset &= 0x1fff
|
||||
if dontfrag {
|
||||
offset |= 0x4000
|
||||
}
|
||||
if hasmore {
|
||||
offset |= 0x2000
|
||||
}
|
||||
p.Flags = PacketFlags(offset)
|
||||
}
|
||||
|
||||
return helper.OpenWriterF(func(w *helper.Writer) {
|
||||
w.WriteUInt32(p.idxdatsz)
|
||||
w.WriteUInt16((uint16(p.TTL) << 8) | uint16(p.Proto))
|
||||
w.WriteUInt16(p.SrcPort)
|
||||
w.WriteUInt16(p.DstPort)
|
||||
w.WriteUInt16(uint16(p.Flags))
|
||||
w.Write(p.Src.To4())
|
||||
w.Write(p.Dst.To4())
|
||||
w.Write(p.Hash[:])
|
||||
w.WriteUInt64(crc64.Checksum(w.Bytes(), crc64.MakeTable(crc64.ISO)))
|
||||
w.Write(p.Data)
|
||||
})
|
||||
}
|
||||
|
||||
// FillHash 生成 p.Data 的 Hash
|
||||
func (p *Packet) FillHash() {
|
||||
h := blake2b.New256()
|
||||
_, err := h.Write(p.Data)
|
||||
if err != nil {
|
||||
logrus.Error("[packet] err when fill hash:", err)
|
||||
return
|
||||
}
|
||||
logrus.Debugln("[packet] sum calulated:", hex.EncodeToString(h.Sum(p.Hash[:0])))
|
||||
}
|
||||
|
||||
// IsVaildHash 验证 packet 合法性
|
||||
func (p *Packet) IsVaildHash() bool {
|
||||
h := blake2b.New256()
|
||||
_, err := h.Write(p.Data)
|
||||
if err != nil {
|
||||
logrus.Error("[packet] err when check hash:", err)
|
||||
return false
|
||||
}
|
||||
var sum [32]byte
|
||||
logrus.Debugln("[packet] sum calulated:", hex.EncodeToString(h.Sum(sum[:0])))
|
||||
logrus.Debugln("[packet] sum in packet:", hex.EncodeToString(p.Hash[:]))
|
||||
return sum == p.Hash
|
||||
// Buffer 用于 builder with 暂存原始包体数据
|
||||
// 以及接收时保存 body, 通过 PacketBytes 截取偏移.
|
||||
}
|
||||
|
||||
// AdditionalData 获得 packet 的 additionalData
|
||||
@@ -211,11 +86,23 @@ func (p *Packet) CipherIndex() uint8 {
|
||||
}
|
||||
|
||||
// Len is packet size
|
||||
func (p *Packet) Len() int {
|
||||
func (p *Packet) Size() int {
|
||||
return int(p.idxdatsz & 0xffff)
|
||||
}
|
||||
|
||||
// Put 将自己放回池中
|
||||
func (p *Packet) Put() {
|
||||
PutPacket(p)
|
||||
// CRC64 extract md5h8rem field
|
||||
func (p *Packet) CRC64() uint64 {
|
||||
return p.md5h8
|
||||
}
|
||||
|
||||
func (p *Packet) Src() net.IP {
|
||||
return append(net.IP{}, p.src[:]...)
|
||||
}
|
||||
|
||||
func (p *Packet) Dst() net.IP {
|
||||
return append(net.IP{}, p.dst[:]...)
|
||||
}
|
||||
|
||||
func (p *Packet) HasFinished() bool {
|
||||
return atomic.LoadInt64(&p.hashrem) <= 0
|
||||
}
|
||||
|
||||
@@ -1,25 +1,157 @@
|
||||
package head
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
crand "crypto/rand"
|
||||
"encoding/hex"
|
||||
"math/rand"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/fumiama/WireGold/internal/algo"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
)
|
||||
|
||||
func TestBuilderNative(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(4096)
|
||||
for i := 0; i < 4096; i++ {
|
||||
go func(i int) {
|
||||
defer runtime.GC()
|
||||
defer wg.Done()
|
||||
dat := BuildPacketFromBytes(NewPacketBuilder().Proto(3).TTL(0xff).
|
||||
Src(net.IPv4(1, 2, 3, 4), 5).Dst(net.IPv4(6, 7, 8, 9), 10).
|
||||
With([]byte("0123456789")).Hash().Plain(0x12, 0x0345).
|
||||
Split(16384, false)[0]).Trans()
|
||||
s := hex.EncodeToString(dat)
|
||||
if s[:8] != "12004593" {
|
||||
panic(s[:8])
|
||||
}
|
||||
if s[16:48] != "03ff05000a0000000102030406070809" {
|
||||
panic(s[16:48])
|
||||
}
|
||||
if s[80:] != "30313233343536373839" {
|
||||
panic(s[80:])
|
||||
}
|
||||
p, err := ParsePacketHeader(dat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
p.B(func(buf []byte, p *Packet) {
|
||||
ok := p.WriteDataSegment(dat, buf)
|
||||
if !ok {
|
||||
panic(i)
|
||||
}
|
||||
if !algo.IsVaildBlake2bHash8(p.PreCRC64(), buf) {
|
||||
panic(i)
|
||||
}
|
||||
if p.Proto != 3 {
|
||||
panic(i)
|
||||
}
|
||||
if p.CipherIndex() != 0x12 {
|
||||
panic(i)
|
||||
}
|
||||
if p.SrcPort != 5 {
|
||||
panic(i)
|
||||
}
|
||||
if p.DstPort != 10 {
|
||||
panic(i)
|
||||
}
|
||||
if !bytes.Equal(p.src[:], net.IPv4(1, 2, 3, 4).To4()) {
|
||||
panic(i)
|
||||
}
|
||||
if !bytes.Equal(p.dst[:], net.IPv4(6, 7, 8, 9).To4()) {
|
||||
panic(i)
|
||||
}
|
||||
if p.AdditionalData() != 0x0345 {
|
||||
panic(i)
|
||||
}
|
||||
})
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestBuilderBE(t *testing.T) {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(4096)
|
||||
bin.IsLittleEndian = false
|
||||
for i := 0; i < 4096; i++ {
|
||||
go func(i int) {
|
||||
defer runtime.GC()
|
||||
defer wg.Done()
|
||||
dat := BuildPacketFromBytes(NewPacketBuilder().Proto(3).TTL(0xff).
|
||||
Src(net.IPv4(1, 2, 3, 4), 5).Dst(net.IPv4(6, 7, 8, 9), 10).
|
||||
With([]byte("0123456789")).Hash().Plain(0x12, 0x0345).
|
||||
Split(16384, false)[0]).Trans()
|
||||
s := hex.EncodeToString(dat)
|
||||
if s[:8] != "12004593" {
|
||||
panic(s[:8])
|
||||
}
|
||||
if s[16:48] != "03ff05000a0000000102030406070809" {
|
||||
panic(s[16:48])
|
||||
}
|
||||
if s[80:] != "30313233343536373839" {
|
||||
panic(s[80:])
|
||||
}
|
||||
p, err := ParsePacketHeader(dat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
p.B(func(buf []byte, p *Packet) {
|
||||
ok := p.WriteDataSegment(dat, buf)
|
||||
if !ok {
|
||||
panic(i)
|
||||
}
|
||||
if !algo.IsVaildBlake2bHash8(p.PreCRC64(), buf) {
|
||||
panic(i)
|
||||
}
|
||||
if p.Proto != 3 {
|
||||
panic(i)
|
||||
}
|
||||
if p.CipherIndex() != 0x12 {
|
||||
panic(i)
|
||||
}
|
||||
if p.SrcPort != 5 {
|
||||
panic(i)
|
||||
}
|
||||
if p.DstPort != 10 {
|
||||
panic(i)
|
||||
}
|
||||
if !bytes.Equal(p.src[:], net.IPv4(1, 2, 3, 4).To4()) {
|
||||
panic(i)
|
||||
}
|
||||
if !bytes.Equal(p.dst[:], net.IPv4(6, 7, 8, 9).To4()) {
|
||||
panic(i)
|
||||
}
|
||||
if p.AdditionalData() != 0x0345 {
|
||||
panic(i)
|
||||
}
|
||||
})
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshal(t *testing.T) {
|
||||
// logrus.SetLevel(logrus.DebugLevel)
|
||||
data := make([]byte, 4096)
|
||||
_, err := crand.Read(data)
|
||||
n, err := crand.Read(data)
|
||||
if n != 4096 {
|
||||
t.Fatal("unexpected")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 0x7ff; i++ {
|
||||
proto := uint8(rand.Intn(255))
|
||||
for i := 0; i < 4096; i++ {
|
||||
proto := uint8(rand.Intn(int(ProtoTop)))
|
||||
teatype := uint8(rand.Intn(32))
|
||||
srcPort := uint16(rand.Intn(65535))
|
||||
dstPort := uint16(rand.Intn(65535))
|
||||
src := make(net.IP, 4)
|
||||
_, err = crand.Read(src)
|
||||
_, err := crand.Read(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -28,41 +160,47 @@ func TestMarshalUnmarshal(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p := NewPacket(proto, srcPort, dst, dstPort, data)
|
||||
p.FillHash()
|
||||
d, cl := p.Marshal(src, teatype, uint16(i), uint32(len(data)), 0, true, false)
|
||||
p = SelectPacket()
|
||||
ok, err := p.Unmarshal(d)
|
||||
cl()
|
||||
if !ok {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
dat := BuildPacketFromBytes(NewPacketBuilder().Proto(proto).
|
||||
Src(src, srcPort).Dst(dst, dstPort).
|
||||
With(data[:i]).Hash().Plain(teatype, uint16(i&0x7ff)).
|
||||
Split(16384, false)[0]).Trans()
|
||||
t.Log("pkt:", hex.EncodeToString(dat))
|
||||
p, err := ParsePacketHeader(dat)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !p.IsVaildHash() {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if p.Proto != proto {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if p.CipherIndex() != teatype {
|
||||
t.Fatal("index", i, "expect", teatype, "got", p.CipherIndex())
|
||||
}
|
||||
if p.SrcPort != srcPort {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if p.DstPort != dstPort {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if !p.Src.Equal(src) {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if !p.Dst.Equal(dst) {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if p.AdditionalData() != uint16(i) {
|
||||
t.Fatal("index", i)
|
||||
t.Fatal("index", i, err)
|
||||
}
|
||||
p.B(func(buf []byte, p *Packet) {
|
||||
ok := p.WriteDataSegment(dat, buf)
|
||||
if !ok {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if !algo.IsVaildBlake2bHash8(p.PreCRC64(), buf) {
|
||||
t.Fatal("index", i, "expect body", hex.EncodeToString(data[:i]), "got", hex.EncodeToString(buf[8:]))
|
||||
}
|
||||
if p.Proto != FlagsProto(proto) {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if p.CipherIndex() != teatype {
|
||||
t.Fatal("index", i, "expect", teatype, "got", p.CipherIndex())
|
||||
}
|
||||
if p.SrcPort != srcPort {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if p.DstPort != dstPort {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if !bytes.Equal(p.src[:], src) {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if !bytes.Equal(p.dst[:], dst) {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if p.AdditionalData() != uint16(i&0x7ff) {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
if !bytes.Equal(buf[8:], data[:i]) {
|
||||
t.Fatal("index", i)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,36 @@
|
||||
package head
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"time"
|
||||
|
||||
var packetPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(Packet)
|
||||
},
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var packetPool = pbuf.NewBufferPool[Packet]()
|
||||
|
||||
func init() {
|
||||
packetPool.LimitInput(256)
|
||||
packetPool.LimitOutput(256)
|
||||
pbuf.LimitInput(256)
|
||||
pbuf.LimitOutput(256)
|
||||
if config.ShowDebugLog {
|
||||
go status()
|
||||
}
|
||||
}
|
||||
|
||||
// SelectPacket 从池中取出一个 Packet
|
||||
func SelectPacket() *Packet {
|
||||
return packetPool.Get().(*Packet)
|
||||
// selectPacket 从池中取出一个 Packet
|
||||
func selectPacket(buf ...byte) *PacketItem {
|
||||
return packetPool.NewBuffer(buf)
|
||||
}
|
||||
|
||||
// PutPacket 将 Packet 放回池中
|
||||
func PutPacket(p *Packet) {
|
||||
p.idxdatsz = 0
|
||||
p.Data = nil
|
||||
packetPool.Put(p)
|
||||
func status() {
|
||||
for range time.NewTicker(time.Minute).C {
|
||||
out, in := packetPool.CountItems()
|
||||
logrus.Infoln(file.Header(), "packet outside:", out, "inside:", in)
|
||||
out, in = pbuf.CountItems()
|
||||
logrus.Infoln(file.Header(), "default outside:", out, "inside:", in)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,24 @@ const (
|
||||
ProtoNotify
|
||||
ProtoQuery
|
||||
ProtoData
|
||||
ProtoTrans
|
||||
)
|
||||
|
||||
const ProtoTop = uint8(protobit + 1)
|
||||
|
||||
func (pf FlagsProto) Proto() uint8 {
|
||||
return uint8(pf & protobit)
|
||||
}
|
||||
|
||||
type Hello uint8
|
||||
|
||||
const (
|
||||
HelloPing Hello = iota
|
||||
HelloPong
|
||||
)
|
||||
|
||||
// Notify 是 map[peerip]{network, endpoint}
|
||||
type Notify = map[string][2]string
|
||||
|
||||
// Query 是 peerips 组成的数组
|
||||
type Query = []string
|
||||
|
||||
20
gold/head/raw.go
Normal file
20
gold/head/raw.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package head
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
ttloffset = unsafe.Offsetof(Packet{}.TTL)
|
||||
)
|
||||
|
||||
// ClearTTL for hash use
|
||||
func ClearTTL(data []byte) {
|
||||
data[ttloffset] = 0
|
||||
}
|
||||
|
||||
// DecTTL on transferring
|
||||
func DecTTL(data []byte) (drop bool) {
|
||||
data[ttloffset]--
|
||||
return data[ttloffset] == 0
|
||||
}
|
||||
126
gold/head/unbox.go
Normal file
126
gold/head/unbox.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package head
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/internal/algo"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
)
|
||||
|
||||
func ParsePacketHeader(data []byte) (pbytes PacketBytes, err error) {
|
||||
if len(data) <= int(PacketHeadLen) {
|
||||
err = ErrDataLenLEHeader
|
||||
return
|
||||
}
|
||||
p := selectPacket()
|
||||
sz := 0
|
||||
p.P(func(pb *PacketBuf) {
|
||||
if bin.IsLittleEndian {
|
||||
copy((*[PacketHeadLen]byte)(
|
||||
(unsafe.Pointer)(&pb.DAT),
|
||||
)[:], data)
|
||||
} else {
|
||||
pb.DAT.idxdatsz = binary.LittleEndian.Uint32(data[:4])
|
||||
pb.DAT.randn = int32(binary.LittleEndian.Uint32(data[4:8]))
|
||||
pt := binary.LittleEndian.Uint16(data[8:10])
|
||||
pb.DAT.Proto = FlagsProto(pt)
|
||||
pb.DAT.TTL = uint8(pt >> 8)
|
||||
pb.DAT.SrcPort = binary.LittleEndian.Uint16(data[10:12])
|
||||
pb.DAT.DstPort = binary.LittleEndian.Uint16(data[12:14])
|
||||
pb.DAT.Offset = binary.LittleEndian.Uint16(data[14:16])
|
||||
copy(pb.DAT.src[:], data[16:20])
|
||||
copy(pb.DAT.dst[:], data[20:24])
|
||||
pb.DAT.md5h8 = binary.LittleEndian.Uint64(data[24:32])
|
||||
}
|
||||
sz = pb.DAT.Size()
|
||||
if !pb.DAT.Proto.IsValid() {
|
||||
err = errors.New("invalid proto " + pb.DAT.Proto.String())
|
||||
return
|
||||
}
|
||||
if (!pb.DAT.Proto.HasMore() && (pb.DAT.Offset != 0 ||
|
||||
sz+int(PacketHeadLen) != len(data))) ||
|
||||
(pb.DAT.Proto.HasMore() && pb.DAT.Offset+
|
||||
uint16(len(data[PacketHeadLen:])) > uint16(sz)) {
|
||||
err = ErrInvalidOffset
|
||||
if config.ShowDebugLog {
|
||||
logrus.Warnf("[unbox] invalid offset %04x size %04x", pb.DAT.Offset, sz)
|
||||
}
|
||||
return
|
||||
}
|
||||
var crc uint64
|
||||
var b [PacketHeadNoCRCLen]byte
|
||||
copy(b[:], data[:PacketHeadNoCRCLen])
|
||||
ClearTTL(b[:])
|
||||
crc = algo.MD5Hash8(b[:])
|
||||
if crc != pb.DAT.md5h8 {
|
||||
err = ErrBadCRCChecksum
|
||||
if config.ShowDebugLog {
|
||||
logrus.Warnf("[unbox] exp crc %016x but got %016x", pb.DAT.md5h8, crc)
|
||||
}
|
||||
return
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[unbox] header data len", sz, "read data len", len(data)-int(PacketHeadLen))
|
||||
}
|
||||
if sz+int(PacketHeadLen) == len(data) {
|
||||
pb.Buffer.Write(data[PacketHeadLen:])
|
||||
pb.DAT.hashrem = -1
|
||||
return
|
||||
}
|
||||
pb.Buffer.Grow(sz)
|
||||
pb.Buffer.Write(make([]byte, sz))
|
||||
pb.DAT.hashrem = int64(sz)
|
||||
})
|
||||
if err != nil {
|
||||
p.ManualDestroy()
|
||||
return
|
||||
}
|
||||
pbytes = pbuf.BufferItemToBytes(p).Ignore()
|
||||
return
|
||||
}
|
||||
|
||||
// WriteDataSegment 将 data 的数据并发解码到自身 buf.
|
||||
//
|
||||
// 必须先调用 ParsePacketHeader 获得 packet.
|
||||
//
|
||||
// return: complete.
|
||||
func (p *Packet) WriteDataSegment(data, buf []byte) bool {
|
||||
if p.HasFinished() {
|
||||
return true
|
||||
}
|
||||
|
||||
flags := FlagsProto(data[8])
|
||||
offset := binary.LittleEndian.Uint16(data[14:16])
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[unbox] parse data flags", flags, "off", offset)
|
||||
}
|
||||
|
||||
if offset == 0 {
|
||||
p.randn = int32(binary.LittleEndian.Uint32(data[4:8]))
|
||||
p.Proto = flags
|
||||
p.TTL = data[9]
|
||||
p.Offset = 0
|
||||
p.md5h8 = binary.LittleEndian.Uint64(data[24:32])
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[unbox] parse data set zero offset flags", flags)
|
||||
}
|
||||
}
|
||||
|
||||
rembytes := atomic.LoadInt64(&p.hashrem)
|
||||
if rembytes > 0 {
|
||||
n := int64(copy(buf[offset:], data[PacketHeadLen:]))
|
||||
newrem := rembytes - n
|
||||
for !atomic.CompareAndSwapInt64(&p.hashrem, rembytes, newrem) {
|
||||
rembytes = atomic.LoadInt64(&p.hashrem)
|
||||
newrem = rembytes - n
|
||||
}
|
||||
}
|
||||
return p.HasFinished()
|
||||
}
|
||||
@@ -1,177 +1,54 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
mrand "math/rand"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCipherTextTooShort = errors.New("ciphertext too short")
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/internal/algo"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
func (l *Link) randkeyidx() uint8 {
|
||||
if l.keys[1] == nil {
|
||||
return 0
|
||||
}
|
||||
return uint8(mrand.Intn(32))
|
||||
return algo.RandKeyIndex()
|
||||
}
|
||||
|
||||
func mixkeys(k1, k2 []byte) []byte {
|
||||
if len(k1) != 32 || len(k2) != 32 {
|
||||
panic("unexpected key len")
|
||||
}
|
||||
k := make([]byte, 64)
|
||||
for i := range k1 {
|
||||
k1i, k2i := i, 31-i
|
||||
k1v, k2v := k1[k1i], k2[k2i]
|
||||
binary.LittleEndian.PutUint16(
|
||||
k[i*2:(i+1)*2],
|
||||
expandkeyunit(k1v, k2v),
|
||||
)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func expandkeyunit(v1, v2 byte) (v uint16) {
|
||||
v1s, v2s := uint16(v1), uint16(bits.Reverse8(v2))
|
||||
for i := 0; i < 8; i++ {
|
||||
v |= v1s & (1 << (i * 2))
|
||||
v1s <<= 1
|
||||
}
|
||||
for i := 0; i < 8; i++ {
|
||||
v2s <<= 1
|
||||
v |= v2s & (2 << (i * 2))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Encode 使用 xchacha20poly1305 和密钥序列加密
|
||||
func (l *Link) Encode(teatype uint8, additional uint16, b []byte) (eb []byte) {
|
||||
// decode by aead and put b into pool
|
||||
func (l *Link) decode(teatype uint8, additional uint16, b []byte) (db []byte, err error) {
|
||||
if len(b) == 0 || teatype >= 32 {
|
||||
return
|
||||
}
|
||||
if l.keys[0] == nil {
|
||||
eb = make([]byte, len(b))
|
||||
copy(eb, b)
|
||||
if config.ShowDebugLog {
|
||||
n := len(b)
|
||||
endl := "."
|
||||
if n > 64 {
|
||||
n = 64
|
||||
endl = "..."
|
||||
}
|
||||
logrus.Debugln(file.Header(), "copy plain text", hex.EncodeToString(b[:n]), endl)
|
||||
}
|
||||
db = make([]byte, len(b))
|
||||
copy(db, b)
|
||||
return
|
||||
}
|
||||
aead := l.keys[teatype]
|
||||
if aead == nil {
|
||||
logrus.Warnln("[crypto] cipher key at index", teatype, "is empty")
|
||||
return
|
||||
panic("unexpected empty aead")
|
||||
}
|
||||
eb = encode(aead, additional, b)
|
||||
return
|
||||
}
|
||||
|
||||
// Decode 使用 xchacha20poly1305 和密钥序列解密
|
||||
func (l *Link) Decode(teatype uint8, additional uint16, b []byte) (db []byte, err error) {
|
||||
if len(b) == 0 || teatype >= 32 {
|
||||
return
|
||||
}
|
||||
if l.keys[0] == nil {
|
||||
db = b
|
||||
return
|
||||
}
|
||||
aead := l.keys[teatype]
|
||||
if aead == nil {
|
||||
return
|
||||
}
|
||||
return decode(aead, additional, b)
|
||||
}
|
||||
|
||||
// encode 使用 xchacha20poly1305 加密
|
||||
func encode(aead cipher.AEAD, additional uint16, b []byte) []byte {
|
||||
nsz := aead.NonceSize()
|
||||
// Accocate capacity for all the stuffs.
|
||||
buf := make([]byte, 2+nsz+len(b)+aead.Overhead())
|
||||
binary.LittleEndian.PutUint16(buf[:2], additional)
|
||||
nonce := buf[2 : 2+nsz]
|
||||
// Select a random nonce
|
||||
_, err := rand.Read(nonce)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Encrypt the message and append the ciphertext to the nonce.
|
||||
eb := aead.Seal(nonce[nsz:nsz], nonce, b, buf[:2])
|
||||
return nonce[:nsz+len(eb)]
|
||||
}
|
||||
|
||||
// decode 使用 xchacha20poly1305 解密
|
||||
func decode(aead cipher.AEAD, additional uint16, b []byte) ([]byte, error) {
|
||||
nsz := aead.NonceSize()
|
||||
if len(b) < nsz {
|
||||
return nil, ErrCipherTextTooShort
|
||||
}
|
||||
// Split nonce and ciphertext.
|
||||
nonce, ciphertext := b[:nsz], b[nsz:]
|
||||
if len(ciphertext) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
// Decrypt the message and check it wasn't tampered with.
|
||||
var buf [2]byte
|
||||
binary.LittleEndian.PutUint16(buf[:], additional)
|
||||
return aead.Open(nil, nonce, ciphertext, buf[:])
|
||||
return algo.DecodeAEAD(aead, additional, b)
|
||||
}
|
||||
|
||||
// xorenc 按 8 字节, 以初始 m.mask 循环异或编码 data
|
||||
func (m *Me) xorenc(data []byte) []byte {
|
||||
batchsz := len(data) / 8
|
||||
remain := len(data) % 8
|
||||
sum := m.mask
|
||||
if remain > 0 {
|
||||
var buf [8]byte
|
||||
p := batchsz * 8
|
||||
copy(buf[:], data[p:])
|
||||
sum ^= binary.LittleEndian.Uint64(buf[:])
|
||||
binary.LittleEndian.PutUint64(buf[:], sum)
|
||||
copy(data[p:], buf[:])
|
||||
}
|
||||
for i := batchsz - 1; i >= 0; i-- {
|
||||
a := i * 8
|
||||
b := (i + 1) * 8
|
||||
sum ^= binary.LittleEndian.Uint64(data[a:b])
|
||||
binary.LittleEndian.PutUint64(data[a:b], sum)
|
||||
}
|
||||
return data
|
||||
func (m *Me) xorenc(data []byte, seq uint32) []byte {
|
||||
return algo.EncodeXOR(data, m.mask, seq)
|
||||
}
|
||||
|
||||
// xordec 按 8 字节, 以初始 m.mask 循环异或解码 data
|
||||
func (m *Me) xordec(data []byte) []byte {
|
||||
batchsz := len(data) / 8
|
||||
remain := len(data) % 8
|
||||
this := uint64(0)
|
||||
next := uint64(0)
|
||||
if len(data) >= 8 {
|
||||
next = binary.LittleEndian.Uint64(data[:8])
|
||||
}
|
||||
for i := 0; i < batchsz-1; i++ {
|
||||
a := i * 8
|
||||
b := (i + 1) * 8
|
||||
this = next
|
||||
next = binary.LittleEndian.Uint64(data[a+8 : b+8])
|
||||
binary.LittleEndian.PutUint64(data[a:b], this^next)
|
||||
}
|
||||
if remain > 0 {
|
||||
var buf [8]byte
|
||||
a := (batchsz - 1) * 8
|
||||
b := batchsz * 8
|
||||
copy(buf[:], data[b:])
|
||||
this = next
|
||||
next = binary.LittleEndian.Uint64(buf[:]) | (m.mask & (uint64(0xffffffff_ffffffff) << (uint64(remain) * 8)))
|
||||
if batchsz > 0 {
|
||||
binary.LittleEndian.PutUint64(data[a:b], this^next)
|
||||
}
|
||||
binary.LittleEndian.PutUint64(buf[:], next^m.mask)
|
||||
copy(data[b:], buf[:])
|
||||
} else {
|
||||
binary.LittleEndian.PutUint64(data[len(data)-8:], next^m.mask)
|
||||
}
|
||||
return data
|
||||
func (m *Me) xordec(data []byte) (uint32, []byte) {
|
||||
return algo.DecodeXOR(data, m.mask)
|
||||
}
|
||||
|
||||
21
gold/link/event.go
Normal file
21
gold/link/event.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"github.com/RomiChan/syncx"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
)
|
||||
|
||||
// 事件分发器
|
||||
var dispachers syncx.Map[uint8, Dispacher]
|
||||
|
||||
type Dispacher func(header *head.Packet, peer *Link, data []byte)
|
||||
|
||||
// RegisterDispacher of proto
|
||||
func RegisterDispacher(p uint8, d Dispacher) (actual Dispacher, hasexist bool) {
|
||||
return dispachers.LoadOrStore(p, d)
|
||||
}
|
||||
|
||||
// GetDispacher fn, ok
|
||||
func GetDispacher(p uint8) (Dispacher, bool) {
|
||||
return dispachers.Load(p)
|
||||
}
|
||||
@@ -6,11 +6,23 @@ import (
|
||||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
base14 "github.com/fumiama/go-base16384"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPerrNotExist = errors.New("peer not exist")
|
||||
)
|
||||
|
||||
type LinkData struct {
|
||||
H head.Packet
|
||||
D []byte
|
||||
}
|
||||
|
||||
// Link 是本机到 peer 的连接抽象
|
||||
type Link struct {
|
||||
// peer 的公钥
|
||||
@@ -20,47 +32,88 @@ type Link struct {
|
||||
// 收到的包的队列
|
||||
// 没有下层 nic 时
|
||||
// 包会分发到此
|
||||
pipe chan *head.Packet
|
||||
pipe chan LinkData
|
||||
// peer 的虚拟 ip
|
||||
peerip net.IP
|
||||
// peer 的公网 endpoint
|
||||
endpoint *net.UDPAddr
|
||||
endpoint p2p.EndPoint
|
||||
// peer 在设置的原始值
|
||||
rawep string
|
||||
// 本机允许接收/发送的 ip 网段
|
||||
allowedips []*net.IPNet
|
||||
// 连接所用对称加密密钥集
|
||||
keys [32]cipher.AEAD
|
||||
// 本机信息
|
||||
me *Me
|
||||
// 连接的状态,详见下方 const
|
||||
status int8
|
||||
// 最后一次收到报文的时间 (UnixNano)
|
||||
lastalive atomic.Int64
|
||||
// 是否允许转发
|
||||
allowtrans bool
|
||||
// 是否对数据进行 zstd 压缩
|
||||
usezstd bool
|
||||
// 是否采用双倍发包对抗强丢包
|
||||
doublepacket bool
|
||||
// udp 数据包的最大大小
|
||||
mtu uint16
|
||||
// 随机放缩 mtu 范围 (只减不增)
|
||||
mturandomrange uint16
|
||||
}
|
||||
|
||||
const (
|
||||
LINK_STATUS_DOWN = iota
|
||||
LINK_STATUS_HALFUP
|
||||
LINK_STATUS_UP
|
||||
)
|
||||
|
||||
// Connect 初始化与 peer 的连接
|
||||
func (m *Me) Connect(peer string) (*Link, error) {
|
||||
p, ok := m.IsInPeer(net.ParseIP(peer).String())
|
||||
if ok {
|
||||
return p, nil
|
||||
}
|
||||
return nil, errors.New("peer not exist")
|
||||
return nil, ErrPerrNotExist
|
||||
}
|
||||
|
||||
func (l *Link) ToLower(header *head.Packet, data []byte) {
|
||||
if l.pipe != nil {
|
||||
d := make([]byte, len(data))
|
||||
copy(d, data)
|
||||
l.pipe <- LinkData{
|
||||
H: *header,
|
||||
D: d,
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] deliver to pipe of", l.peerip)
|
||||
}
|
||||
return
|
||||
}
|
||||
_, err := l.me.nic.Write(data)
|
||||
if err != nil {
|
||||
logrus.Errorln("[listen] deliver", len(data), "bytes data to nic err:", err)
|
||||
} else if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] deliver", len(data), "bytes data to nic")
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭到 peer 的连接
|
||||
func (l *Link) Close() {
|
||||
l.status = LINK_STATUS_DOWN
|
||||
l.Destroy()
|
||||
}
|
||||
|
||||
// IP is wiregold peer ip
|
||||
func (l *Link) IP() net.IP {
|
||||
return l.peerip
|
||||
}
|
||||
|
||||
// RawEndPoint is initial ep in cfg
|
||||
func (l *Link) RawEndPoint() string {
|
||||
return l.rawep
|
||||
}
|
||||
|
||||
func (l *Link) EndPoint() p2p.EndPoint {
|
||||
return l.endpoint
|
||||
}
|
||||
|
||||
func (l *Link) SetEndPoint(ep p2p.EndPoint) {
|
||||
l.endpoint = ep
|
||||
}
|
||||
|
||||
func (l *Link) Me() *Me {
|
||||
return l.me
|
||||
}
|
||||
|
||||
// Destroy 从 connections 移除 peer
|
||||
@@ -75,7 +128,7 @@ func (l *Link) String() (n string) {
|
||||
if l.pubk != nil {
|
||||
b, err := base14.UTF16BE2UTF8(base14.Encode(l.pubk[:7]))
|
||||
if err == nil {
|
||||
n = helper.BytesToString(b)
|
||||
n = bin.BytesToString(b)
|
||||
} else {
|
||||
n = err.Error()
|
||||
}
|
||||
|
||||
@@ -1,205 +1,236 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/algo"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
)
|
||||
|
||||
// 监听本机 UDP endpoint
|
||||
func (m *Me) listenudp() (conn *net.UDPConn, err error) {
|
||||
conn, err = net.ListenUDP("udp", net.UDPAddrFromAddrPort(netip.MustParseAddrPort(m.udpep.String())))
|
||||
const lstnbufgragsz = 65536
|
||||
|
||||
type job struct {
|
||||
addr p2p.EndPoint
|
||||
buf pbuf.Bytes
|
||||
n int
|
||||
fil *uintptr
|
||||
}
|
||||
|
||||
func (m *Me) runworkers() {
|
||||
ncpu := runtime.NumCPU()
|
||||
m.jobs = make([]chan job, ncpu)
|
||||
for i := 0; i < ncpu; i++ {
|
||||
m.jobs[i] = make(chan job, 4096)
|
||||
go func(i int, jobs <-chan job) {
|
||||
for jb := range jobs {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] job thread", i, "call waitordispatch")
|
||||
}
|
||||
m.waitordispatch(jb.addr, jb.buf, jb.n, jb.fil)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] job thread", i, "fin waitordispatch")
|
||||
}
|
||||
}
|
||||
}(i, m.jobs[i])
|
||||
}
|
||||
}
|
||||
|
||||
// 监听本机 endpoint
|
||||
func (m *Me) listen() (conn p2p.Conn, err error) {
|
||||
conn, err = m.ep.Listen()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m.udpep = conn.LocalAddr()
|
||||
logrus.Infoln("[listen] at", m.udpep)
|
||||
m.ep = conn.LocalAddr()
|
||||
logrus.Infoln("[listen] at", m.ep)
|
||||
ncpu := runtime.NumCPU()
|
||||
bufs := make([]byte, lstnbufgragsz*ncpu)
|
||||
fils := make([]uintptr, ncpu)
|
||||
go m.runworkers()
|
||||
go func() {
|
||||
recvtotlcnt := uint64(0)
|
||||
recvloopcnt := uint16(0)
|
||||
recvlooptime := time.Now().UnixMilli()
|
||||
n := runtime.NumCPU()
|
||||
if n > 64 {
|
||||
n = 64 // 只用最多 64 核
|
||||
}
|
||||
logrus.Infoln("[listen] use cpu num:", n)
|
||||
listenbuff := make([]byte, 65536*n)
|
||||
hasntfinished := make([]sync.Mutex, n)
|
||||
for i := 0; err == nil; i++ {
|
||||
i %= n
|
||||
for !hasntfinished[i].TryLock() {
|
||||
i++
|
||||
i %= n
|
||||
if i == 0 { // looked up a full round
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
var (
|
||||
n int
|
||||
addr p2p.EndPoint
|
||||
err error
|
||||
)
|
||||
for {
|
||||
idx := -1
|
||||
for i := 0; i < ncpu; i++ {
|
||||
if !atomic.CompareAndSwapUintptr(&fils[i], 0, 1) {
|
||||
continue
|
||||
}
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
logrus.Debugln("[listen] lock index", i)
|
||||
lbf := listenbuff[i*65536 : (i+1)*65536]
|
||||
n, addr, err := conn.ReadFromUDP(lbf)
|
||||
if m.loop == nil || errors.Is(err, net.ErrClosed) {
|
||||
|
||||
var (
|
||||
lbf pbuf.Bytes
|
||||
fil *uintptr
|
||||
)
|
||||
if idx < 0 {
|
||||
lbf = pbuf.NewLargeBytes(lstnbufgragsz)
|
||||
} else {
|
||||
lbf = pbuf.ParseBytes(bufs[idx*lstnbufgragsz : (idx+1)*lstnbufgragsz : (idx+1)*lstnbufgragsz]...).Ignore()
|
||||
fil = &fils[idx]
|
||||
}
|
||||
|
||||
lbf.V(func(b []byte) {
|
||||
n, addr, err = conn.ReadFromPeer(b)
|
||||
})
|
||||
if m.connections == nil || errors.Is(err, net.ErrClosed) {
|
||||
logrus.Warnln("[listen] quit listening")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Warnln("[listen] read from udp err, reconnect:", err)
|
||||
conn, err = net.ListenUDP("udp", net.UDPAddrFromAddrPort(netip.MustParseAddrPort(m.udpep.String())))
|
||||
logrus.Warnln("[listen] read from conn err, reconnect:", err)
|
||||
conn, err = m.ep.Listen()
|
||||
if err != nil {
|
||||
logrus.Errorln("[listen] reconnect udp err:", err)
|
||||
return
|
||||
}
|
||||
logrus.Debugln("[listen] unlock index", i)
|
||||
hasntfinished[i].Unlock()
|
||||
i--
|
||||
continue
|
||||
}
|
||||
recvtotlcnt += uint64(n)
|
||||
recvloopcnt++
|
||||
if recvloopcnt%m.speedloop == 0 {
|
||||
now := time.Now().UnixMilli()
|
||||
logrus.Infof("[listen] recv avg speed: %.2f KB/s", float64(recvtotlcnt)/float64(now-recvlooptime))
|
||||
recvtotlcnt = 0
|
||||
recvlooptime = now
|
||||
}
|
||||
packet := m.wait(lbf[:n])
|
||||
if packet == nil {
|
||||
logrus.Debugln("[listen] unlock index", i)
|
||||
hasntfinished[i].Unlock()
|
||||
i--
|
||||
if n <= 0 {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] unexpected read n =", n)
|
||||
}
|
||||
continue
|
||||
}
|
||||
go m.listenthread(packet, addr, i, hasntfinished[i].Unlock)
|
||||
if idx < 0 {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Infoln("[listen] go dispatch")
|
||||
}
|
||||
go m.waitordispatch(addr, lbf, n, fil)
|
||||
} else {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] send dispatch to cpu", idx)
|
||||
}
|
||||
m.jobs[idx] <- job{addr: addr, buf: lbf, n: n, fil: fil}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Me) listenthread(packet *head.Packet, addr *net.UDPAddr, index int, finish func()) {
|
||||
defer finish()
|
||||
defer logrus.Debugln("[listen] unlock index", index)
|
||||
r := packet.Len() - len(packet.Data)
|
||||
if r > 0 {
|
||||
logrus.Warnln("[listen] @", index, "packet from endpoint", addr, "is smaller than it declared: drop it")
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
p, ok := m.IsInPeer(packet.Src.String())
|
||||
logrus.Debugln("[listen] @", index, "recv from endpoint", addr, "src", packet.Src, "dst", packet.Dst)
|
||||
if !ok {
|
||||
logrus.Warnln("[listen] @", index, "packet from", packet.Src, "to", packet.Dst, "is refused")
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
if p.endpoint == nil || p.endpoint.String() != addr.String() {
|
||||
logrus.Infoln("[listen] @", index, "set endpoint of peer", p.peerip, "to", addr.String())
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.endpoint)), unsafe.Pointer(addr))
|
||||
}
|
||||
switch {
|
||||
case p.IsToMe(packet.Dst):
|
||||
if !p.Accept(packet.Src) {
|
||||
logrus.Warnln("[listen] @", index, "refused packet from", packet.Src.String()+":"+strconv.Itoa(int(packet.SrcPort)))
|
||||
packet.Put()
|
||||
return
|
||||
func (m *Me) waitordispatch(addr p2p.EndPoint, buf pbuf.Bytes, n int, fil *uintptr) {
|
||||
defer func() {
|
||||
buf.ManualDestroy()
|
||||
if fil != nil {
|
||||
atomic.StoreUintptr(fil, 0)
|
||||
}
|
||||
addt := packet.AdditionalData()
|
||||
var err error
|
||||
packet.Data, err = p.Decode(packet.CipherIndex(), addt, packet.Data)
|
||||
if err != nil {
|
||||
logrus.Debugln("[listen] @", index, "drop invalid packet", ", key idx:", packet.CipherIndex(), "addt:", addt, "err:", err)
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
if p.usezstd {
|
||||
dec, _ := zstd.NewReader(bytes.NewReader(packet.Data))
|
||||
var err error
|
||||
packet.Data, err = io.ReadAll(dec)
|
||||
dec.Close()
|
||||
if err != nil {
|
||||
logrus.Debugln("[listen] @", index, "drop invalid zstd packet:", err)
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
}
|
||||
if !packet.IsVaildHash() {
|
||||
logrus.Debugln("[listen] @", index, "drop invalid hash packet")
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
switch packet.Proto {
|
||||
case head.ProtoHello:
|
||||
switch p.status {
|
||||
case LINK_STATUS_DOWN:
|
||||
n, err := p.WriteAndPut(head.NewPacket(head.ProtoHello, m.SrcPort(), p.peerip, m.DstPort(), nil), false)
|
||||
if err == nil {
|
||||
logrus.Debugln("[listen] @", index, "send", n, "bytes hello ack packet")
|
||||
p.status = LINK_STATUS_HALFUP
|
||||
} else {
|
||||
logrus.Errorln("[listen] @", index, "send hello ack packet error:", err)
|
||||
}
|
||||
case LINK_STATUS_HALFUP:
|
||||
p.status = LINK_STATUS_UP
|
||||
case LINK_STATUS_UP:
|
||||
}
|
||||
packet.Put()
|
||||
case head.ProtoNotify:
|
||||
logrus.Infoln("[listen] @", index, "recv notify from", packet.Src)
|
||||
go p.onNotify(packet.Data)
|
||||
packet.Put()
|
||||
case head.ProtoQuery:
|
||||
logrus.Infoln("[listen] @", index, "recv query from", packet.Src)
|
||||
go p.onQuery(packet.Data)
|
||||
packet.Put()
|
||||
case head.ProtoData:
|
||||
if p.pipe != nil {
|
||||
p.pipe <- packet
|
||||
logrus.Debugln("[listen] @", index, "deliver to pipe of", p.peerip)
|
||||
} else {
|
||||
_, err := m.nic.Write(packet.Data)
|
||||
if err != nil {
|
||||
logrus.Errorln("[listen] @", index, "deliver", len(packet.Data), "bytes data to nic err:", err)
|
||||
} else {
|
||||
logrus.Debugln("[listen] @", index, "deliver", len(packet.Data), "bytes data to nic")
|
||||
}
|
||||
packet.Put()
|
||||
}
|
||||
default:
|
||||
logrus.Warnln("[listen] @", index, "recv unknown proto:", packet.Proto)
|
||||
packet.Put()
|
||||
}
|
||||
case p.Accept(packet.Dst):
|
||||
if !p.allowtrans {
|
||||
logrus.Warnln("[listen] @", index, "refused to trans packet to", packet.Dst.String()+":"+strconv.Itoa(int(packet.DstPort)))
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
// 转发
|
||||
lnk := m.router.NextHop(packet.Dst.String())
|
||||
if lnk == nil {
|
||||
logrus.Warnln("[listen] @", index, "transfer drop packet: nil nexthop")
|
||||
packet.Put()
|
||||
return
|
||||
}
|
||||
n, err := lnk.WriteAndPut(packet, true)
|
||||
if err == nil {
|
||||
logrus.Debugln("[listen] @", index, "trans", n, "bytes packet to", packet.Dst.String()+":"+strconv.Itoa(int(packet.DstPort)))
|
||||
}()
|
||||
|
||||
recvtotlcnt := atomic.AddUint64(&m.recvtotlcnt, uint64(n))
|
||||
recvloopcnt := atomic.AddUintptr(&m.recvloopcnt, 1)
|
||||
recvlooptime := atomic.LoadInt64(&m.recvlooptime)
|
||||
if recvloopcnt%uintptr(m.speedloop) == 0 {
|
||||
now := time.Now().UnixMilli()
|
||||
kb := float64(recvtotlcnt) / float64(now-recvlooptime)
|
||||
if kb < 1024 {
|
||||
logrus.Infof("[listen] queue recv avg speed: %.2f KB/s", kb)
|
||||
} else {
|
||||
logrus.Errorln("[listen] @", index, "trans packet to", packet.Dst.String()+":"+strconv.Itoa(int(packet.DstPort)), "err:", err)
|
||||
kb /= 1024
|
||||
if kb < 1024 {
|
||||
logrus.Infof("[listen] queue recv avg speed: %.2f MB/s", kb)
|
||||
} else {
|
||||
logrus.Infof("[listen] queue recv avg speed: %.2f GB/s", kb/1024)
|
||||
}
|
||||
}
|
||||
default:
|
||||
logrus.Warnln("[listen] @", index, "packet dst", packet.Dst.String()+":"+strconv.Itoa(int(packet.DstPort)), "is not in peers")
|
||||
packet.Put()
|
||||
atomic.StoreUint64(&m.recvtotlcnt, 0)
|
||||
atomic.StoreInt64(&m.recvlooptime, now)
|
||||
}
|
||||
buf.V(func(b []byte) {
|
||||
h := m.wait(b[:n], addr)
|
||||
if !h.HasInit() {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] queue waiting")
|
||||
}
|
||||
return
|
||||
}
|
||||
h.B(func(b []byte, p *head.Packet) {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] dispatch", len(b), "bytes packet")
|
||||
}
|
||||
if !p.HasFinished() {
|
||||
panic("unexpected unfinished")
|
||||
}
|
||||
m.dispatch(p, b, addr)
|
||||
})
|
||||
h.ManualDestroy()
|
||||
})
|
||||
}
|
||||
|
||||
func (m *Me) dispatch(header *head.Packet, body []byte, addr p2p.EndPoint) {
|
||||
r := header.Size() - len(body)
|
||||
if r > 0 {
|
||||
logrus.Warnln("[listen] packet from endpoint", addr, "len", len(body), "is smaller than it declared len", header.Size(), ", drop it")
|
||||
return
|
||||
}
|
||||
srcip := header.Src()
|
||||
dstip := header.Dst()
|
||||
p := m.extractPeer(srcip, dstip, addr)
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
if !p.Accept(srcip) {
|
||||
logrus.Warnln("[listen] refused packet from", srcip.String()+":"+strconv.Itoa(int(header.SrcPort)))
|
||||
return
|
||||
}
|
||||
if !p.IsToMe(dstip) {
|
||||
logrus.Warnln("[listen] unhandled trans packet from", srcip.String()+":"+strconv.Itoa(int(header.SrcPort)))
|
||||
return
|
||||
}
|
||||
addt := header.AdditionalData()
|
||||
var err error
|
||||
data, err := p.decode(header.CipherIndex(), addt, body)
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] drop invalid packet key idx:", header.CipherIndex(), "addt:", addt, "err:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(data) < 8 {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] drop invalid data len packet key idx:", header.CipherIndex(), "addt:", addt, "len", len(data))
|
||||
}
|
||||
return
|
||||
}
|
||||
ok := false
|
||||
ok = algo.IsVaildBlake2bHash8(header.PreCRC64(), data)
|
||||
if !ok {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] drop invalid hash packet")
|
||||
}
|
||||
return
|
||||
}
|
||||
data = data[8:]
|
||||
if p.usezstd {
|
||||
data, err = algo.DecodeZstd(data) // skip hash
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] drop invalid zstd packet:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] zstd decoded len:", len(data))
|
||||
}
|
||||
}
|
||||
fn, ok := GetDispacher(header.Proto.Proto())
|
||||
if !ok {
|
||||
logrus.Warnln(file.Header(), "unsupported proto", header.Proto.Proto())
|
||||
return
|
||||
}
|
||||
fn(header, p, data)
|
||||
}
|
||||
|
||||
219
gold/link/me.go
219
gold/link/me.go
@@ -10,61 +10,98 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/ttl"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/lower"
|
||||
"github.com/fumiama/water/waterutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
"github.com/fumiama/WireGold/lower"
|
||||
)
|
||||
|
||||
// Me 是本机的抽象
|
||||
type Me struct {
|
||||
// 读写同步锁
|
||||
connmapmu sync.RWMutex
|
||||
// 本机总接收字节数
|
||||
recvtotlcnt uint64
|
||||
// 上一次触发循环计数时间
|
||||
recvlooptime int64
|
||||
// 本机私钥
|
||||
// 利用 Curve25519 生成
|
||||
// https://pkg.go.dev/golang.org/x/crypto/curve25519
|
||||
// https://www.zhihu.com/question/266758647
|
||||
privKey [32]byte
|
||||
// 本机虚拟 ip
|
||||
me net.IP
|
||||
// 本机子网
|
||||
subnet net.IPNet
|
||||
// 本机 UDP endpoint
|
||||
udpep net.Addr
|
||||
// 本机环回 link
|
||||
loop *Link
|
||||
// 本机活跃的所有连接
|
||||
connections map[string]*Link
|
||||
// 读写同步锁
|
||||
connmapmu sync.RWMutex
|
||||
// 本机监听的 udp 连接, 用于向对端直接发送报文
|
||||
udpconn *net.UDPConn
|
||||
// 本机网卡
|
||||
nic lower.NICIO
|
||||
// 本机路由表
|
||||
router *Router
|
||||
// 本机未接收完全分片池
|
||||
recving *ttl.Cache[[32]byte, *head.Packet]
|
||||
// 抗重放攻击记录池
|
||||
recved *ttl.Cache[uint64, bool]
|
||||
// 本机上层配置
|
||||
srcport, dstport, mtu, speedloop uint16
|
||||
// 报头掩码
|
||||
mask uint64
|
||||
// 本机总接收数据包计数
|
||||
recvloopcnt uintptr
|
||||
// 用于自我重连
|
||||
cfg *MyConfig
|
||||
// 本机网卡
|
||||
nic *lower.NICIO
|
||||
// 本机路由表
|
||||
router *Router
|
||||
// 本机未接收完全分片池
|
||||
recving *ttl.Cache[uint16, head.PacketBytes]
|
||||
// 抗重放攻击记录池
|
||||
recved *ttl.Cache[uint64, struct{}]
|
||||
|
||||
// 以上为64位对齐, 修改时注意保持
|
||||
|
||||
// 本机虚拟 ip
|
||||
me net.IP
|
||||
// 本机子网
|
||||
subnet net.IPNet
|
||||
// 本机 endpoint
|
||||
ep p2p.EndPoint
|
||||
// 本机活跃的所有连接
|
||||
connections map[string]*Link
|
||||
// 本机监听的连接端点, 也用于向对端直接发送报文
|
||||
conn p2p.Conn
|
||||
// 供内部使用的包分发任务列
|
||||
jobs []chan job
|
||||
// 是否进行 base16384 编码
|
||||
base14 bool
|
||||
// 本机初始 ttl
|
||||
ttl uint8
|
||||
// 本机网络端点初始化配置
|
||||
networkconfigs []any
|
||||
}
|
||||
|
||||
type MyConfig struct {
|
||||
MyIPwithMask string
|
||||
MyEndpoint string
|
||||
Network string
|
||||
NetworkConfigs []any
|
||||
PrivateKey *[32]byte
|
||||
NIC lower.NICIO
|
||||
NICConfig *NICConfig
|
||||
SrcPort, DstPort, MTU, SpeedLoop uint16
|
||||
Mask uint64
|
||||
Base14 bool
|
||||
MaxTTL uint8
|
||||
}
|
||||
|
||||
type NICConfig struct {
|
||||
IP net.IP
|
||||
SubNet *net.IPNet
|
||||
CIDRs []string
|
||||
}
|
||||
|
||||
// NewMe 设置本机参数
|
||||
func NewMe(cfg *MyConfig) (m Me) {
|
||||
m.cfg = cfg
|
||||
m.privKey = *cfg.PrivateKey
|
||||
var err error
|
||||
m.udpep, err = net.ResolveUDPAddr("udp", cfg.MyEndpoint)
|
||||
nw := cfg.Network
|
||||
if nw == "" {
|
||||
nw = "udp"
|
||||
}
|
||||
m.networkconfigs = cfg.NetworkConfigs
|
||||
m.ep, err = p2p.NewEndPoint(nw, cfg.MyEndpoint, m.networkconfigs...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -74,45 +111,77 @@ func NewMe(cfg *MyConfig) (m Me) {
|
||||
}
|
||||
m.me = ip
|
||||
m.subnet = *cidr
|
||||
m.udpconn, err = m.listenudp()
|
||||
m.speedloop = cfg.SpeedLoop
|
||||
if m.speedloop == 0 {
|
||||
m.speedloop = 4096
|
||||
}
|
||||
m.conn, err = m.listen()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.connections = make(map[string]*Link)
|
||||
m.nic = cfg.NIC
|
||||
m.router = &Router{
|
||||
list: make([]*net.IPNet, 1, 16),
|
||||
table: make(map[string]*Link, 16),
|
||||
cache: ttl.NewCache[string, *Link](time.Minute),
|
||||
}
|
||||
m.router.SetDefault(nil)
|
||||
_, localp, err := net.SplitHostPort(m.EndPoint().String())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m.loop = m.AddPeer(&PeerConfig{
|
||||
PeerIP: m.me.String(),
|
||||
EndPoint: "127.0.0.1:" + localp,
|
||||
AllowedIPs: []string{cfg.MyIPwithMask},
|
||||
NoPipe: cfg.NIC != nil,
|
||||
MTU: cfg.MTU,
|
||||
})
|
||||
m.srcport = cfg.SrcPort
|
||||
m.dstport = cfg.DstPort
|
||||
m.mtu = cfg.MTU & 0xfff8
|
||||
m.speedloop = cfg.SpeedLoop
|
||||
if m.speedloop == 0 {
|
||||
m.speedloop = 4096
|
||||
m.mtu = cfg.MTU
|
||||
if cfg.NICConfig != nil {
|
||||
m.nic = lower.NewNIC(
|
||||
cfg.NICConfig.IP, cfg.NICConfig.SubNet,
|
||||
strconv.FormatUint(uint64(m.mtu), 10), cfg.NICConfig.CIDRs...,
|
||||
)
|
||||
}
|
||||
m.mask = cfg.Mask
|
||||
m.recvlooptime = time.Now().UnixMilli()
|
||||
m.base14 = cfg.Base14
|
||||
if cfg.MaxTTL == 0 {
|
||||
m.ttl = 64
|
||||
} else {
|
||||
m.ttl = cfg.MaxTTL
|
||||
}
|
||||
var buf [8]byte
|
||||
binary.BigEndian.PutUint64(buf[:], m.mask)
|
||||
logrus.Infoln("[me] xor mask", hex.EncodeToString(buf[:]))
|
||||
m.recving = ttl.NewCache[[32]byte, *head.Packet](time.Second * 30)
|
||||
m.recved = ttl.NewCache[uint64, bool](time.Second * 30)
|
||||
m.recving = ttl.NewCache[uint16, head.PacketBytes](time.Second * 10)
|
||||
m.recved = ttl.NewCache[uint64, struct{}](time.Minute)
|
||||
return
|
||||
}
|
||||
|
||||
// Restart 重新连接
|
||||
func (m *Me) Restart() error {
|
||||
for i := 0; i < len(m.jobs); i++ {
|
||||
close(m.jobs[i])
|
||||
}
|
||||
oldconn := m.conn
|
||||
m.conn = nil
|
||||
if bin.IsNonNilInterface(oldconn) {
|
||||
_ = oldconn.Close()
|
||||
}
|
||||
var err error
|
||||
nw := m.cfg.Network
|
||||
if nw == "" {
|
||||
nw = "udp"
|
||||
}
|
||||
m.networkconfigs = m.cfg.NetworkConfigs
|
||||
m.ep, err = p2p.NewEndPoint(nw, m.cfg.MyEndpoint, m.networkconfigs...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ip, cidr, err := net.ParseCIDR(m.cfg.MyIPwithMask)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.me = ip
|
||||
m.subnet = *cidr
|
||||
m.recvlooptime = time.Now().UnixMilli()
|
||||
m.conn, err = m.listen()
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Me) SrcPort() uint16 {
|
||||
return m.srcport
|
||||
}
|
||||
@@ -125,16 +194,29 @@ func (m *Me) MTU() uint16 {
|
||||
return m.mtu
|
||||
}
|
||||
|
||||
func (m *Me) EndPoint() net.Addr {
|
||||
return m.udpep
|
||||
func (m *Me) TTL() uint8 {
|
||||
return m.ttl
|
||||
}
|
||||
|
||||
func (m *Me) EndPoint() p2p.EndPoint {
|
||||
return m.ep
|
||||
}
|
||||
|
||||
func (m *Me) NetworkConfigs() []any {
|
||||
return m.networkconfigs
|
||||
}
|
||||
|
||||
func (m *Me) Close() error {
|
||||
m.loop = nil
|
||||
for i := 0; i < len(m.jobs); i++ {
|
||||
jb := m.jobs[i]
|
||||
if jb != nil {
|
||||
close(jb)
|
||||
}
|
||||
}
|
||||
m.connections = nil
|
||||
if m.udpconn != nil {
|
||||
_ = m.udpconn.Close()
|
||||
m.udpconn = nil
|
||||
if bin.IsNonNilInterface(m.conn) {
|
||||
_ = m.conn.Close()
|
||||
m.conn = nil
|
||||
}
|
||||
m.router = nil
|
||||
if m.recving != nil {
|
||||
@@ -154,7 +236,9 @@ func (m *Me) Close() error {
|
||||
|
||||
func (m *Me) Write(packet []byte) (n int, err error) {
|
||||
n = m.sendAllSameDst(packet)
|
||||
logrus.Debugln("[me] writer ate", len(packet), "bytes, remain", len(packet)-n, "bytes")
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[me] writer ate", len(packet), "bytes, remain", len(packet)-n, "bytes")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -183,7 +267,9 @@ func (m *Me) sendAllSameDst(packet []byte) (n int) {
|
||||
}
|
||||
n += pktl
|
||||
rem = packet[n:]
|
||||
logrus.Debugln("[me] skip to send", len(packet), "bytes ipv6 packet")
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[me] skip to send", len(packet), "bytes ipv6 packet")
|
||||
}
|
||||
}
|
||||
if len(rem) == 0 || !waterutil.IsIPv4(rem) {
|
||||
logrus.Warnln("[me] skip to send", len(packet), "bytes full packet")
|
||||
@@ -196,12 +282,16 @@ func (m *Me) sendAllSameDst(packet []byte) (n int) {
|
||||
for len(ptr) > 20 && p.issame(ptr) {
|
||||
totl := waterutil.IPv4TotalLength(ptr)
|
||||
if int(totl) > len(ptr) {
|
||||
logrus.Debugln("[me] wrap got invalid totl, break")
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[me] wrap got invalid totl, break")
|
||||
}
|
||||
break
|
||||
}
|
||||
i += int(totl)
|
||||
ptr = rem[i:]
|
||||
logrus.Debugln("[me] wrap", totl, "bytes packet to send together")
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[me] wrap", totl, "bytes packet to send together")
|
||||
}
|
||||
}
|
||||
if i == 0 {
|
||||
return
|
||||
@@ -210,15 +300,24 @@ func (m *Me) sendAllSameDst(packet []byte) (n int) {
|
||||
packet = rem[:i]
|
||||
rem = rem[i:]
|
||||
dst := waterutil.IPv4Destination(packet)
|
||||
logrus.Debugln("[me] sending", len(packet), "bytes packet from :"+strconv.Itoa(int(m.SrcPort())), "to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), "remain:", len(rem), "bytes")
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[me] sending", len(packet), "bytes packet from :"+strconv.Itoa(int(m.SrcPort())), "to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), "remain:", len(rem), "bytes")
|
||||
}
|
||||
if m.me.Equal(dst) { // is to myself, write to nic (pipe not allow loopback)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[me] loopback packet")
|
||||
}
|
||||
_, err := m.nic.Write(packet)
|
||||
if err != nil {
|
||||
logrus.Warnln("[me] write to loopback err:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
lnk := m.router.NextHop(dst.String())
|
||||
if lnk == nil {
|
||||
logrus.Warnln("[me] drop packet to", dst.String()+":"+strconv.Itoa(int(m.DstPort())), ": nil nexthop")
|
||||
return
|
||||
}
|
||||
_, err := lnk.WriteAndPut(head.NewPacket(head.ProtoData, m.SrcPort(), lnk.peerip, m.DstPort(), packet), false)
|
||||
if err != nil {
|
||||
logrus.Warnln("[me] write to peer", lnk.peerip, "err:", err)
|
||||
}
|
||||
lnk.WritePacket(head.ProtoData, packet, lnk.me.ttl)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@ package link
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
// 保持 NAT
|
||||
@@ -16,88 +15,30 @@ import (
|
||||
// 以秒为单位,小于等于 0 不发送
|
||||
func (l *Link) keepAlive(dur int64) {
|
||||
if dur > 0 {
|
||||
logrus.Infoln("[nat] start to keep alive")
|
||||
logrus.Infoln(file.Header(), "start to keep alive")
|
||||
t := time.NewTicker(time.Second * time.Duration(dur))
|
||||
for range t.C {
|
||||
n, err := l.WriteAndPut(head.NewPacket(head.ProtoHello, l.me.srcport, l.peerip, l.me.dstport, nil), false)
|
||||
if err == nil {
|
||||
logrus.Infoln("[nat] send", n, "bytes keep alive packet")
|
||||
} else {
|
||||
logrus.Errorln("[nat] send keep alive packet error:", err)
|
||||
if l.me.connections == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 收到通告包的处理函数
|
||||
func (l *Link) onNotify(packet []byte) {
|
||||
// TODO: 完成data解包与endpoint注册
|
||||
// 1. Data 解包
|
||||
// ---- 使用 head.Notify 解释 packet
|
||||
notify := make(head.Notify, 32)
|
||||
err := json.Unmarshal(packet, ¬ify)
|
||||
if err != nil {
|
||||
logrus.Errorln("[nat] notify json unmarshal err:", err)
|
||||
return
|
||||
}
|
||||
// 2. endpoint注册
|
||||
// ---- 遍历 Notify,注册对方的 endpoint 到
|
||||
// ---- connections,注意使用读写锁connmapmu
|
||||
for peer, ep := range notify {
|
||||
addr, err := net.ResolveUDPAddr("udp", ep)
|
||||
if err == nil {
|
||||
p, ok := l.me.IsInPeer(peer)
|
||||
if ok {
|
||||
if p.endpoint.String() != ep {
|
||||
p.endpoint = addr
|
||||
logrus.Infoln("[nat] notify set ep of peer", peer, "to", ep)
|
||||
la := l.lastalive.Load()
|
||||
if la != 0 && time.Since(time.Unix(0, la)) > 10*time.Second*time.Duration(dur) { // 可能已经被阻断, 断开重连
|
||||
logrus.Warnln(file.Header(), "no response after 10 keep alive tries, re-connecting...")
|
||||
err := l.me.Restart()
|
||||
if err != nil {
|
||||
logrus.Errorln(file.Header(), "re-connect me err:", err)
|
||||
} else {
|
||||
logrus.Infoln(file.Header(), "re-connect me succeeded")
|
||||
}
|
||||
continue
|
||||
}
|
||||
l.WritePacket(head.ProtoHello, []byte{byte(head.HelloPing)}, 64)
|
||||
logrus.Infoln(file.Header(), "send keep alive to", l.peerip)
|
||||
}
|
||||
logrus.Debugln("[nat] notify drop invalid peer:", peer, "ep:", ep)
|
||||
}
|
||||
}
|
||||
|
||||
// 收到询问包的处理函数
|
||||
func (l *Link) onQuery(packet []byte) {
|
||||
// 完成data解包与notify分发
|
||||
|
||||
// 1. Data 解包
|
||||
// ---- 使用 head.Query 解释 packet
|
||||
// ---- 根据 Query 确定需要封装的 Notify
|
||||
var peers head.Query
|
||||
err := json.Unmarshal(packet, &peers)
|
||||
if err != nil {
|
||||
logrus.Errorln("[nat] query json unmarshal err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 2. notify分发
|
||||
// ---- 封装 Notify 到 新的 packet
|
||||
// ---- 调用 l.Send 发送到对方
|
||||
notify := make(head.Notify, len(peers))
|
||||
for _, p := range peers {
|
||||
lnk, ok := l.me.IsInPeer(p)
|
||||
if ok {
|
||||
notify[p] = lnk.endpoint.String()
|
||||
}
|
||||
}
|
||||
if len(notify) > 0 {
|
||||
logrus.Infoln("[nat] query wrap", len(notify), "notify")
|
||||
w := helper.SelectWriter()
|
||||
_ = json.NewEncoder(w).Encode(¬ify)
|
||||
_, err = l.WriteAndPut(head.NewPacket(head.ProtoNotify, l.me.srcport, l.peerip, l.me.dstport, w.Bytes()), false)
|
||||
if err != nil {
|
||||
logrus.Errorln("[nat] notify peer", l, "err:", err)
|
||||
return
|
||||
}
|
||||
helper.PutWriter(w)
|
||||
}
|
||||
}
|
||||
|
||||
// sendquery 主动发起查询,询问对方是否可以到达 peers
|
||||
func (l *Link) sendquery(tick time.Duration, peers ...string) {
|
||||
func (l *Link) sendQuery(tick time.Duration, peers ...string) {
|
||||
if len(peers) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -107,10 +48,7 @@ func (l *Link) sendquery(tick time.Duration, peers ...string) {
|
||||
}
|
||||
t := time.NewTicker(tick)
|
||||
for range t.C {
|
||||
logrus.Infoln("[nat] query send query to", l.peerip)
|
||||
_, err = l.WriteAndPut(head.NewPacket(head.ProtoQuery, l.me.srcport, l.peerip, l.me.dstport, data), false)
|
||||
if err != nil {
|
||||
logrus.Errorln("[nat] query write err:", err)
|
||||
}
|
||||
l.WritePacket(head.ProtoQuery, data, l.me.ttl)
|
||||
logrus.Infoln(file.Header(), "send query to", l.peerip)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,15 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
curve "github.com/fumiama/go-x25519"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/algo"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
type PeerConfig struct {
|
||||
@@ -21,6 +26,7 @@ type PeerConfig struct {
|
||||
MTURandomRange uint16
|
||||
AllowTrans, NoPipe bool
|
||||
UseZstd bool
|
||||
DoublePacket bool
|
||||
}
|
||||
|
||||
// AddPeer 添加一个 peer
|
||||
@@ -31,21 +37,23 @@ func (m *Me) AddPeer(cfg *PeerConfig) (l *Link) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
if cfg.MTU == 0 || (m.mtu != 0 && cfg.MTU > m.mtu) {
|
||||
if cfg.MTU == 0 {
|
||||
panic("invalid mtu for peer " + cfg.PeerIP)
|
||||
}
|
||||
l = &Link{
|
||||
pubk: cfg.PubicKey,
|
||||
peerip: net.ParseIP(cfg.PeerIP),
|
||||
rawep: cfg.EndPoint,
|
||||
allowtrans: cfg.AllowTrans,
|
||||
usezstd: cfg.UseZstd,
|
||||
doublepacket: cfg.DoublePacket,
|
||||
me: m,
|
||||
mtu: cfg.MTU,
|
||||
mturandomrange: cfg.MTURandomRange,
|
||||
}
|
||||
|
||||
if !cfg.NoPipe {
|
||||
l.pipe = make(chan *head.Packet, 32)
|
||||
l.pipe = make(chan LinkData, 4096)
|
||||
}
|
||||
var k, p []byte
|
||||
if cfg.PubicKey != nil {
|
||||
@@ -57,7 +65,7 @@ func (m *Me) AddPeer(cfg *PeerConfig) (l *Link) {
|
||||
if len(k) == 32 {
|
||||
var err error
|
||||
if len(p) == 32 {
|
||||
mixk := mixkeys(k, p)
|
||||
mixk := algo.MixKeys(k, p)
|
||||
for i := range k {
|
||||
l.keys[i], err = chacha20poly1305.NewX(mixk[i : i+32])
|
||||
if err != nil {
|
||||
@@ -72,7 +80,7 @@ func (m *Me) AddPeer(cfg *PeerConfig) (l *Link) {
|
||||
}
|
||||
}
|
||||
if cfg.EndPoint != "" {
|
||||
e, err := net.ResolveUDPAddr("udp", cfg.EndPoint)
|
||||
e, err := p2p.NewEndPoint(m.ep.Network(), cfg.EndPoint, m.networkconfigs...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -85,7 +93,8 @@ func (m *Me) AddPeer(cfg *PeerConfig) (l *Link) {
|
||||
continue
|
||||
}
|
||||
noroute := ipnet[0] == 'x'
|
||||
if noroute {
|
||||
innerroute := ipnet[0] == 'y'
|
||||
if noroute || innerroute {
|
||||
ipnet = ipnet[1:]
|
||||
if len(ipnet) == 0 {
|
||||
continue
|
||||
@@ -107,7 +116,7 @@ func (m *Me) AddPeer(cfg *PeerConfig) (l *Link) {
|
||||
}
|
||||
logrus.Infoln("[peer] add peer:", cfg.PeerIP, "allow:", cfg.AllowedIPs)
|
||||
go l.keepAlive(cfg.KeepAliveDur)
|
||||
go l.sendquery(time.Second*time.Duration(cfg.QueryTick), cfg.Querys...)
|
||||
go l.sendQuery(time.Second*time.Duration(cfg.QueryTick), cfg.Querys...)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -118,3 +127,25 @@ func (m *Me) IsInPeer(peer string) (p *Link, ok bool) {
|
||||
m.connmapmu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Me) extractPeer(srcip, dstip net.IP, addr p2p.EndPoint) *Link {
|
||||
p, ok := m.IsInPeer(srcip.String())
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln(file.Header(), "recv from endpoint", addr, "src", srcip, "dst", dstip)
|
||||
}
|
||||
if !ok {
|
||||
logrus.Warnln(file.Header(), "packet from", srcip, "to", dstip, "is refused")
|
||||
return nil
|
||||
}
|
||||
if bin.IsNilInterface(p.endpoint) || !p.endpoint.Equal(addr) {
|
||||
if m.ep.Network() == "tcp" && !addr.Equal(p.endpoint) {
|
||||
logrus.Infoln(file.Header(), "set endpoint of peer", p.peerip, "to", addr.String())
|
||||
p.endpoint = addr
|
||||
} else { // others are all no status link
|
||||
logrus.Infoln(file.Header(), "set endpoint of peer", p.peerip, "to", addr.String())
|
||||
p.endpoint = addr
|
||||
}
|
||||
}
|
||||
p.lastalive.Store(time.Now().UnixNano())
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
base14 "github.com/fumiama/go-base16384"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Read 从 peer 收包
|
||||
func (l *Link) Read() *head.Packet {
|
||||
func (l *Link) Read() LinkData {
|
||||
return <-l.pipe
|
||||
}
|
||||
|
||||
func (m *Me) wait(data []byte) *head.Packet {
|
||||
if len(data) < 60 { // not a valid packet
|
||||
return nil
|
||||
func (m *Me) wait(data []byte, addr p2p.EndPoint) (h head.PacketBytes) {
|
||||
if len(data) < int(head.PacketHeadLen)+8 { // not a valid packet
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv] invalid data len", len(data))
|
||||
}
|
||||
return
|
||||
}
|
||||
bound := 64
|
||||
endl := "..."
|
||||
@@ -25,58 +33,157 @@ func (m *Me) wait(data []byte) *head.Packet {
|
||||
bound = len(data)
|
||||
endl = "."
|
||||
}
|
||||
logrus.Debugln("[recv] data bytes", hex.EncodeToString(data[:bound]), endl)
|
||||
data = m.xordec(data)
|
||||
logrus.Debugln("[recv] data xored", hex.EncodeToString(data[:bound]), endl)
|
||||
flags := head.Flags(data)
|
||||
if !flags.IsValid() {
|
||||
logrus.Debugln("[recv] drop invalid flags packet:", hex.EncodeToString(data[11:12]), hex.EncodeToString(data[10:11]))
|
||||
return nil
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv] data bytes, len", len(data), "val", hex.EncodeToString(data[:bound]), endl)
|
||||
}
|
||||
crc := binary.LittleEndian.Uint64(data[52:60])
|
||||
if m.recved.Get(crc) { // 是重放攻击
|
||||
logrus.Warnln("[recv] ignore duplicated crc packet", strconv.FormatUint(crc, 16))
|
||||
return nil
|
||||
}
|
||||
logrus.Debugln("[recv]", len(data), "bytes data with flag", hex.EncodeToString(data[11:12]), hex.EncodeToString(data[10:11]))
|
||||
if flags.IsSingle() || flags.NoFrag() {
|
||||
h := head.SelectPacket()
|
||||
_, err := h.Unmarshal(data)
|
||||
if err != nil {
|
||||
logrus.Errorln("[recv] unmarshal err:", err)
|
||||
return nil
|
||||
if m.base14 {
|
||||
w := bin.SelectWriter()
|
||||
_, err := io.Copy(w, base14.NewDecoder(bytes.NewReader(data)))
|
||||
if err != nil { // not a valid packet
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv] decode base14 err:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
m.recved.Set(crc, true)
|
||||
return h
|
||||
data = w.ToBytes().Copy().Trans()
|
||||
w.Destroy()
|
||||
if len(data) < bound {
|
||||
bound = len(data)
|
||||
endl = "."
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv] data b14ed, len", len(data), "val", hex.EncodeToString(data[:bound]), endl)
|
||||
}
|
||||
if len(data) < int(head.PacketHeadLen)+8 { // not a valid packet
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv] invalid data len", len(data))
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
seq, data := m.xordec(data) // inplace decoding
|
||||
if len(data) < bound {
|
||||
bound = len(data)
|
||||
endl = "."
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv] data xored, len", len(data), "val", hex.EncodeToString(data[:bound]), endl)
|
||||
}
|
||||
header, err := head.ParsePacketHeader(data)
|
||||
if err != nil { // not a valid packet
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv] invalid packet header:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugf("[recv] packet seq %08x", seq)
|
||||
}
|
||||
crc := uint64(0)
|
||||
header.B(func(_ []byte, p *head.Packet) {
|
||||
crc = p.CRC64()
|
||||
})
|
||||
if _, got := m.recved.GetOrSet(uint64(seq)^crc, struct{}{}); got {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv] ignore duplicated seq^crc packet, seq", strconv.FormatUint(uint64(seq), 16), "crc", strconv.FormatUint(crc, 16))
|
||||
}
|
||||
header.ManualDestroy()
|
||||
return
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
header.B(func(_ []byte, p *head.Packet) {
|
||||
logrus.Debugln(
|
||||
"[recv]", strconv.FormatUint(uint64(seq), 16),
|
||||
len(data), "bytes data with protoflag", p.Proto,
|
||||
"offset", p.Offset,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
hashd := data[20:52]
|
||||
hsh := *(*[32]byte)(*(*unsafe.Pointer)(unsafe.Pointer(&hashd)))
|
||||
h := m.recving.Get(hsh)
|
||||
if h != nil {
|
||||
logrus.Debugln("[recv] get another frag part of", hex.EncodeToString(hashd))
|
||||
ok, err := h.Unmarshal(data)
|
||||
if err == nil {
|
||||
if ok {
|
||||
m.recving.Delete(hsh)
|
||||
m.recved.Set(crc, true)
|
||||
logrus.Debugln("[recv] all parts of", hex.EncodeToString(hashd), "has reached")
|
||||
return h
|
||||
}
|
||||
} else {
|
||||
h.Put()
|
||||
logrus.Errorln("[recv] unmarshal err:", err)
|
||||
ok := false
|
||||
header.B(func(buf []byte, p *head.Packet) {
|
||||
peer := m.extractPeer(p.Src(), p.Dst(), addr)
|
||||
if peer == nil {
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
return nil
|
||||
if !peer.IsToMe(p.Dst()) { // 提前处理转发
|
||||
ok = true
|
||||
if !peer.allowtrans {
|
||||
logrus.Warnln("[recv] refused to trans packet to", p.Dst().String()+":"+strconv.Itoa(int(p.DstPort)))
|
||||
return
|
||||
}
|
||||
// 转发
|
||||
lnk := m.router.NextHop(p.Dst().String())
|
||||
if lnk == nil {
|
||||
logrus.Warnln("[recv] transfer drop packet: nil nexthop")
|
||||
return
|
||||
}
|
||||
if head.DecTTL(data) { // need drop
|
||||
logrus.Warnln("[recv] transfer drop packet: zero ttl")
|
||||
return
|
||||
}
|
||||
go lnk.write2peer(pbuf.ParseBytes(data...).Ignore().Copy(), seq)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[listen] trans", len(data), "bytes packet to", p.Dst().String()+":"+strconv.Itoa(int(p.DstPort)))
|
||||
}
|
||||
return
|
||||
}
|
||||
if !p.Proto.HasMore() {
|
||||
ok = true
|
||||
if !p.WriteDataSegment(data, buf) {
|
||||
logrus.Errorln("[recv]", strconv.FormatUint(uint64(seq), 16), "unexpected !ok")
|
||||
return
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv]", strconv.FormatUint(uint64(seq), 16), len(data), "bytes full data waited")
|
||||
}
|
||||
h = header
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if ok {
|
||||
if !h.HasInit() {
|
||||
header.ManualDestroy()
|
||||
}
|
||||
return
|
||||
}
|
||||
logrus.Debugln("[recv] get new frag part of", hex.EncodeToString(hashd))
|
||||
h = head.SelectPacket()
|
||||
_, err := h.Unmarshal(data)
|
||||
if err != nil {
|
||||
h.Put()
|
||||
logrus.Errorln("[recv] unmarshal err:", err)
|
||||
return nil
|
||||
|
||||
h, got := m.recving.GetOrSet(uint16(seq), header)
|
||||
if got {
|
||||
if !h.HasInit() {
|
||||
// GetOrSet found an expired entry: it deleted it and
|
||||
// returned zero-value while reporting got=true, but did
|
||||
// NOT store our header. Re-store it now.
|
||||
m.recving.Set(uint16(seq), header)
|
||||
h = header
|
||||
got = false
|
||||
} else if h == header {
|
||||
panic("unexpected multi-put found")
|
||||
} else {
|
||||
header.ManualDestroy()
|
||||
}
|
||||
}
|
||||
m.recving.Set(hsh, h)
|
||||
return nil
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv]", strconv.FormatUint(uint64(seq&0xffff), 16), "get frag part isnew:", !got)
|
||||
}
|
||||
ok = false
|
||||
h.B(func(buf []byte, p *head.Packet) {
|
||||
ok = p.WriteDataSegment(data, buf)
|
||||
if !ok {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv]", strconv.FormatUint(uint64(seq&0xffff), 16), "wait other frag parts isnew:", !got)
|
||||
}
|
||||
return
|
||||
}
|
||||
m.recving.Delete(uint16(seq))
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[recv]", strconv.FormatUint(uint64(seq&0xffff), 16), "all parts has reached")
|
||||
}
|
||||
})
|
||||
if !ok {
|
||||
return head.PacketBytes{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
"github.com/FloatTech/ttl"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
)
|
||||
|
||||
type Router struct {
|
||||
@@ -45,7 +47,9 @@ func (r *Router) SetDefault(l *Link) {
|
||||
func (r *Router) NextHop(ip string) (l *Link) {
|
||||
l = r.cache.Get(ip)
|
||||
if l != nil {
|
||||
logrus.Debugln("[router] get cached nexthop to", ip, "link", l)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[router] get cached nexthop to", ip, "link", l)
|
||||
}
|
||||
return
|
||||
}
|
||||
ipb := net.ParseIP(ip)
|
||||
@@ -62,7 +66,9 @@ func (r *Router) NextHop(ip string) (l *Link) {
|
||||
for _, c := range r.list {
|
||||
if c.Contains(ipb) {
|
||||
l = r.table[c.String()]
|
||||
logrus.Debugln("[router] get nexthop to", ipb, "-->", c, "link", l)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[router] get nexthop to", ipb, "-->", c, "link", l)
|
||||
}
|
||||
r.cache.Set(ip, l)
|
||||
return l
|
||||
}
|
||||
|
||||
@@ -1,113 +1,154 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
base14 "github.com/fumiama/go-base16384"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
)
|
||||
|
||||
// WriteAndPut 向 peer 发包并将包放回缓存池
|
||||
func (l *Link) WriteAndPut(p *head.Packet, istransfer bool) (n int, err error) {
|
||||
defer p.Put()
|
||||
var (
|
||||
ErrDropBigDontFragPkt = errors.New("drop big don't fragmnet packet")
|
||||
ErrTTL = errors.New("ttl exceeded")
|
||||
)
|
||||
|
||||
func randseq(i uint16) uint32 {
|
||||
var buf [4]byte
|
||||
_, _ = crand.Read(buf[:2])
|
||||
binary.BigEndian.PutUint16(buf[2:4], i)
|
||||
return binary.BigEndian.Uint32(buf[:])
|
||||
}
|
||||
|
||||
// WritePacket 基于 data 向 peer 发包
|
||||
//
|
||||
// data 可为空, 因为不保证可达所以不返回错误。
|
||||
func (l *Link) WritePacket(proto uint8, data []byte, ttl uint8) {
|
||||
teatype := l.randkeyidx()
|
||||
sndcnt := uint16(l.incgetsndcnt())
|
||||
mtu := l.mtu
|
||||
if l.mturandomrange > 0 {
|
||||
mtu -= uint16(rand.Intn(int(l.mturandomrange)))
|
||||
}
|
||||
logrus.Debugln("[send] mtu:", mtu, ", addt:", sndcnt&0x07ff, ", key index:", teatype)
|
||||
if !istransfer {
|
||||
l.encrypt(p, sndcnt, teatype)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[send] write mtu:", mtu, ", addt:", sndcnt&0x07ff, ", key index:", teatype, ", data len:", len(data))
|
||||
}
|
||||
delta := (int(mtu) - 60) & 0x0000fff8
|
||||
if delta <= 0 {
|
||||
logrus.Warnln("[send] reset invalid data frag len", delta, "to 8")
|
||||
delta = 8
|
||||
}
|
||||
if len(p.Data) <= delta {
|
||||
return l.write(p, teatype, sndcnt, uint32(len(p.Data)), 0, istransfer, false)
|
||||
}
|
||||
if istransfer && p.Flags.DontFrag() && len(p.Data) > delta {
|
||||
return 0, errors.New("drop don't fragmnet big trans packet")
|
||||
}
|
||||
data := p.Data
|
||||
ttl := p.TTL
|
||||
totl := uint32(len(data))
|
||||
pos := 0
|
||||
packet := head.SelectPacket()
|
||||
*packet = *p
|
||||
for ; int(totl)-pos > delta; pos += delta {
|
||||
logrus.Debugln("[send] split frag [", pos, "~", pos+delta, "], remain:", int(totl)-pos-delta)
|
||||
packet.Data = data[:delta]
|
||||
cnt, err := l.write(packet, teatype, sndcnt, totl, uint16(pos>>3), istransfer, true)
|
||||
n += cnt
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
data = data[delta:]
|
||||
packet.TTL = ttl
|
||||
}
|
||||
packet.Put()
|
||||
if len(data) > 0 {
|
||||
p.Data = data
|
||||
cnt := 0
|
||||
cnt, err = l.write(p, teatype, sndcnt, totl, uint16(pos>>3), istransfer, false)
|
||||
n += cnt
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (l *Link) encrypt(p *head.Packet, sndcnt uint16, teatype uint8) {
|
||||
p.FillHash()
|
||||
logrus.Debugln("[send] data len before encrypt:", len(p.Data))
|
||||
pb := head.NewPacketBuilder().
|
||||
Src(l.me.me, l.me.srcport).Dst(l.peerip, l.me.dstport).
|
||||
Proto(proto).TTL(ttl).With(data)
|
||||
if l.usezstd {
|
||||
w := helper.SelectWriter()
|
||||
defer helper.PutWriter(w)
|
||||
enc, _ := zstd.NewWriter(w, zstd.WithEncoderLevel(zstd.SpeedFastest))
|
||||
_, _ = io.Copy(enc, bytes.NewReader(p.Data))
|
||||
enc.Close()
|
||||
p.Data = w.Bytes()
|
||||
logrus.Debugln("[send] data len after zstd:", len(p.Data))
|
||||
pb.Zstd()
|
||||
}
|
||||
pb = pb.Hash()
|
||||
var pktb *head.PacketBuilder
|
||||
if l.keys[0] == nil {
|
||||
pktb = pb.Plain(teatype, sndcnt&0x07ff)
|
||||
} else {
|
||||
pktb = pb.Seal(l.keys[teatype], teatype, sndcnt&0x07ff)
|
||||
}
|
||||
bs := pktb.Split(int(mtu), false)
|
||||
pktb.Destroy()
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[send] split packet into", len(bs), "parts")
|
||||
}
|
||||
for _, b := range bs { //TODO: impl. nofrag
|
||||
go l.write2peer(head.BuildPacketFromBytes(b), randseq(sndcnt))
|
||||
b.ManualDestroy()
|
||||
}
|
||||
p.Data = l.Encode(teatype, sndcnt&0x07ff, p.Data)
|
||||
logrus.Debugln("[send] data len after xchacha20:", len(p.Data), "addt:", sndcnt)
|
||||
}
|
||||
|
||||
// write 向 peer 发一个包
|
||||
func (l *Link) write(p *head.Packet, teatype uint8, additional uint16, datasz uint32, offset uint16, istransfer, hasmore bool) (n int, err error) {
|
||||
var d []byte
|
||||
var cl func()
|
||||
if istransfer {
|
||||
d, cl = p.Marshal(nil, teatype, additional, 0, 0, false, false)
|
||||
} else {
|
||||
d, cl = p.Marshal(l.me.me, teatype, additional, datasz, offset, false, hasmore)
|
||||
// write2peer 计算 xor + b14 后向 peer 发包
|
||||
//
|
||||
// 因为不保证可达所以不返回错误。
|
||||
func (l *Link) write2peer(b pbuf.Bytes, seq uint32) {
|
||||
defer b.ManualDestroy()
|
||||
if l.doublepacket {
|
||||
err := l.write2peer1(b, seq)
|
||||
if err != nil {
|
||||
logrus.Warnln("[send] double wr2peer", l.peerip, "err:", err)
|
||||
}
|
||||
}
|
||||
if d == nil {
|
||||
return 0, errors.New("[send] ttl exceeded")
|
||||
err := l.write2peer1(b, seq)
|
||||
if err != nil {
|
||||
logrus.Warnln("[send] wr2peer", l.peerip, "err:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// write2peer1 计算 xor + b14 后向 peer 发一个包
|
||||
func (l *Link) write2peer1(b pbuf.Bytes, seq uint32) (err error) {
|
||||
peerep := l.endpoint
|
||||
if peerep == nil {
|
||||
return 0, errors.New("[send] nil endpoint of " + p.Dst.String())
|
||||
if bin.IsNilInterface(peerep) {
|
||||
return errors.New("nil endpoint of " + l.peerip.String())
|
||||
}
|
||||
bound := 64
|
||||
endl := "..."
|
||||
if len(d) < bound {
|
||||
bound = len(d)
|
||||
endl = "."
|
||||
|
||||
conn := l.me.conn
|
||||
if conn == nil {
|
||||
return io.ErrClosedPipe
|
||||
}
|
||||
isnewb := false
|
||||
b.V(func(data []byte) {
|
||||
if config.ShowDebugLog {
|
||||
bound := 64
|
||||
endl := "..."
|
||||
if len(data) < bound {
|
||||
bound = len(data)
|
||||
endl = "."
|
||||
}
|
||||
logrus.Debugln("[send] crc seq", fmt.Sprintf("%08x", seq), "raw data bytes", hex.EncodeToString(data[:bound]), endl)
|
||||
}
|
||||
b = pbuf.ParseBytes(l.me.xorenc(data, seq)...).Ignore()
|
||||
isnewb = true
|
||||
})
|
||||
if config.ShowDebugLog {
|
||||
bound := 64
|
||||
endl := "..."
|
||||
if b.Len() < bound {
|
||||
bound = b.Len()
|
||||
endl = "."
|
||||
}
|
||||
b.V(func(b []byte) {
|
||||
logrus.Debugln("[send] crc seq", fmt.Sprintf("%08x", seq), "xored data bytes", hex.EncodeToString(b[:bound]), endl)
|
||||
})
|
||||
}
|
||||
if l.me.base14 {
|
||||
b.V(func(data []byte) {
|
||||
old := b
|
||||
b = pbuf.ParseBytes(base14.Encode(data)...).Ignore()
|
||||
if isnewb {
|
||||
old.ManualDestroy()
|
||||
}
|
||||
isnewb = true
|
||||
if config.ShowDebugLog {
|
||||
bound := 64
|
||||
endl := "..."
|
||||
if b.Len() < bound {
|
||||
bound = b.Len()
|
||||
endl = "."
|
||||
}
|
||||
b.V(func(b []byte) {
|
||||
logrus.Debugln("[send] crc seq", fmt.Sprintf("%08x", seq), "b14ed data bytes", hex.EncodeToString(b[:bound]), endl)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
b.V(func(b []byte) {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[send] crc seq", fmt.Sprintf("%08x", seq), "write", len(b), "bytes data from ep", conn.LocalAddr(), "to", peerep)
|
||||
}
|
||||
_, err = conn.WriteToPeer(b, peerep)
|
||||
})
|
||||
if isnewb {
|
||||
b.ManualDestroy()
|
||||
}
|
||||
logrus.Debugln("[send] write", len(d), "bytes data from ep", l.me.udpconn.LocalAddr(), "to", peerep, "offset:", fmt.Sprintf("%04x", offset))
|
||||
logrus.Debugln("[send] data bytes", hex.EncodeToString(d[:bound]), endl)
|
||||
d = l.me.xorenc(d)
|
||||
logrus.Debugln("[send] data xored", hex.EncodeToString(d[:bound]), endl)
|
||||
n, err = l.me.udpconn.WriteToUDP(d, peerep)
|
||||
cl()
|
||||
return
|
||||
}
|
||||
|
||||
44
gold/p2p/define.go
Normal file
44
gold/p2p/define.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package p2p
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/RomiChan/syncx"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEndpointTypeMistatch = errors.New("endpoint type mismatch")
|
||||
)
|
||||
|
||||
type Initializer func(endpoint string, configs ...any) (EndPoint, error)
|
||||
|
||||
var factory syncx.Map[string, Initializer]
|
||||
|
||||
func Register(network string, initializer Initializer) (actual Initializer, hasexist bool) {
|
||||
return factory.LoadOrStore(network, initializer)
|
||||
}
|
||||
|
||||
type EndPoint interface {
|
||||
fmt.Stringer
|
||||
Network() string
|
||||
Equal(EndPoint) bool
|
||||
Listen() (Conn, error)
|
||||
}
|
||||
|
||||
func NewEndPoint(network, endpoint string, configs ...any) (EndPoint, error) {
|
||||
initializer, ok := factory.Load(network)
|
||||
if !ok {
|
||||
return nil, errors.New("network " + network + " not found")
|
||||
}
|
||||
return initializer(endpoint, configs...)
|
||||
}
|
||||
|
||||
type Conn interface {
|
||||
io.Closer
|
||||
fmt.Stringer // basically, the local address string
|
||||
LocalAddr() EndPoint
|
||||
ReadFromPeer([]byte) (int, EndPoint, error)
|
||||
WriteToPeer([]byte, EndPoint) (int, error)
|
||||
}
|
||||
295
gold/p2p/icmp/icmp.go
Normal file
295
gold/p2p/icmp/icmp.go
Normal file
@@ -0,0 +1,295 @@
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/RomiChan/syncx"
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidBodyType = errors.New("invalid body type")
|
||||
)
|
||||
|
||||
var (
|
||||
echoid = os.Getpid()
|
||||
)
|
||||
|
||||
// seqFIFO is a FIFO queue that generates new sequence numbers when empty.
|
||||
type seqFIFO struct {
|
||||
mu sync.Mutex
|
||||
q []uintptr
|
||||
next *atomic.Uintptr
|
||||
}
|
||||
|
||||
func (f *seqFIFO) Get() uintptr {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if len(f.q) > 0 {
|
||||
v := f.q[0]
|
||||
copy(f.q, f.q[1:])
|
||||
f.q = f.q[:len(f.q)-1]
|
||||
return v
|
||||
}
|
||||
return f.next.Add(1)
|
||||
}
|
||||
|
||||
func (f *seqFIFO) Put(v uintptr) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if len(f.q) == 0 {
|
||||
f.q = make([]uintptr, 1, 128)
|
||||
f.q[0] = v
|
||||
return
|
||||
}
|
||||
if len(f.q) < cap(f.q) {
|
||||
f.q = append(f.q, v)
|
||||
return
|
||||
}
|
||||
copy(f.q, f.q[1:])
|
||||
f.q[len(f.q)-1] = v
|
||||
}
|
||||
|
||||
// peerState holds per-peer ICMP echo state within a Conn.
|
||||
type peerState struct {
|
||||
id int
|
||||
seq atomic.Uintptr
|
||||
seqfifo seqFIFO
|
||||
}
|
||||
|
||||
func newPeerState() *peerState {
|
||||
ps := &peerState{}
|
||||
ps.seqfifo.next = &ps.seq
|
||||
return ps
|
||||
}
|
||||
|
||||
type EndPoint netip.Addr
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return (*netip.Addr)(ep).String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return "icmp"
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
ipep2, ok := ep2.(*EndPoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ipep1 := ep
|
||||
return (*netip.Addr)(ipep1).Compare(*(*netip.Addr)(ipep2)) == 0
|
||||
}
|
||||
|
||||
// network get ipv4/ipv6 info and choose different options.
|
||||
func (ep *EndPoint) network() (string, *netip.Addr) {
|
||||
nw := "ip4:icmp"
|
||||
if (*netip.Addr)(ep).Is6() {
|
||||
nw = "ip6:ipv6-icmp"
|
||||
}
|
||||
return nw, (*netip.Addr)(ep)
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
nw, addr := ep.network()
|
||||
conn, err := icmp.ListenPacket(nw, addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Conn{inner: conn}, nil
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
inner *icmp.PacketConn
|
||||
peers syncx.Map[netip.Addr, *peerState]
|
||||
}
|
||||
|
||||
func (conn *Conn) getOrCreatePeerState(addr netip.Addr) *peerState {
|
||||
if ps, ok := conn.peers.Load(addr); ok {
|
||||
return ps
|
||||
}
|
||||
ps := newPeerState()
|
||||
actual, _ := conn.peers.LoadOrStore(addr, ps)
|
||||
return actual
|
||||
}
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
return conn.inner.Close()
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return conn.inner.LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
eps := conn.inner.LocalAddr().String()
|
||||
addr, err := netip.ParseAddrPort(eps)
|
||||
if err == nil {
|
||||
eps = addr.Addr().String()
|
||||
}
|
||||
ep, _ := NewEndpoint(eps)
|
||||
return ep
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (n int, ep p2p.EndPoint, err error) {
|
||||
buf := pbuf.NewBytes(8192)
|
||||
defer buf.ManualDestroy()
|
||||
var ipaddr netip.Addr
|
||||
buf.V(func(data []byte) {
|
||||
ok := false
|
||||
var msg *icmp.Message
|
||||
for !ok {
|
||||
var (
|
||||
cnt int
|
||||
addr net.Addr
|
||||
)
|
||||
cnt, addr, err = conn.inner.ReadFrom(data)
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv ReadFrom err:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ipaddr, err = netip.ParseAddr(addr.String())
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv ParseAddr err:", err, ", addr:", addr)
|
||||
}
|
||||
return
|
||||
}
|
||||
ep, err = NewEndpoint(ipaddr.String())
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv NewEndpoint err:", err, ", addr:", addr)
|
||||
}
|
||||
return
|
||||
}
|
||||
proton := ipv4.ICMPTypeEcho.Protocol()
|
||||
if ipaddr.Is6() {
|
||||
proton = ipv6.ICMPTypeEchoRequest.Protocol()
|
||||
}
|
||||
|
||||
msg, err = icmp.ParseMessage(proton, data[:cnt])
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv ParseMessage err:", err, ", addr:", addr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ok = msg.Type == ipv4.ICMPTypeEcho || msg.Type == ipv4.ICMPTypeEchoReply
|
||||
if ipaddr.Is6() {
|
||||
ok = msg.Type == ipv6.ICMPTypeEchoRequest || msg.Type == ipv6.ICMPTypeEchoReply
|
||||
}
|
||||
ok = ok && msg.Code == 1
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv from", ipaddr, ", is valid:", ok)
|
||||
}
|
||||
}
|
||||
body, okk := msg.Body.(*icmp.Echo)
|
||||
if !okk {
|
||||
err = ErrInvalidBodyType
|
||||
return
|
||||
}
|
||||
if msg.Type == ipv4.ICMPTypeEcho || msg.Type == ipv6.ICMPTypeEchoRequest {
|
||||
ps := conn.getOrCreatePeerState(ipaddr)
|
||||
ps.id = body.ID
|
||||
ps.seq.Store(uintptr(body.Seq))
|
||||
ps.seqfifo.Put(uintptr(body.Seq))
|
||||
}
|
||||
n = copy(b, body.Data)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[icmp] recv", n, "bytes data from", ipaddr)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||
icmpep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
addr := (*netip.Addr)(icmpep)
|
||||
ps := conn.getOrCreatePeerState(*addr)
|
||||
seq := int(ps.seqfifo.Get())
|
||||
id := ps.id
|
||||
isrequest := id == 0
|
||||
if isrequest {
|
||||
id = echoid
|
||||
}
|
||||
var (
|
||||
ip net.IP
|
||||
msg icmp.Message
|
||||
)
|
||||
if addr.Is4() {
|
||||
x := addr.As4()
|
||||
ip = x[:]
|
||||
msg = icmp.Message{
|
||||
Code: 1,
|
||||
Body: &icmp.Echo{
|
||||
ID: id,
|
||||
Seq: seq,
|
||||
Data: b,
|
||||
},
|
||||
}
|
||||
if isrequest {
|
||||
msg.Type = ipv4.ICMPTypeEcho
|
||||
} else {
|
||||
msg.Type = ipv4.ICMPTypeEchoReply
|
||||
}
|
||||
} else {
|
||||
x := addr.As16()
|
||||
ip = x[:]
|
||||
msg = icmp.Message{
|
||||
Code: 1,
|
||||
Body: &icmp.Echo{
|
||||
ID: id,
|
||||
Seq: seq,
|
||||
Data: b,
|
||||
},
|
||||
}
|
||||
if isrequest {
|
||||
msg.Type = ipv6.ICMPTypeEchoRequest
|
||||
} else {
|
||||
msg.Type = ipv6.ICMPTypeEchoReply
|
||||
}
|
||||
}
|
||||
buf := pbuf.NewBytes(8192)
|
||||
defer buf.ManualDestroy()
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
n int
|
||||
)
|
||||
buf.V(func(bin []byte) {
|
||||
data, err = msg.Marshal(bin[:0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = conn.inner.WriteTo(data, &net.IPAddr{
|
||||
IP: ip,
|
||||
Zone: addr.Zone(),
|
||||
})
|
||||
if err == nil {
|
||||
n = len(b)
|
||||
}
|
||||
})
|
||||
return n, err
|
||||
}
|
||||
26
gold/p2p/icmp/init.go
Normal file
26
gold/p2p/icmp/init.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Package icmp for non-privileged datagram-oriented ICMP endpoints,
|
||||
// currently only Darwin and Linux support this.
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
||||
addr, err := netip.ParseAddr(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*EndPoint)(&addr), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := file.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
35
gold/p2p/ip/init.go
Normal file
35
gold/p2p/ip/init.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package ip
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
func NewEndpoint(endpoint string, configs ...any) (p2p.EndPoint, error) {
|
||||
addr, err := netip.ParseAddr(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ptcl := uint(0x6C) // IPComp https://datatracker.ietf.org/doc/html/rfc3173
|
||||
if len(configs) > 0 {
|
||||
ptcl = configs[0].(uint)
|
||||
}
|
||||
return &EndPoint{
|
||||
addr: &net.IPAddr{
|
||||
IP: addr.AsSlice(),
|
||||
Zone: addr.Zone(),
|
||||
},
|
||||
ptcl: ptcl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := file.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
82
gold/p2p/ip/ip.go
Normal file
82
gold/p2p/ip/ip.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package ip
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
)
|
||||
|
||||
type EndPoint struct {
|
||||
addr *net.IPAddr
|
||||
ptcl uint
|
||||
}
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return ep.addr.String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return ep.addr.Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
ipep2, ok := ep2.(*EndPoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ipep1 := ep
|
||||
return ipep1.addr.IP.Equal(ipep2.addr.IP) &&
|
||||
ipep1.addr.Zone == ipep2.addr.Zone
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
conn, err := net.ListenIP(
|
||||
"ip:"+strconv.Itoa(int(ep.ptcl)),
|
||||
ep.addr,
|
||||
)
|
||||
return &Conn{
|
||||
ep: ep,
|
||||
conn: conn,
|
||||
}, err
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
ep *EndPoint
|
||||
conn *net.IPConn
|
||||
}
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
return conn.conn.Close()
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return conn.conn.LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
ep, _ := NewEndpoint(conn.conn.LocalAddr().String())
|
||||
return ep
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := conn.conn.ReadFromIP(b)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return n, &EndPoint{
|
||||
addr: addr,
|
||||
ptcl: conn.ep.ptcl,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||
ipep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
return conn.conn.WriteToIP(b, ipep.addr)
|
||||
}
|
||||
49
gold/p2p/tcp/init.go
Normal file
49
gold/p2p/tcp/init.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DialTimeout time.Duration
|
||||
PeersTimeout time.Duration
|
||||
KeepInterval time.Duration
|
||||
ReceiveChannelSize int
|
||||
}
|
||||
|
||||
func NewEndpoint(endpoint string, configs ...any) (p2p.EndPoint, error) {
|
||||
return newEndpoint(endpoint, configs...)
|
||||
}
|
||||
|
||||
func newEndpoint(endpoint string, configs ...any) (*EndPoint, error) {
|
||||
var cfg *Config
|
||||
if len(configs) == 0 || configs[0] == nil {
|
||||
cfg = &Config{}
|
||||
} else {
|
||||
cfg = configs[0].(*Config)
|
||||
}
|
||||
addr, err := netip.ParseAddrPort(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EndPoint{
|
||||
addr: net.TCPAddrFromAddrPort(addr),
|
||||
dialtimeout: cfg.DialTimeout,
|
||||
peerstimeout: cfg.PeersTimeout,
|
||||
keepinterval: cfg.KeepInterval,
|
||||
recvchansize: cfg.ReceiveChannelSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := file.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
134
gold/p2p/tcp/pdu.go
Normal file
134
gold/p2p/tcp/pdu.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidMagic = errors.New("invalid magic")
|
||||
)
|
||||
|
||||
type packetType uint8
|
||||
|
||||
const (
|
||||
packetTypeKeepAlive packetType = iota
|
||||
packetTypeNormal
|
||||
packetTypeSubKeepAlive
|
||||
packetTypeTop
|
||||
)
|
||||
|
||||
var (
|
||||
magicbuf = []byte("GET ")
|
||||
magic = binary.LittleEndian.Uint32(magicbuf)
|
||||
)
|
||||
|
||||
type packet struct {
|
||||
typ packetType
|
||||
len uint16
|
||||
dat []byte
|
||||
io.ReaderFrom
|
||||
io.WriterTo
|
||||
}
|
||||
|
||||
func (p *packet) pack() *net.Buffers {
|
||||
return &net.Buffers{magicbuf, bin.NewWriterF(func(w *bin.Writer) {
|
||||
w.WriteByte(byte(p.typ))
|
||||
w.WriteUInt16(p.len)
|
||||
}), p.dat}
|
||||
}
|
||||
|
||||
func (p *packet) Read(_ []byte) (int, error) {
|
||||
panic("stub")
|
||||
}
|
||||
|
||||
func (p *packet) Write(_ []byte) (int, error) {
|
||||
panic("stub")
|
||||
}
|
||||
|
||||
func (p *packet) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
var buf [4]byte
|
||||
cnt, err := io.ReadFull(r, buf[:])
|
||||
n = int64(cnt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if binary.LittleEndian.Uint32(buf[:]) != magic {
|
||||
err = ErrInvalidMagic
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugf("[tcp] expect magic %08x but got %08x", magic, binary.LittleEndian.Uint32(buf[:]))
|
||||
}
|
||||
return
|
||||
}
|
||||
cnt, err = io.ReadFull(r, buf[:3])
|
||||
n += int64(cnt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.typ = packetType(buf[0])
|
||||
p.len = binary.LittleEndian.Uint16(buf[1:3])
|
||||
w := bin.SelectWriter()
|
||||
copied, err := io.CopyN(w, r, int64(p.len))
|
||||
n += copied
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.dat = w.ToBytes().Copy().Trans()
|
||||
w.Destroy()
|
||||
return
|
||||
}
|
||||
|
||||
func (p *packet) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return io.Copy(w, p.pack())
|
||||
}
|
||||
|
||||
func isvalid(tcpconn *net.TCPConn, timeout time.Duration) (issub, ok bool) {
|
||||
pckt := packet{}
|
||||
|
||||
stopch := make(chan struct{})
|
||||
t := time.AfterFunc(timeout, func() {
|
||||
stopch <- struct{}{}
|
||||
})
|
||||
|
||||
var err error
|
||||
copych := make(chan struct{})
|
||||
go func() {
|
||||
_, err = io.Copy(&pckt, tcpconn)
|
||||
copych <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-stopch:
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] validate recv from", tcpconn.RemoteAddr(), "timeout")
|
||||
}
|
||||
return
|
||||
case <-copych:
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] validate recv from", tcpconn.RemoteAddr(), "err:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if pckt.typ != packetTypeKeepAlive && pckt.typ != packetTypeSubKeepAlive {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] validate got invalid typ", pckt.typ, "from", tcpconn.RemoteAddr())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] passed validate recv from", tcpconn.RemoteAddr())
|
||||
}
|
||||
return pckt.typ == packetTypeSubKeepAlive, true
|
||||
}
|
||||
508
gold/p2p/tcp/tcp.go
Normal file
508
gold/p2p/tcp/tcp.go
Normal file
@@ -0,0 +1,508 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/ttl"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
)
|
||||
|
||||
type EndPoint struct {
|
||||
addr *net.TCPAddr
|
||||
dialtimeout time.Duration
|
||||
peerstimeout time.Duration
|
||||
keepinterval time.Duration
|
||||
recvchansize int
|
||||
}
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return ep.addr.String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return ep.addr.Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
tcpep2, ok := ep2.(*EndPoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
tcpep1 := ep
|
||||
return tcpep1.addr.IP.Equal(tcpep2.addr.IP) &&
|
||||
tcpep1.addr.Port == tcpep2.addr.Port &&
|
||||
tcpep1.addr.Zone == tcpep2.addr.Zone
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
lstn, err := net.ListenTCP(ep.addr.Network(), ep.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ep.addr = lstn.Addr().(*net.TCPAddr)
|
||||
peerstimeout := ep.peerstimeout
|
||||
if peerstimeout < time.Second*30 {
|
||||
peerstimeout = time.Second * 30
|
||||
}
|
||||
chansz := ep.recvchansize
|
||||
if chansz < 32 {
|
||||
chansz = 32
|
||||
}
|
||||
conn := &Conn{
|
||||
addr: ep,
|
||||
lstn: lstn,
|
||||
peers: ttl.NewCacheOn(peerstimeout, [4]func(string, *net.TCPConn){
|
||||
func(_ string, t *net.TCPConn) {
|
||||
_ = t.SetLinger(0)
|
||||
_ = t.SetNoDelay(true)
|
||||
}, nil, func(_ string, t *net.TCPConn) {
|
||||
err := t.CloseWrite()
|
||||
if config.ShowDebugLog {
|
||||
if err != nil {
|
||||
logrus.Debugln("[tcp] close write from", t.LocalAddr(), "to", t.RemoteAddr(), "err:", err)
|
||||
} else {
|
||||
logrus.Debugln("[tcp] close write from", t.LocalAddr(), "to", t.RemoteAddr())
|
||||
}
|
||||
}
|
||||
}, nil,
|
||||
}),
|
||||
recv: make(chan *connrecv, chansz),
|
||||
cplk: &sync.Mutex{},
|
||||
sblk: &sync.RWMutex{},
|
||||
}
|
||||
go conn.accept()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
type connrecv struct {
|
||||
addr *EndPoint // cast from tcpconn.RemoteAddr()
|
||||
conn *net.TCPConn
|
||||
pckt packet
|
||||
}
|
||||
|
||||
type subconn struct {
|
||||
cplk sync.Mutex
|
||||
last time.Time // last active time
|
||||
conn *net.TCPConn
|
||||
}
|
||||
|
||||
// Conn 伪装成无状态的有状态连接
|
||||
type Conn struct {
|
||||
addr *EndPoint
|
||||
lstn *net.TCPListener
|
||||
peers *ttl.Cache[string, *net.TCPConn]
|
||||
recv chan *connrecv
|
||||
cplk *sync.Mutex
|
||||
sblk *sync.RWMutex
|
||||
subs []*subconn
|
||||
suberr bool
|
||||
}
|
||||
|
||||
func (conn *Conn) accept() {
|
||||
for {
|
||||
tcpconn, err := conn.lstn.AcceptTCP()
|
||||
if err != nil {
|
||||
if errors.Is(err, net.ErrClosed) { // normal close
|
||||
logrus.Infoln("[tcp] accept of", conn.addr, "got closed")
|
||||
return
|
||||
}
|
||||
if conn.addr == nil || conn.lstn == nil || conn.peers == nil || conn.recv == nil {
|
||||
return
|
||||
}
|
||||
logrus.Warnln("[tcp] accept on", conn.addr, "err:", err)
|
||||
_ = conn.Close()
|
||||
newc, err := conn.addr.Listen()
|
||||
if err != nil {
|
||||
logrus.Warnln("[tcp] re-listen on", conn.addr, "err:", err)
|
||||
return
|
||||
}
|
||||
*conn = *newc.(*Conn)
|
||||
logrus.Infoln("[tcp] re-listen on", conn.addr)
|
||||
continue
|
||||
}
|
||||
go conn.receive(tcpconn, false)
|
||||
}
|
||||
}
|
||||
|
||||
func delsubs(i int, subs []*subconn) []*subconn {
|
||||
tcpconn := subs[i].conn
|
||||
err := tcpconn.CloseWrite()
|
||||
if config.ShowDebugLog {
|
||||
if err != nil {
|
||||
logrus.Debugln("[tcp] close sub write from", tcpconn.LocalAddr(), "to", tcpconn.RemoteAddr(), "err:", err)
|
||||
} else {
|
||||
logrus.Debugln("[tcp] close sub write from", tcpconn.LocalAddr(), "to", tcpconn.RemoteAddr())
|
||||
}
|
||||
}
|
||||
switch i {
|
||||
case 0:
|
||||
subs = subs[1:]
|
||||
case len(subs) - 1:
|
||||
subs = subs[:len(subs)-1]
|
||||
default:
|
||||
subs = append(subs[:i], subs[i+1:]...)
|
||||
}
|
||||
return subs
|
||||
}
|
||||
|
||||
func (conn *Conn) receive(tcpconn *net.TCPConn, hasvalidated bool) {
|
||||
if conn.peers == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ep, _ := newEndpoint(tcpconn.RemoteAddr().String(), &Config{
|
||||
DialTimeout: conn.addr.dialtimeout,
|
||||
PeersTimeout: conn.addr.peerstimeout,
|
||||
KeepInterval: conn.addr.keepinterval,
|
||||
ReceiveChannelSize: conn.addr.recvchansize,
|
||||
})
|
||||
|
||||
issub, ok := false, false
|
||||
|
||||
peerstimeout := conn.addr.peerstimeout
|
||||
if peerstimeout < time.Second*30 {
|
||||
peerstimeout = time.Second * 30
|
||||
}
|
||||
peerstimeout *= 2
|
||||
|
||||
if !hasvalidated {
|
||||
issub, ok = isvalid(tcpconn, peerstimeout)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] accept from", ep, "issub:", issub)
|
||||
}
|
||||
if issub {
|
||||
conn.sblk.Lock()
|
||||
conn.subs = append(conn.subs, &subconn{conn: tcpconn, last: time.Now()})
|
||||
conn.sblk.Unlock()
|
||||
} else {
|
||||
if conn.peers == nil {
|
||||
return
|
||||
}
|
||||
conn.peers.Set(ep.String(), tcpconn)
|
||||
}
|
||||
}
|
||||
|
||||
if issub {
|
||||
defer func() {
|
||||
conn.sblk.Lock()
|
||||
subs := conn.subs
|
||||
for i, sub := range subs {
|
||||
if sub.conn == tcpconn {
|
||||
conn.subs = delsubs(i, conn.subs)
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.sblk.Unlock()
|
||||
}()
|
||||
} else {
|
||||
if conn.peers == nil {
|
||||
return
|
||||
}
|
||||
defer conn.peers.Delete(ep.String())
|
||||
}
|
||||
|
||||
go conn.keep(ep)
|
||||
|
||||
for {
|
||||
r := &connrecv{addr: ep}
|
||||
if conn.addr == nil || conn.lstn == nil || conn.peers == nil || conn.recv == nil {
|
||||
return
|
||||
}
|
||||
if !issub {
|
||||
tcpconn = conn.peers.Get(ep.String())
|
||||
if tcpconn == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
r.conn = tcpconn
|
||||
|
||||
t := time.NewTimer(peerstimeout)
|
||||
|
||||
var err error
|
||||
copych := make(chan struct{})
|
||||
go func() {
|
||||
_, err = io.Copy(&r.pckt, tcpconn)
|
||||
copych <- struct{}{}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] recv from", ep, "timeout")
|
||||
}
|
||||
_ = tcpconn.CloseRead()
|
||||
return
|
||||
case <-copych:
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
if conn.addr == nil || conn.lstn == nil || conn.peers == nil || conn.recv == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] recv from", ep, "err:", err)
|
||||
}
|
||||
if errors.Is(err, net.ErrClosed) ||
|
||||
errors.Is(err, io.ErrClosedPipe) ||
|
||||
errors.Is(err, io.EOF) ||
|
||||
errors.Is(err, ErrInvalidMagic) {
|
||||
_ = tcpconn.CloseRead()
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
if r.pckt.typ >= packetTypeTop {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] close reading invalid conn from", ep, "typ", r.pckt.typ, "len", r.pckt.len)
|
||||
}
|
||||
_ = tcpconn.CloseRead()
|
||||
return
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] dispatch packet from", ep, "typ", r.pckt.typ, "len", r.pckt.len)
|
||||
}
|
||||
conn.recv <- r
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) keep(ep *EndPoint) {
|
||||
keepinterval := ep.keepinterval
|
||||
if keepinterval < time.Second*10 {
|
||||
keepinterval = time.Second * 10
|
||||
}
|
||||
t := time.NewTicker(keepinterval)
|
||||
defer t.Stop()
|
||||
for range t.C {
|
||||
if conn.addr == nil || conn.peers == nil {
|
||||
return
|
||||
}
|
||||
tcpconn := conn.peers.Get(ep.String())
|
||||
if tcpconn != nil {
|
||||
_, err := io.Copy(tcpconn, &packet{typ: packetTypeKeepAlive})
|
||||
if conn.addr == nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Warnln("[tcp] keep main conn alive from", conn, "to", ep, "err:", err)
|
||||
conn.peers.Delete(ep.String())
|
||||
} else if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] keep main conn alive from", conn, "to", ep)
|
||||
}
|
||||
}
|
||||
conn.sblk.RLock()
|
||||
subs := conn.subs
|
||||
for i, sub := range subs {
|
||||
if time.Since(sub.last) < keepinterval {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] skip to keep busy sub conn from", conn, "to", ep)
|
||||
}
|
||||
continue
|
||||
}
|
||||
_, err := io.Copy(sub.conn, &packet{typ: packetTypeSubKeepAlive})
|
||||
if conn.addr == nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Warnln("[tcp] keep sub conn alive from", conn, "to", sub.conn.RemoteAddr(), "err:", err)
|
||||
conn.subs = delsubs(i, conn.subs) // del 1 link at once
|
||||
break
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] keep sub conn alive from", conn, "to", ep)
|
||||
}
|
||||
}
|
||||
conn.sblk.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
lstn := conn.lstn
|
||||
peers := conn.peers
|
||||
recv := conn.recv
|
||||
conn.addr = nil
|
||||
conn.lstn = nil
|
||||
conn.peers = nil
|
||||
conn.recv = nil
|
||||
|
||||
if lstn != nil {
|
||||
_ = lstn.Close()
|
||||
}
|
||||
if peers != nil {
|
||||
peers.Destroy()
|
||||
}
|
||||
if recv != nil {
|
||||
close(recv)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return conn.addr.String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
return conn.addr
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
var p *connrecv
|
||||
for {
|
||||
p = <-conn.recv
|
||||
if p == nil {
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
if conn.peers == nil {
|
||||
return 0, nil, net.ErrClosed
|
||||
}
|
||||
conn.peers.Set(p.addr.String(), p.conn)
|
||||
if p.pckt.typ == packetTypeNormal {
|
||||
break
|
||||
}
|
||||
}
|
||||
n := copy(b, p.pckt.dat)
|
||||
return n, p.addr, nil
|
||||
}
|
||||
|
||||
// writeToPeer after acquiring lock
|
||||
func (conn *Conn) writeToPeer(b []byte, tcpep *EndPoint, issub bool) (n int, err error) {
|
||||
retried := false
|
||||
ok := false
|
||||
var (
|
||||
tcpconn *net.TCPConn
|
||||
subc *subconn
|
||||
)
|
||||
RECONNECT:
|
||||
if issub {
|
||||
conn.sblk.RLock()
|
||||
for _, sub := range conn.subs {
|
||||
if sub.cplk.TryLock() {
|
||||
tcpconn = sub.conn
|
||||
subc = sub
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.sblk.RUnlock()
|
||||
} else {
|
||||
tcpconn = conn.peers.Get(tcpep.String())
|
||||
}
|
||||
if tcpconn == nil {
|
||||
dialtimeout := tcpep.dialtimeout
|
||||
if dialtimeout < time.Second {
|
||||
dialtimeout = time.Second
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] dial to", tcpep.addr, "timeout", dialtimeout, "issub", issub)
|
||||
}
|
||||
var cn net.Conn
|
||||
// must use another port to send because there's no exsiting conn
|
||||
cn, err = net.DialTimeout(tcpep.Network(), tcpep.addr.String(), dialtimeout)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tcpconn, ok = cn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return 0, errors.New("expect *net.TCPConn but got " + reflect.ValueOf(cn).Type().String())
|
||||
}
|
||||
pkt := &packet{}
|
||||
if issub {
|
||||
pkt.typ = packetTypeSubKeepAlive
|
||||
} else {
|
||||
pkt.typ = packetTypeKeepAlive
|
||||
}
|
||||
_, err = io.Copy(tcpconn, pkt)
|
||||
if err != nil {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] dial to", tcpep.addr, "issub", issub, "success, but write err:", err)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] dial to", tcpep.addr, "success, local:", tcpconn.LocalAddr(), "issub", issub)
|
||||
}
|
||||
if !issub {
|
||||
conn.peers.Set(tcpep.String(), tcpconn)
|
||||
} else {
|
||||
conn.sblk.Lock()
|
||||
conn.subs = append(conn.subs, &subconn{conn: tcpconn, last: time.Now()})
|
||||
conn.sblk.Unlock()
|
||||
}
|
||||
go conn.receive(tcpconn, true)
|
||||
} else if config.ShowDebugLog {
|
||||
logrus.Debugln("[tcp] reuse tcpconn from", tcpconn.LocalAddr(), "to", tcpconn.RemoteAddr())
|
||||
}
|
||||
cnt, err := io.Copy(tcpconn, &packet{
|
||||
typ: packetTypeNormal,
|
||||
len: uint16(len(b)),
|
||||
dat: b,
|
||||
})
|
||||
if err != nil {
|
||||
if subc == nil {
|
||||
conn.peers.Delete(tcpep.String())
|
||||
} else {
|
||||
conn.sblk.Lock()
|
||||
subs := conn.subs
|
||||
for i, sub := range subs {
|
||||
if sub == subc {
|
||||
conn.subs = delsubs(i, conn.subs)
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.sblk.Unlock()
|
||||
}
|
||||
if !retried {
|
||||
logrus.Warnln("[tcp] reconnect due to write to", tcpconn.RemoteAddr(), "err:", err)
|
||||
retried = true
|
||||
tcpconn = nil
|
||||
goto RECONNECT
|
||||
}
|
||||
}
|
||||
if subc != nil {
|
||||
subc.last = time.Now()
|
||||
subc.cplk.Unlock()
|
||||
}
|
||||
return int(cnt) - 3, err
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (n int, err error) {
|
||||
tcpep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
if len(b) >= 65536 {
|
||||
return 0, errors.New("data size " + strconv.Itoa(len(b)) + " is too large")
|
||||
}
|
||||
locked := conn.cplk.TryLock()
|
||||
if !locked {
|
||||
if !conn.suberr || len(conn.subs) > 0 {
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debug("[tcp] try sub write to", tcpep)
|
||||
}
|
||||
n, err = conn.writeToPeer(b, tcpep, true) // try sub write
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
conn.suberr = true // fast fail
|
||||
}
|
||||
conn.cplk.Lock() // add to main queue
|
||||
}
|
||||
defer conn.cplk.Unlock()
|
||||
return conn.writeToPeer(b, tcpep, false)
|
||||
}
|
||||
25
gold/p2p/udp/init.go
Normal file
25
gold/p2p/udp/init.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
||||
addr, err := netip.ParseAddrPort(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*EndPoint)(net.UDPAddrFromAddrPort(addr)), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := file.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
65
gold/p2p/udp/udp.go
Normal file
65
gold/p2p/udp/udp.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
)
|
||||
|
||||
type EndPoint net.UDPAddr
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return (*net.UDPAddr)(ep).String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return (*net.UDPAddr)(ep).Network()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
udpep2, ok := ep2.(*EndPoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
udpep1 := ep
|
||||
return udpep1.IP.Equal(udpep2.IP) && udpep1.Port == udpep2.Port && udpep1.Zone == udpep2.Zone
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
conn, err := net.ListenUDP((*net.UDPAddr)(ep).Network(), (*net.UDPAddr)(ep))
|
||||
return (*Conn)(conn), err
|
||||
}
|
||||
|
||||
type Conn net.UDPConn
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
return (*net.UDPConn)(conn).Close()
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return (*net.UDPConn)(conn).LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
ep, _ := NewEndpoint((*net.UDPConn)(conn).LocalAddr().String())
|
||||
return ep
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return n, (*EndPoint)(addr), err
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||
udpep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
return (*net.UDPConn)(conn).WriteTo(b, (*net.UDPAddr)(udpep))
|
||||
}
|
||||
27
gold/p2p/udplite/init.go
Normal file
27
gold/p2p/udplite/init.go
Normal file
@@ -0,0 +1,27 @@
|
||||
//go:build !darwin && !windows
|
||||
|
||||
package udplite
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
func NewEndpoint(endpoint string, _ ...any) (p2p.EndPoint, error) {
|
||||
addr, err := netip.ParseAddrPort(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*EndPoint)(net.UDPAddrFromAddrPort(addr)), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
name := file.FolderName()
|
||||
_, hasexist := p2p.Register(name, NewEndpoint)
|
||||
if hasexist {
|
||||
panic("network " + name + " has been registered")
|
||||
}
|
||||
}
|
||||
93
gold/p2p/udplite/lite.go
Normal file
93
gold/p2p/udplite/lite.go
Normal file
@@ -0,0 +1,93 @@
|
||||
//go:build !darwin && !windows
|
||||
|
||||
package udplite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
)
|
||||
|
||||
// https://www.kernel.org/doc/Documentation/networking/udplite.txt
|
||||
const (
|
||||
IPPROTO_UDPLITE = 136
|
||||
SOL_UDPLITE = 136
|
||||
UDPLITE_SEND_CSCOV = 10
|
||||
UDPLITE_RECV_CSCOV = 11
|
||||
)
|
||||
|
||||
type sysListener struct {
|
||||
net.ListenConfig
|
||||
network, address string
|
||||
}
|
||||
|
||||
type sockaddr interface {
|
||||
net.Addr
|
||||
}
|
||||
|
||||
//go:linkname toLocal net.(*UDPAddr).toLocal
|
||||
func toLocal(a *net.UDPAddr, net string) sockaddr
|
||||
|
||||
//go:linkname internetSocket net.internetSocket
|
||||
func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd unsafe.Pointer, err error)
|
||||
|
||||
//go:linkname newUDPConn net.newUDPConn
|
||||
func newUDPConn(fd unsafe.Pointer) *net.UDPConn
|
||||
|
||||
var sockaddrinterfaceinstance = toLocal(&net.UDPAddr{}, "")
|
||||
|
||||
func (sl *sysListener) listenUDP(ctx context.Context, laddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
var ctrlCtxFn func(cxt context.Context, network, address string, c syscall.RawConn) error
|
||||
if sl.ListenConfig.Control != nil {
|
||||
ctrlCtxFn = func(cxt context.Context, network, address string, c syscall.RawConn) error {
|
||||
return sl.ListenConfig.Control(network, address, c)
|
||||
}
|
||||
}
|
||||
sockladdr := sockaddrinterfaceinstance
|
||||
*(**net.UDPAddr)(unsafe.Add(unsafe.Pointer(&sockladdr), unsafe.Sizeof(uintptr(0)))) = laddr
|
||||
fd, err := internetSocket(ctx, sl.network, sockladdr, nil, syscall.SOCK_DGRAM, IPPROTO_UDPLITE, "listen", ctrlCtxFn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newUDPConn(fd), nil
|
||||
}
|
||||
|
||||
func listenUDPLite(network string, laddr *net.UDPAddr) (*net.UDPConn, error) {
|
||||
if laddr == nil {
|
||||
laddr = &net.UDPAddr{}
|
||||
}
|
||||
sl := &sysListener{network: network, address: laddr.String()}
|
||||
conn, err := sl.listenUDP(context.Background(), laddr)
|
||||
if err != nil {
|
||||
var laddrgeneral net.Addr
|
||||
if laddr != nil {
|
||||
laddrgeneral = laddr
|
||||
}
|
||||
return nil, &net.OpError{Op: "listen", Net: network, Source: nil, Addr: laddrgeneral, Err: err}
|
||||
}
|
||||
rc, err := conn.SyscallConn()
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
var errsys error
|
||||
err = rc.Control(func(fd uintptr) {
|
||||
errsys = syscall.SetsockoptInt(int(fd), SOL_UDPLITE, UDPLITE_SEND_CSCOV, int(head.PacketHeadLen)) // for xor rand
|
||||
if errsys != nil {
|
||||
return
|
||||
}
|
||||
errsys = syscall.SetsockoptInt(int(fd), SOL_UDPLITE, UDPLITE_RECV_CSCOV, int(head.PacketHeadLen)) // for xor rand
|
||||
})
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if errsys != nil {
|
||||
_ = conn.Close()
|
||||
return nil, errsys
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
1
gold/p2p/udplite/stub.go
Normal file
1
gold/p2p/udplite/stub.go
Normal file
@@ -0,0 +1 @@
|
||||
package udplite
|
||||
67
gold/p2p/udplite/udp.go
Normal file
67
gold/p2p/udplite/udp.go
Normal file
@@ -0,0 +1,67 @@
|
||||
//go:build !darwin && !windows
|
||||
|
||||
package udplite
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
)
|
||||
|
||||
type EndPoint net.UDPAddr
|
||||
|
||||
func (ep *EndPoint) String() string {
|
||||
return (*net.UDPAddr)(ep).String()
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Network() string {
|
||||
return "udplite"
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Equal(ep2 p2p.EndPoint) bool {
|
||||
if ep == nil || ep2 == nil {
|
||||
return ep == nil && ep2 == nil
|
||||
}
|
||||
udpep2, ok := ep2.(*EndPoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
udpep1 := ep
|
||||
return udpep1.IP.Equal(udpep2.IP) && udpep1.Port == udpep2.Port && udpep1.Zone == udpep2.Zone
|
||||
}
|
||||
|
||||
func (ep *EndPoint) Listen() (p2p.Conn, error) {
|
||||
conn, err := listenUDPLite((*net.UDPAddr)(ep).Network(), (*net.UDPAddr)(ep))
|
||||
return (*Conn)(conn), err
|
||||
}
|
||||
|
||||
type Conn net.UDPConn
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
return (*net.UDPConn)(conn).Close()
|
||||
}
|
||||
|
||||
func (conn *Conn) String() string {
|
||||
return (*net.UDPConn)(conn).LocalAddr().String()
|
||||
}
|
||||
|
||||
func (conn *Conn) LocalAddr() p2p.EndPoint {
|
||||
ep, _ := NewEndpoint((*net.UDPConn)(conn).LocalAddr().String())
|
||||
return ep
|
||||
}
|
||||
|
||||
func (conn *Conn) ReadFromPeer(b []byte) (int, p2p.EndPoint, error) {
|
||||
n, addr, err := (*net.UDPConn)(conn).ReadFromUDP(b)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return n, (*EndPoint)(addr), err
|
||||
}
|
||||
|
||||
func (conn *Conn) WriteToPeer(b []byte, ep p2p.EndPoint) (int, error) {
|
||||
udpep, ok := ep.(*EndPoint)
|
||||
if !ok {
|
||||
return 0, p2p.ErrEndpointTypeMistatch
|
||||
}
|
||||
return (*net.UDPConn)(conn).WriteTo(b, (*net.UDPAddr)(udpep))
|
||||
}
|
||||
12
gold/proto/data/data.go
Normal file
12
gold/proto/data/data.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
)
|
||||
|
||||
func init() {
|
||||
link.RegisterDispacher(head.ProtoData, func(header *head.Packet, peer *link.Link, data []byte) {
|
||||
peer.ToLower(header, data)
|
||||
})
|
||||
}
|
||||
23
gold/proto/hello/hello.go
Normal file
23
gold/proto/hello/hello.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package hello
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
func init() {
|
||||
link.RegisterDispacher(head.ProtoHello, func(_ *head.Packet, peer *link.Link, data []byte) {
|
||||
switch {
|
||||
case len(data) == 0:
|
||||
logrus.Warnln(file.Header(), "recv old packet, do nothing")
|
||||
case data[0] == byte(head.HelloPing):
|
||||
go peer.WritePacket(head.ProtoHello, []byte{byte(head.HelloPong)}, peer.Me().TTL())
|
||||
logrus.Infoln(file.Header(), "recv, send ack")
|
||||
default:
|
||||
logrus.Infoln(file.Header(), "recv ack, do nothing")
|
||||
}
|
||||
})
|
||||
}
|
||||
109
gold/proto/nat/nat.go
Normal file
109
gold/proto/nat/nat.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package nat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
"github.com/fumiama/WireGold/gold/p2p"
|
||||
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 收到通告包的处理
|
||||
link.RegisterDispacher(head.ProtoNotify, func(_ *head.Packet, peer *link.Link, data []byte) {
|
||||
// 1. Data 解包
|
||||
// ---- 使用 head.Notify 解释 packet
|
||||
notify := make(head.Notify, 32)
|
||||
err := json.Unmarshal(data, ¬ify)
|
||||
if err != nil {
|
||||
logrus.Errorln(file.Header(), "notify json unmarshal err:", err)
|
||||
return
|
||||
}
|
||||
// 2. endpoint注册
|
||||
// ---- 遍历 Notify,注册对方的 endpoint 到
|
||||
// ---- connections,注意使用读写锁connmapmu
|
||||
for ps, ep := range notify {
|
||||
nw, epstr := ep[0], ep[1]
|
||||
if nw != peer.Me().EndPoint().Network() {
|
||||
logrus.Warnln(file.Header(), "ignore different network notify", nw, "addr", epstr)
|
||||
continue
|
||||
}
|
||||
addr, err := p2p.NewEndPoint(nw, epstr, peer.Me().NetworkConfigs()...)
|
||||
if err == nil {
|
||||
p, ok := peer.Me().IsInPeer(ps)
|
||||
if ok {
|
||||
if bin.IsNilInterface(p.EndPoint()) || !p.EndPoint().Equal(addr) {
|
||||
p.SetEndPoint(addr)
|
||||
logrus.Infoln(file.Header(), "notify set ep of peer", ps, "to", ep)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln(file.Header(), "notify drop invalid peer:", ps, "ep:", ep)
|
||||
}
|
||||
}
|
||||
})
|
||||
// 收到询问包的处理
|
||||
link.RegisterDispacher(head.ProtoQuery, func(_ *head.Packet, peer *link.Link, data []byte) {
|
||||
// 完成data解包与notify分发
|
||||
|
||||
// 1. Data 解包
|
||||
// ---- 使用 head.Query 解释 packet
|
||||
// ---- 根据 Query 确定需要封装的 Notify
|
||||
var peers head.Query
|
||||
err := json.Unmarshal(data, &peers)
|
||||
if err != nil {
|
||||
logrus.Errorln(file.Header(), "query json unmarshal err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if peer == nil || peer.Me() == nil {
|
||||
logrus.Errorln(file.Header(), "nil link/me")
|
||||
return
|
||||
}
|
||||
|
||||
// 2. notify分发
|
||||
// ---- 封装 Notify 到 新的 packet
|
||||
// ---- 发送到对方
|
||||
notify := make(head.Notify, len(peers))
|
||||
for _, p := range peers {
|
||||
lnk, ok := peer.Me().IsInPeer(p)
|
||||
eps := ""
|
||||
if peer.Me().EndPoint().Network() == "udp" { // udp has real p2p
|
||||
if bin.IsNilInterface(lnk.EndPoint()) {
|
||||
continue
|
||||
}
|
||||
eps = lnk.EndPoint().String()
|
||||
}
|
||||
if eps == "" {
|
||||
eps = peer.RawEndPoint() // use registered ep only
|
||||
}
|
||||
if eps == "" {
|
||||
continue
|
||||
}
|
||||
if ok && bin.IsNonNilInterface(lnk.EndPoint()) {
|
||||
notify[p] = [2]string{
|
||||
lnk.EndPoint().Network(),
|
||||
eps,
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(notify) > 0 {
|
||||
logrus.Infoln(file.Header(), "query wrap", len(notify), "notify")
|
||||
w := bin.SelectWriter()
|
||||
_ = json.NewEncoder(w).Encode(¬ify)
|
||||
w.P(func(b *pbuf.Buffer) {
|
||||
peer.WritePacket(head.ProtoNotify, b.Bytes(), peer.Me().TTL())
|
||||
})
|
||||
w.Destroy()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package helper
|
||||
|
||||
import "os"
|
||||
|
||||
// IsExist 文件/路径存在
|
||||
func IsExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
||||
// IsNotExist 文件/路径不存在
|
||||
func IsNotExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err != nil && os.IsNotExist(err)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// https://github.com/Mrs4s/MiraiGo/blob/master/binary/pool.go
|
||||
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(Writer)
|
||||
},
|
||||
}
|
||||
|
||||
// SelectWriter 从池中取出一个 Writer
|
||||
func SelectWriter() *Writer {
|
||||
// 因为 bufferPool 定义有 New 函数
|
||||
// 所以 bufferPool.Get() 永不为 nil
|
||||
// 不用判空
|
||||
return bufferPool.Get().(*Writer)
|
||||
}
|
||||
|
||||
// PutWriter 将 Writer 放回池中
|
||||
func PutWriter(w *Writer) {
|
||||
// See https://golang.org/issue/23199
|
||||
const maxSize = 1 << 16
|
||||
if (*bytes.Buffer)(w).Cap() < maxSize { // 对于大Buffer直接丢弃
|
||||
w.Reset()
|
||||
bufferPool.Put(w)
|
||||
}
|
||||
}
|
||||
177
helper/writer.go
177
helper/writer.go
@@ -1,177 +0,0 @@
|
||||
package helper
|
||||
|
||||
// https://github.com/Mrs4s/MiraiGo/blob/master/binary/writer.go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Writer 写入
|
||||
type Writer bytes.Buffer
|
||||
|
||||
func NewWriterF(f func(writer *Writer)) []byte {
|
||||
w := SelectWriter()
|
||||
f(w)
|
||||
b := append([]byte(nil), w.Bytes()...)
|
||||
w.put()
|
||||
return b
|
||||
}
|
||||
|
||||
// OpenWriterF must call func cl to close
|
||||
func OpenWriterF(f func(*Writer)) (b []byte, cl func()) {
|
||||
w := SelectWriter()
|
||||
f(w)
|
||||
return w.Bytes(), w.put
|
||||
}
|
||||
|
||||
func (w *Writer) FillUInt16() (pos int) {
|
||||
pos = w.Len()
|
||||
(*bytes.Buffer)(w).Write([]byte{0, 0})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt16At(pos int, v uint16) {
|
||||
newdata := (*bytes.Buffer)(w).Bytes()[pos:]
|
||||
binary.LittleEndian.PutUint16(newdata, v)
|
||||
}
|
||||
|
||||
func (w *Writer) FillUInt32() (pos int) {
|
||||
pos = w.Len()
|
||||
(*bytes.Buffer)(w).Write([]byte{0, 0, 0, 0})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt32At(pos int, v uint32) {
|
||||
newdata := (*bytes.Buffer)(w).Bytes()[pos:]
|
||||
binary.LittleEndian.PutUint32(newdata, v)
|
||||
}
|
||||
|
||||
func (w *Writer) Write(b []byte) (n int, err error) {
|
||||
return (*bytes.Buffer)(w).Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteHex(h string) {
|
||||
b, _ := hex.DecodeString(h)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteByte(b byte) error {
|
||||
return (*bytes.Buffer)(w).WriteByte(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt16(v uint16) {
|
||||
b := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt32(v uint32) {
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt64(v uint64) {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteString(v string) {
|
||||
//w.WriteUInt32(uint32(len(v) + 4))
|
||||
(*bytes.Buffer)(w).WriteString(v)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteStringShort(v string) {
|
||||
w.WriteUInt16(uint16(len(v)))
|
||||
(*bytes.Buffer)(w).WriteString(v)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteBool(b bool) {
|
||||
if b {
|
||||
w.WriteByte(0x01)
|
||||
} else {
|
||||
w.WriteByte(0x00)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) WriteBytesShort(data []byte) {
|
||||
w.WriteUInt16(uint16(len(data)))
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func (w *Writer) Len() int {
|
||||
return (*bytes.Buffer)(w).Len()
|
||||
}
|
||||
|
||||
func (w *Writer) Bytes() []byte {
|
||||
return (*bytes.Buffer)(w).Bytes()
|
||||
}
|
||||
|
||||
func (w *Writer) Reset() {
|
||||
(*bytes.Buffer)(w).Reset()
|
||||
}
|
||||
|
||||
func (w *Writer) Grow(n int) {
|
||||
(*bytes.Buffer)(w).Grow(n)
|
||||
}
|
||||
|
||||
func (w *Writer) Skip(n int) (int, error) {
|
||||
b := (*buffer)(unsafe.Pointer(w))
|
||||
b.lastRead = opInvalid
|
||||
if len(b.buf) <= b.off {
|
||||
// Buffer is empty, reset to recover space.
|
||||
w.Reset()
|
||||
if n == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = min(n, len(b.buf[b.off:]))
|
||||
b.off += n
|
||||
if n > 0 {
|
||||
b.lastRead = opRead
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (w *Writer) put() {
|
||||
PutWriter(w)
|
||||
}
|
||||
|
||||
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
|
||||
// The zero value for Buffer is an empty buffer ready to use.
|
||||
type buffer struct {
|
||||
buf []byte // contents are the bytes buf[off : len(buf)]
|
||||
off int // read at &buf[off], write at &buf[len(buf)]
|
||||
lastRead readOp // last read operation, so that Unread* can work correctly.
|
||||
}
|
||||
|
||||
// The readOp constants describe the last action performed on
|
||||
// the buffer, so that UnreadRune and UnreadByte can check for
|
||||
// invalid usage. opReadRuneX constants are chosen such that
|
||||
// converted to int they correspond to the rune size that was read.
|
||||
type readOp int8
|
||||
|
||||
// Don't use iota for these, as the values need to correspond with the
|
||||
// names and comments, which is easier to see when being explicit.
|
||||
const (
|
||||
opRead readOp = -1 // Any other read operation.
|
||||
opInvalid readOp = 0 // Non-read operation.
|
||||
opReadRune1 readOp = 1 // Read rune of size 1.
|
||||
opReadRune2 readOp = 2 // Read rune of size 2.
|
||||
opReadRune3 readOp = 3 // Read rune of size 3.
|
||||
opReadRune4 readOp = 4 // Read rune of size 4.
|
||||
)
|
||||
|
||||
// min 返回两数最小值,该函数将被内联
|
||||
func min[T int | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64](a, b T) T {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
106
internal/algo/crypto.go
Normal file
106
internal/algo/crypto.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package algo
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCipherTextTooShort = errors.New("ciphertext too short")
|
||||
)
|
||||
|
||||
func EncodeAEAD(aead cipher.AEAD, additional uint16, b []byte) []byte {
|
||||
nsz := aead.NonceSize()
|
||||
// Accocate capacity for all the stuffs.
|
||||
buf := make([]byte, 2+nsz+len(b)+aead.Overhead())
|
||||
n := 0
|
||||
binary.LittleEndian.PutUint16(buf[:2], additional)
|
||||
nonce := buf[2 : 2+nsz]
|
||||
// Select a random nonce
|
||||
_, err := rand.Read(nonce)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Encrypt the message and append the ciphertext to the nonce.
|
||||
eb := aead.Seal(nonce[nsz:nsz], nonce, b, buf[:2])
|
||||
n = len(eb)
|
||||
return buf[2 : 2+nsz+n]
|
||||
}
|
||||
|
||||
func DecodeAEAD(aead cipher.AEAD, additional uint16, b []byte) (data []byte, err error) {
|
||||
nsz := aead.NonceSize()
|
||||
if len(b) < nsz {
|
||||
return nil, ErrCipherTextTooShort
|
||||
}
|
||||
// Split nonce and ciphertext.
|
||||
nonce, ciphertext := b[:nsz], b[nsz:]
|
||||
if len(ciphertext) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
// Decrypt the message and check it wasn't tampered with.
|
||||
var buf [2]byte
|
||||
binary.LittleEndian.PutUint16(buf[:], additional)
|
||||
data = make([]byte, len(ciphertext))
|
||||
n := 0
|
||||
d, err := aead.Open(data[:0], nonce, ciphertext, buf[:])
|
||||
n = len(d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return data[:n], nil
|
||||
}
|
||||
|
||||
func EncodeXORLen(datalen int) int {
|
||||
batchsz := datalen / 8
|
||||
return 8 + batchsz*8 + 8 // seqrand dat tail
|
||||
}
|
||||
|
||||
// EncodeXOR 按 8 字节, 以初始 mask 循环异或编码 data
|
||||
func EncodeXOR(data []byte, mask uint64, seq uint32) []byte {
|
||||
batchsz := len(data) / 8
|
||||
remain := len(data) % 8
|
||||
sum := mask
|
||||
buf := make([]byte, EncodeXORLen(len(data)))
|
||||
binary.LittleEndian.PutUint32(buf[:4], seq)
|
||||
_, _ = rand.Read(buf[4:8]) // seqrand
|
||||
sum ^= binary.LittleEndian.Uint64(buf[:8]) // init from seqrand
|
||||
binary.LittleEndian.PutUint64(buf[:8], sum)
|
||||
for i := 0; i < batchsz; i++ { // range on batch data
|
||||
a := i * 8
|
||||
b := (i + 1) * 8
|
||||
sum ^= binary.LittleEndian.Uint64(data[a:b])
|
||||
binary.LittleEndian.PutUint64(buf[a+8:b+8], sum)
|
||||
}
|
||||
p := batchsz * 8
|
||||
copy(buf[8+p:], data[p:])
|
||||
buf[len(buf)-1] = byte(remain)
|
||||
sum ^= binary.LittleEndian.Uint64(buf[8+p:])
|
||||
binary.LittleEndian.PutUint64(buf[8+p:], sum)
|
||||
return buf
|
||||
}
|
||||
|
||||
// DecodeXOR 按 8 字节, 以初始 mask 循环异或解码 data,
|
||||
// 解码结果完全覆盖 data.
|
||||
func DecodeXOR(data []byte, mask uint64) (uint32, []byte) {
|
||||
if len(data) < 16 || len(data)%8 != 0 {
|
||||
return 0, nil
|
||||
}
|
||||
batchsz := len(data) / 8
|
||||
sum := mask
|
||||
for i := 0; i < batchsz; i++ {
|
||||
a := i * 8
|
||||
b := (i + 1) * 8
|
||||
x := binary.LittleEndian.Uint64(data[a:b])
|
||||
sum ^= x
|
||||
binary.LittleEndian.PutUint64(data[a:b], sum)
|
||||
sum = x
|
||||
}
|
||||
remain := data[len(data)-1]
|
||||
if remain >= 8 {
|
||||
return 0, nil
|
||||
}
|
||||
return binary.LittleEndian.Uint32(data[:4]),
|
||||
data[8 : len(data)-8+int(remain)]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package link
|
||||
package algo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -12,12 +12,10 @@ import (
|
||||
)
|
||||
|
||||
func TestXOR(t *testing.T) {
|
||||
m := Me{
|
||||
mask: 0x12345678_90abcdef,
|
||||
}
|
||||
mask := uint64(0x12345678_90abcdef)
|
||||
buf := make([]byte, 4096)
|
||||
buf2 := make([]byte, 4096)
|
||||
for i := 1; i < 4096; i++ {
|
||||
for i := 0; i < 4096; i++ {
|
||||
data := buf[:i]
|
||||
orgdata := buf2[:i]
|
||||
r1 := bytes.NewBuffer(data[:0])
|
||||
@@ -27,8 +25,12 @@ func TestXOR(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(m.xordec(m.xorenc(r1.Bytes())), r2.Bytes()) {
|
||||
t.Fatal("unexpected xor at", i)
|
||||
seq, dec := DecodeXOR(EncodeXOR(r1.Bytes(), mask, uint32(i)), mask)
|
||||
if !bytes.Equal(dec, r2.Bytes()) {
|
||||
t.Fatal("unexpected xor at", i, "except", hex.EncodeToString(r2.Bytes()), "got", hex.EncodeToString(dec))
|
||||
}
|
||||
if seq != uint32(i) {
|
||||
t.Fatal("unexpected xor at", i, "seq", seq)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +51,7 @@ func TestXChacha20(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 4096; i++ {
|
||||
db, err := decode(aead, uint16(i), encode(aead, uint16(i), data[:i]))
|
||||
db, err := DecodeAEAD(aead, uint16(i), EncodeAEAD(aead, uint16(i), data[:i]))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -73,14 +75,14 @@ func TestExpandKeyUnit(t *testing.T) {
|
||||
func TestMixKeys(t *testing.T) {
|
||||
k1, _ := hex.DecodeString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
||||
k2, _ := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
k := mixkeys(k1, k2)
|
||||
k := MixKeys(k1, k2)
|
||||
kexp, _ := hex.DecodeString("55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555")
|
||||
if !bytes.Equal(k, kexp) {
|
||||
t.Fatal(hex.EncodeToString(k))
|
||||
}
|
||||
k1, _ = hex.DecodeString("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
|
||||
k2, _ = hex.DecodeString("deadbeef1239876540deadbeef1239876540deadbeef1239876540abcdef4567")
|
||||
k = mixkeys(k1, k2)
|
||||
k = MixKeys(k1, k2)
|
||||
kexp, _ = hex.DecodeString("2ca9188d3ebb4a9f22e34d4479d857fca48390253ebbe23f22cbcf6e59507ddc06a9b08794316abfa26b67cedb7a5d542c8912adb493c0352aebe76e73dadf7e")
|
||||
if !bytes.Equal(k, kexp) {
|
||||
t.Fatal(hex.EncodeToString(k))
|
||||
48
internal/algo/hash.go
Normal file
48
internal/algo/hash.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package algo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/blake2b-simd"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Blake2bHash8 生成 data 的 blake2b hash, 返回前八位
|
||||
func Blake2bHash8(precrc64 uint64, data []byte) uint64 {
|
||||
var tgt [32]byte
|
||||
h := blake2b.New256()
|
||||
binary.LittleEndian.PutUint64(tgt[:8], precrc64)
|
||||
_, _ = h.Write(tgt[:8])
|
||||
_, _ = h.Write(data)
|
||||
b := h.Sum(tgt[:0])[:8]
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[algo] precrc64:", fmt.Sprintf("%04x", precrc64), "blk2b hash:", hex.EncodeToString(b))
|
||||
}
|
||||
return binary.LittleEndian.Uint64(b)
|
||||
}
|
||||
|
||||
// IsVaildBlake2bHash8 在收齐全部分片并解密后验证 packet 合法性
|
||||
func IsVaildBlake2bHash8(precrc64 uint64, hash8data []byte) bool {
|
||||
var tgt [32]byte
|
||||
h := blake2b.New256()
|
||||
binary.LittleEndian.PutUint64(tgt[:8], precrc64)
|
||||
_, _ = h.Write(tgt[:8])
|
||||
_, _ = h.Write(hash8data[8:])
|
||||
b := h.Sum(tgt[:0])[:8]
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[algo] blk2b sum calulated:", hex.EncodeToString(b))
|
||||
logrus.Debugln("[algo] blk2b sum in packet:", hex.EncodeToString(hash8data[:8]))
|
||||
}
|
||||
return bytes.Equal(b, hash8data[:8])
|
||||
}
|
||||
|
||||
// MD5Hash8 calculate packet header checksum
|
||||
func MD5Hash8(data []byte) uint64 {
|
||||
m := md5.Sum(data)
|
||||
return binary.LittleEndian.Uint64(m[:8])
|
||||
}
|
||||
40
internal/algo/key.go
Normal file
40
internal/algo/key.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package algo
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func RandKeyIndex() uint8 {
|
||||
return uint8(rand.Intn(32))
|
||||
}
|
||||
|
||||
func MixKeys(k1, k2 []byte) []byte {
|
||||
if len(k1) != 32 || len(k2) != 32 {
|
||||
panic("unexpected key len")
|
||||
}
|
||||
k := make([]byte, 64)
|
||||
for i := range k1 {
|
||||
k1i, k2i := i, 31-i
|
||||
k1v, k2v := k1[k1i], k2[k2i]
|
||||
binary.LittleEndian.PutUint16(
|
||||
k[i*2:(i+1)*2],
|
||||
expandkeyunit(k1v, k2v),
|
||||
)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func expandkeyunit(v1, v2 byte) (v uint16) {
|
||||
v1s, v2s := uint16(v1), uint16(bits.Reverse8(v2))
|
||||
for i := 0; i < 8; i++ {
|
||||
v |= v1s & (1 << (i * 2))
|
||||
v1s <<= 1
|
||||
}
|
||||
for i := 0; i < 8; i++ {
|
||||
v2s <<= 1
|
||||
v |= v2s & (2 << (i * 2))
|
||||
}
|
||||
return
|
||||
}
|
||||
41
internal/algo/zstd.go
Normal file
41
internal/algo/zstd.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package algo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
)
|
||||
|
||||
func EncodeZstd(data []byte) []byte {
|
||||
return bin.SelectWriter().P(func(w *pbuf.Buffer) {
|
||||
enc, err := zstd.NewWriter(w, zstd.WithEncoderLevel(zstd.SpeedFastest))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(enc, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = enc.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}).ToBytes().Copy().Ignore().Trans()
|
||||
}
|
||||
|
||||
func DecodeZstd(data []byte) (b []byte, err error) {
|
||||
dec, err := zstd.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b = bin.SelectWriter().P(func(w *pbuf.Buffer) {
|
||||
_, err = io.Copy(w, dec)
|
||||
dec.Close()
|
||||
}).ToBytes().Copy().Ignore().Trans()
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package helper
|
||||
package bin
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsLittleEndian judge by binary packet.
|
||||
var IsLittleEndian = reflect.ValueOf(&binary.NativeEndian).Elem().Field(0).Type().String() == "binary.littleEndian"
|
||||
|
||||
// slice is the runtime representation of a slice.
|
||||
// It cannot be used safely or portably and its representation may
|
||||
// change in a later release.
|
||||
@@ -30,3 +35,11 @@ func StringToBytes(s string) (b []byte) {
|
||||
bh.cap = sh.len
|
||||
return b
|
||||
}
|
||||
|
||||
func IsNilInterface(x any) bool {
|
||||
return x == nil || reflect.ValueOf(x).IsZero()
|
||||
}
|
||||
|
||||
func IsNonNilInterface(x any) bool {
|
||||
return !IsNilInterface(x)
|
||||
}
|
||||
12
internal/bin/pool.go
Normal file
12
internal/bin/pool.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package bin
|
||||
|
||||
import (
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
)
|
||||
|
||||
// SelectWriter 从池中取出一个 Writer
|
||||
//
|
||||
// 不要忘记调用 Destroy 以快速回收资源
|
||||
func SelectWriter() *Writer {
|
||||
return (*Writer)(pbuf.NewBuffer(nil))
|
||||
}
|
||||
72
internal/bin/writer.go
Normal file
72
internal/bin/writer.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package bin
|
||||
|
||||
// https://github.com/Mrs4s/MiraiGo/blob/master/binary/writer.go
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/fumiama/orbyte/pbuf"
|
||||
)
|
||||
|
||||
// Writer 写入
|
||||
type Writer pbuf.OBuffer
|
||||
|
||||
func NewWriterF(f func(writer *Writer)) []byte {
|
||||
w := SelectWriter()
|
||||
f(w)
|
||||
b := w.ToBytes().Copy()
|
||||
w.Destroy()
|
||||
return b.Trans()
|
||||
}
|
||||
|
||||
func (w *Writer) P(f func(*pbuf.Buffer)) *Writer {
|
||||
(*pbuf.OBuffer)(w).P(f)
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *Writer) Write(b []byte) (n int, err error) {
|
||||
w.P(func(buf *pbuf.Buffer) {
|
||||
n, err = buf.Write(b)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) WriteByte(b byte) (err error) {
|
||||
w.P(func(buf *pbuf.Buffer) {
|
||||
err = buf.WriteByte(b)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) WriteString(s string) (n int, err error) {
|
||||
w.P(func(buf *pbuf.Buffer) {
|
||||
n, err = buf.WriteString(s)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt16(v uint16) {
|
||||
b := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt32(v uint32) {
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) WriteUInt64(v uint64) {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, v)
|
||||
w.Write(b)
|
||||
}
|
||||
|
||||
func (w *Writer) ToBytes() pbuf.Bytes {
|
||||
return pbuf.BufferItemToBytes((*pbuf.OBuffer)(w))
|
||||
}
|
||||
|
||||
func (w *Writer) Destroy() {
|
||||
(*pbuf.OBuffer)(w).ManualDestroy()
|
||||
}
|
||||
37
internal/file/file.go
Normal file
37
internal/file/file.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsExist 文件/路径存在
|
||||
func IsExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
||||
// IsNotExist 文件/路径不存在
|
||||
func IsNotExist(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err != nil && os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// FolderName 本文件所在最下级文件夹名
|
||||
func FolderName() string {
|
||||
_, file, _, ok := runtime.Caller(1)
|
||||
if !ok {
|
||||
return "<unk>"
|
||||
}
|
||||
i := strings.LastIndex(file, "/")
|
||||
if i <= 0 {
|
||||
return file
|
||||
}
|
||||
file = file[:i]
|
||||
i = strings.LastIndex(file, "/")
|
||||
if i <= 0 || i+1 >= len(file) {
|
||||
return file
|
||||
}
|
||||
return file[i+1:]
|
||||
}
|
||||
52
internal/file/log.go
Normal file
52
internal/file/log.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Header() string {
|
||||
file, fn := fileFuncName(2)
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("[")
|
||||
sb.WriteString(file)
|
||||
sb.WriteString("] ")
|
||||
sb.WriteString(fn)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func fileFuncName(skip int) (string, string) {
|
||||
pc, file, _, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
return "unknown", "unknown"
|
||||
}
|
||||
fn := runtime.FuncForPC(pc).Name()
|
||||
i := strings.LastIndex(fn, "/")
|
||||
fn = fn[i+1:]
|
||||
i = strings.LastIndex(file, "/")
|
||||
if i < 0 {
|
||||
i = strings.LastIndex(file, "\\")
|
||||
if i < 0 {
|
||||
return file, fn
|
||||
}
|
||||
}
|
||||
nm := file[i+1:]
|
||||
if len(nm) == 0 {
|
||||
return file, fn
|
||||
}
|
||||
i = strings.LastIndex(nm, ".")
|
||||
if i <= 0 {
|
||||
return nm, fn
|
||||
}
|
||||
return nm[:i], fn
|
||||
}
|
||||
|
||||
func ToLimitHexString(data []byte, bound int) string {
|
||||
endl := "..."
|
||||
if len(data) < bound {
|
||||
bound = len(data)
|
||||
endl = "."
|
||||
}
|
||||
return hex.EncodeToString(data[:bound]) + endl
|
||||
}
|
||||
51
lower/nic.go
51
lower/nic.go
@@ -1,60 +1,61 @@
|
||||
package lower
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"github.com/fumiama/water"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type NICIO interface {
|
||||
io.ReadWriteCloser
|
||||
Up()
|
||||
Down()
|
||||
}
|
||||
|
||||
// NIC 虚拟网卡
|
||||
type NIC struct {
|
||||
ifce *water.Interface
|
||||
ip string
|
||||
subnet string
|
||||
mtu string
|
||||
cidrs []string
|
||||
// NICIO 虚拟网卡
|
||||
type NICIO struct {
|
||||
ifce *water.Interface
|
||||
ip net.IP
|
||||
subnet *net.IPNet
|
||||
rawipnet string
|
||||
mtu string
|
||||
cidrs []string
|
||||
}
|
||||
|
||||
// NewNIC 新建 TUN 网络接口卡
|
||||
// 网卡地址为 ip, 所属子网为 subnet
|
||||
// 以本网卡为下一跳的所有子网为 cidrs
|
||||
// cidrs 不包括本网卡 subnet
|
||||
func NewNIC(ip, subnet, mtu string, cidrs ...string) NICIO {
|
||||
func NewNIC(ip net.IP, subnet *net.IPNet, mtu string, cidrs ...string) *NICIO {
|
||||
ifce, err := water.New(water.Config{DeviceType: water.TUN})
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
logrus.Errorln(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
n := &NIC{
|
||||
ifce: ifce,
|
||||
ip: ip,
|
||||
subnet: subnet,
|
||||
mtu: mtu,
|
||||
cidrs: cidrs,
|
||||
subn, bitsn := subnet.Mask.Size()
|
||||
if bitsn != 32 {
|
||||
panic("mask len " + strconv.Itoa(bitsn) + " is not supported")
|
||||
}
|
||||
n := &NICIO{
|
||||
ifce: ifce,
|
||||
ip: ip,
|
||||
subnet: subnet,
|
||||
rawipnet: ip.String() + "/" + strconv.Itoa(subn),
|
||||
mtu: mtu,
|
||||
cidrs: cidrs,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Read 匹配 PacketsIO Interface
|
||||
func (nc *NIC) Read(buf []byte) (int, error) {
|
||||
func (nc *NICIO) Read(buf []byte) (int, error) {
|
||||
return nc.ifce.Read(buf)
|
||||
}
|
||||
|
||||
func (nc *NIC) Write(packet []byte) (int, error) {
|
||||
func (nc *NICIO) Write(packet []byte) (int, error) {
|
||||
return nc.ifce.Write(packet)
|
||||
}
|
||||
|
||||
// Close 关闭网卡
|
||||
func (n *NIC) Close() error {
|
||||
func (n *NICIO) Close() error {
|
||||
return n.ifce.Close()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,17 +3,24 @@
|
||||
|
||||
package lower
|
||||
|
||||
func (n *NIC) Up() {
|
||||
import "net"
|
||||
|
||||
func (n *NICIO) Up() {
|
||||
execute("ifconfig", n.ifce.Name(), "mtu", n.mtu) // max: 9159
|
||||
execute("ifconfig", n.ifce.Name(), "inet", n.ip, n.ip, "up")
|
||||
execute("route", "add", n.subnet, "-interface", n.ifce.Name())
|
||||
execute(
|
||||
"ifconfig", n.ifce.Name(),
|
||||
"inet", n.ip.String(), n.ip.String(),
|
||||
"netmask", (net.IP)(n.subnet.Mask).String(),
|
||||
"up",
|
||||
)
|
||||
execute("route", "add", n.subnet.String(), "-interface", n.ifce.Name())
|
||||
for _, c := range n.cidrs {
|
||||
execute("route", "add", c, "-interface", n.ifce.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NIC) Down() {
|
||||
execute("route", "delete", n.subnet, "-interface", n.ifce.Name())
|
||||
func (n *NICIO) Down() {
|
||||
execute("route", "delete", n.subnet.String(), "-interface", n.ifce.Name())
|
||||
for _, c := range n.cidrs {
|
||||
execute("route", "delete", c, "-interface", n.ifce.Name())
|
||||
}
|
||||
|
||||
@@ -3,18 +3,16 @@
|
||||
|
||||
package lower
|
||||
|
||||
func (n *NIC) Up() {
|
||||
func (n *NICIO) Up() {
|
||||
execute("/sbin/ip", "link", "set", "dev", n.ifce.Name(), "mtu", n.mtu)
|
||||
execute("/sbin/ip", "addr", "add", n.ip, "dev", n.ifce.Name())
|
||||
execute("/sbin/ip", "addr", "add", n.rawipnet, "dev", n.ifce.Name())
|
||||
execute("/sbin/ip", "link", "set", "dev", n.ifce.Name(), "up")
|
||||
execute("/sbin/ip", "route", "add", n.subnet, "dev", n.ifce.Name())
|
||||
for _, c := range n.cidrs {
|
||||
execute("/sbin/ip", "route", "add", c, "dev", n.ifce.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NIC) Down() {
|
||||
execute("/sbin/ip", "route", "del", n.subnet, "dev", n.ifce.Name())
|
||||
func (n *NICIO) Down() {
|
||||
for _, c := range n.cidrs {
|
||||
execute("/sbin/ip", "route", "del", c, "dev", n.ifce.Name())
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
package lower
|
||||
|
||||
func (n *NIC) Up() {
|
||||
func (n *NICIO) Up() {
|
||||
panic("not support lower on this os now")
|
||||
}
|
||||
|
||||
func (n *NIC) Down() {
|
||||
func (n *NICIO) Down() {
|
||||
panic("not support lower on this os now")
|
||||
}
|
||||
|
||||
@@ -3,26 +3,28 @@
|
||||
|
||||
package lower
|
||||
|
||||
import "net"
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func (n *NIC) Up() {
|
||||
// execute("netsh", "interface", "set", "interface", n.ifce.Name(), "enabled")
|
||||
_, ipn, err := net.ParseCIDR(n.subnet)
|
||||
func (n *NICIO) Up() {
|
||||
execute("cmd", "/c", "netsh interface ip set address name=\""+n.ifce.Name()+"\" source=static addr=\""+n.ip.String()+"\" mask=\""+(net.IP)(n.subnet.Mask).String()+"\" gateway=none")
|
||||
execute("cmd", "/c", "netsh interface ipv4 set subinterface \""+n.ifce.Name()+"\" mtu="+n.mtu)
|
||||
iface, err := net.InterfaceByName(n.ifce.Name())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
execute("cmd", "/c", "netsh interface ip set address name=\""+n.ifce.Name()+"\" source=static addr=\""+n.ip+"\" mask=\""+(net.IP)(ipn.Mask).String()+"\" gateway=none")
|
||||
execute("cmd", "/c", "netsh interface ipv4 set subinterface \""+n.ifce.Name()+"\" mtu="+n.mtu)
|
||||
for _, c := range n.cidrs {
|
||||
ip, cidr, err := net.ParseCIDR(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
execute("cmd", "/c", "route ADD "+ip.String()+" MASK "+(net.IP)(cidr.Mask).String()+" "+n.ip)
|
||||
execute("cmd", "/c", "route ADD "+ip.String()+" MASK "+(net.IP)(cidr.Mask).String()+" "+n.ip.String()+" IF "+strconv.Itoa(iface.Index))
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NIC) Down() {
|
||||
func (n *NICIO) Down() {
|
||||
// execute("netsh", "interface", "set", "interface", n.ifce.Name(), "disabled")
|
||||
for _, c := range n.cidrs {
|
||||
ip, _, err := net.ParseCIDR(c)
|
||||
|
||||
30
main.go
30
main.go
@@ -15,7 +15,8 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
"github.com/fumiama/WireGold/internal/file"
|
||||
"github.com/fumiama/WireGold/upper"
|
||||
"github.com/fumiama/WireGold/upper/services/wg"
|
||||
)
|
||||
@@ -25,7 +26,7 @@ func main() {
|
||||
gen := flag.Bool("g", false, "generate key pair")
|
||||
pshgen := flag.Bool("pg", false, "generate preshared key")
|
||||
showp := flag.Bool("p", false, "show my publickey")
|
||||
file := flag.String("c", "config.yaml", "specify conf file")
|
||||
cfile := flag.String("c", "config.yaml", "specify conf file")
|
||||
debug := flag.Bool("d", false, "print debug logs")
|
||||
warn := flag.Bool("w", false, "only show logs above warn level")
|
||||
logfile := flag.String("l", "-", "write log to file")
|
||||
@@ -51,8 +52,8 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("PublicKey:", helper.BytesToString(pubk[:57]))
|
||||
fmt.Println("PrivateKey:", helper.BytesToString(prvk[:57]))
|
||||
fmt.Println("PublicKey:", bin.BytesToString(pubk[:57]))
|
||||
fmt.Println("PrivateKey:", bin.BytesToString(prvk[:57]))
|
||||
os.Exit(0)
|
||||
}
|
||||
if *pshgen {
|
||||
@@ -65,7 +66,7 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("PresharedKey:", helper.BytesToString(pshk[:57]))
|
||||
fmt.Println("PresharedKey:", bin.BytesToString(pshk[:57]))
|
||||
os.Exit(0)
|
||||
}
|
||||
if *logfile != "-" {
|
||||
@@ -76,7 +77,7 @@ func main() {
|
||||
defer f.Close()
|
||||
logrus.SetOutput(f)
|
||||
}
|
||||
if helper.IsNotExist(*file) {
|
||||
if file.IsNotExist(*cfile) {
|
||||
f := new(bytes.Buffer)
|
||||
var r string
|
||||
fmt.Print("IP: ")
|
||||
@@ -124,14 +125,14 @@ func main() {
|
||||
f.WriteString("MTU: " + strings.TrimSpace(r) + "\n")
|
||||
r = ""
|
||||
|
||||
cfgf, err := os.Create(*file)
|
||||
cfgf, err := os.Create(*cfile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cfgf.Write(f.Bytes())
|
||||
cfgf.Close()
|
||||
}
|
||||
c := config.Parse(*file)
|
||||
c := config.Parse(*cfile)
|
||||
if c.IP == "" {
|
||||
displayHelp("nil ip")
|
||||
}
|
||||
@@ -144,8 +145,8 @@ func main() {
|
||||
if c.EndPoint == "" {
|
||||
displayHelp("nil endpoint")
|
||||
}
|
||||
if c.MTU == 0 {
|
||||
displayHelp("nil mtu")
|
||||
if c.MTU < 128 {
|
||||
displayHelp("invalid mtu")
|
||||
}
|
||||
w, err := wg.NewWireGold(&c)
|
||||
if err != nil {
|
||||
@@ -167,7 +168,14 @@ func main() {
|
||||
}
|
||||
|
||||
func displayHelp(hint string) {
|
||||
fmt.Println(hint)
|
||||
if hint != "" {
|
||||
fmt.Println(hint)
|
||||
fmt.Println("")
|
||||
}
|
||||
fmt.Println("WireGold Version:", config.Version)
|
||||
fmt.Println("Author: Fumiama Minamoto")
|
||||
fmt.Println("Released with GPL-3.0 license")
|
||||
fmt.Println("")
|
||||
flag.Usage()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,16 @@ import (
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/icmp" // support icmp connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/udplite" // support udplite connection
|
||||
_ "github.com/fumiama/WireGold/gold/proto/data" // support data proto
|
||||
_ "github.com/fumiama/WireGold/gold/proto/hello" // support hello proto
|
||||
_ "github.com/fumiama/WireGold/gold/proto/nat" // support nat proto
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/head"
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
)
|
||||
@@ -15,7 +25,7 @@ import (
|
||||
type Tunnel struct {
|
||||
l *link.Link
|
||||
in chan []byte
|
||||
out chan *head.Packet
|
||||
out chan link.LinkData
|
||||
outcache []byte
|
||||
peerip net.IP
|
||||
src uint16
|
||||
@@ -27,7 +37,7 @@ func Create(me *link.Me, peer string) (s Tunnel, err error) {
|
||||
s.l, err = me.Connect(peer)
|
||||
if err == nil {
|
||||
s.in = make(chan []byte, 4)
|
||||
s.out = make(chan *head.Packet, 4)
|
||||
s.out = make(chan link.LinkData, 4)
|
||||
s.peerip = net.ParseIP(peer)
|
||||
} else {
|
||||
logrus.Errorln("[tunnel] create err:", err)
|
||||
@@ -36,7 +46,7 @@ func Create(me *link.Me, peer string) (s Tunnel, err error) {
|
||||
}
|
||||
|
||||
func (s *Tunnel) Start(srcport, destport, mtu uint16) {
|
||||
logrus.Infoln("[tunnel] start from", srcport, "to", destport)
|
||||
logrus.Infoln("[tunnel] start port through", srcport, "->", destport, "mtu", mtu)
|
||||
s.src = srcport
|
||||
s.dest = destport
|
||||
s.mtu = mtu
|
||||
@@ -64,15 +74,14 @@ func (s *Tunnel) Read(p []byte) (int, error) {
|
||||
d = s.outcache
|
||||
} else {
|
||||
pkt := <-s.out
|
||||
if pkt == nil {
|
||||
if len(pkt.D) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
defer pkt.Put()
|
||||
if len(pkt.Data) < 4 {
|
||||
logrus.Warnln("[tunnel] unexpected packet data len", len(pkt.Data), "content", pkt.Data)
|
||||
if pkt.H.Size() < 4 {
|
||||
logrus.Warnln("[tunnel] unexpected packet data len", pkt.H.Size(), "content", hex.EncodeToString(pkt.D))
|
||||
return 0, io.EOF
|
||||
}
|
||||
d = pkt.Data[4:]
|
||||
d = pkt.D[4:]
|
||||
}
|
||||
if d != nil {
|
||||
if len(p) >= len(d) {
|
||||
@@ -102,72 +111,79 @@ func (s *Tunnel) handleWrite() {
|
||||
end = len(b)
|
||||
endl = "."
|
||||
}
|
||||
logrus.Debugln("[tunnel] write send", hex.EncodeToString(b[:end]), endl)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] write send", hex.EncodeToString(b[:end]), endl)
|
||||
}
|
||||
if b == nil {
|
||||
logrus.Errorln("[tunnel] write recv nil")
|
||||
break
|
||||
}
|
||||
logrus.Debugln("[tunnel] writing", len(b), "bytes...")
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] writing", len(b), "bytes...")
|
||||
}
|
||||
for len(b) > int(s.mtu)-4 {
|
||||
logrus.Infoln("[tunnel] seq", seq, "split buffer")
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] seq", seq, "split buffer")
|
||||
}
|
||||
binary.LittleEndian.PutUint32(buf[:4], seq)
|
||||
seq++
|
||||
copy(buf[4:], b[:s.mtu-4])
|
||||
_, err := s.l.WriteAndPut(
|
||||
head.NewPacket(head.ProtoData, s.src, s.peerip, s.dest, buf), false,
|
||||
)
|
||||
if err != nil {
|
||||
logrus.Errorln("[tunnel] seq", seq-1, "write err:", err)
|
||||
return
|
||||
s.l.WritePacket(head.ProtoData, buf, s.l.Me().TTL())
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] seq", seq-1, "written")
|
||||
}
|
||||
logrus.Debugln("[tunnel] seq", seq-1, "write succeeded")
|
||||
b = b[s.mtu-4:]
|
||||
}
|
||||
binary.LittleEndian.PutUint32(buf[:4], seq)
|
||||
seq++
|
||||
copy(buf[4:], b)
|
||||
_, err := s.l.WriteAndPut(
|
||||
head.NewPacket(head.ProtoData, s.src, s.peerip, s.dest, buf[:len(b)+4]), false,
|
||||
)
|
||||
if err != nil {
|
||||
logrus.Errorln("[tunnel] seq", seq-1, "write err:", err)
|
||||
break
|
||||
s.l.WritePacket(head.ProtoData, buf[:len(b)+4], s.l.Me().TTL())
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] seq", seq-1, "written")
|
||||
}
|
||||
logrus.Debugln("[tunnel] seq", seq-1, "write succeeded")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Tunnel) handleRead() {
|
||||
seq := uint32(0)
|
||||
seqmap := make(map[uint32]*head.Packet)
|
||||
seqmap := make(map[uint32]link.LinkData)
|
||||
for {
|
||||
if p, ok := seqmap[seq]; ok {
|
||||
logrus.Debugln("[tunnel] dispatch cached seq", seq)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] dispatch cached seq", seq)
|
||||
}
|
||||
delete(seqmap, seq)
|
||||
seq++
|
||||
s.out <- p
|
||||
continue
|
||||
}
|
||||
p := s.l.Read()
|
||||
if p == nil {
|
||||
if len(p.D) == 0 {
|
||||
logrus.Errorln("[tunnel] read recv nil")
|
||||
break
|
||||
}
|
||||
end := 64
|
||||
endl := "..."
|
||||
if len(p.Data) < 64 {
|
||||
end = len(p.Data)
|
||||
if len(p.D) < 64 {
|
||||
end = len(p.D)
|
||||
endl = "."
|
||||
}
|
||||
logrus.Debugln("[tunnel] read recv", hex.EncodeToString(p.Data[:end]), endl)
|
||||
recvseq := binary.LittleEndian.Uint32(p.Data[:4])
|
||||
var recvseq uint32
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] read recv", hex.EncodeToString(p.D[:end]), endl)
|
||||
}
|
||||
recvseq = binary.LittleEndian.Uint32(p.D[:4])
|
||||
if recvseq == seq {
|
||||
logrus.Debugln("[tunnel] dispatch seq", seq)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] dispatch seq", seq)
|
||||
}
|
||||
seq++
|
||||
s.out <- p
|
||||
continue
|
||||
}
|
||||
seqmap[recvseq] = p
|
||||
logrus.Debugln("[tunnel] cache seq", recvseq)
|
||||
if config.ShowDebugLog {
|
||||
logrus.Debugln("[tunnel] cache seq", recvseq)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
466
upper/services/tunnel/tunnel_icmp_test.go
Normal file
466
upper/services/tunnel/tunnel_icmp_test.go
Normal file
@@ -0,0 +1,466 @@
|
||||
//go:build linux
|
||||
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
curve "github.com/fumiama/go-x25519"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
)
|
||||
|
||||
const (
|
||||
icmpNS1 = "wgtest_ns1"
|
||||
icmpNS2 = "wgtest_ns2"
|
||||
icmpIP1 = "10.0.0.1"
|
||||
icmpIP2 = "10.0.0.2"
|
||||
icmpVeth1 = "veth1"
|
||||
icmpVeth2 = "veth2"
|
||||
)
|
||||
|
||||
// setupICMPNetns creates two network namespaces connected by a veth pair.
|
||||
// It returns a cleanup function. Requires root.
|
||||
func setupICMPNetns(t *testing.T) func() {
|
||||
t.Helper()
|
||||
|
||||
cmds := [][]string{
|
||||
{"ip", "netns", "add", icmpNS1},
|
||||
{"ip", "netns", "add", icmpNS2},
|
||||
{"ip", "link", "add", icmpVeth1, "type", "veth", "peer", "name", icmpVeth2},
|
||||
{"ip", "link", "set", icmpVeth1, "netns", icmpNS1},
|
||||
{"ip", "link", "set", icmpVeth2, "netns", icmpNS2},
|
||||
{"ip", "netns", "exec", icmpNS1, "ifconfig", icmpVeth1, icmpIP1, "up"},
|
||||
{"ip", "netns", "exec", icmpNS2, "ifconfig", icmpVeth2, icmpIP2, "up"},
|
||||
}
|
||||
|
||||
for _, args := range cmds {
|
||||
if out, err := exec.Command(args[0], args[1:]...).CombinedOutput(); err != nil {
|
||||
// best-effort cleanup
|
||||
exec.Command("ip", "netns", "del", icmpNS1).Run()
|
||||
exec.Command("ip", "netns", "del", icmpNS2).Run()
|
||||
t.Fatalf("setup netns: %v failed: %v\n%s", args, err, out)
|
||||
}
|
||||
}
|
||||
|
||||
return func() {
|
||||
exec.Command("ip", "netns", "del", icmpNS1).Run()
|
||||
exec.Command("ip", "netns", "del", icmpNS2).Run()
|
||||
}
|
||||
}
|
||||
|
||||
// enterNetns pins the current goroutine to its OS thread, switches into
|
||||
// the named network namespace, and returns a function that restores the
|
||||
// original namespace and unlocks the thread.
|
||||
func enterNetns(nsName string) (func(), error) {
|
||||
runtime.LockOSThread()
|
||||
|
||||
origFd, err := unix.Open("/proc/self/ns/net", unix.O_RDONLY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
runtime.UnlockOSThread()
|
||||
return nil, fmt.Errorf("open current netns: %w", err)
|
||||
}
|
||||
|
||||
targetFd, err := unix.Open("/var/run/netns/"+nsName, unix.O_RDONLY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
unix.Close(origFd)
|
||||
runtime.UnlockOSThread()
|
||||
return nil, fmt.Errorf("open target netns %s: %w", nsName, err)
|
||||
}
|
||||
|
||||
if err := unix.Setns(targetFd, unix.CLONE_NEWNET); err != nil {
|
||||
unix.Close(targetFd)
|
||||
unix.Close(origFd)
|
||||
runtime.UnlockOSThread()
|
||||
return nil, fmt.Errorf("setns to %s: %w", nsName, err)
|
||||
}
|
||||
unix.Close(targetFd)
|
||||
|
||||
return func() {
|
||||
unix.Setns(origFd, unix.CLONE_NEWNET)
|
||||
unix.Close(origFd)
|
||||
runtime.UnlockOSThread()
|
||||
}, nil
|
||||
}
|
||||
|
||||
// initMeInNetns initializes a link.Me at dst inside the given network namespace.
|
||||
// The underlying socket fd remains bound to that namespace after return.
|
||||
func initMeInNetns(t testing.TB, nsName string, cfg *link.MyConfig, dst *link.Me) {
|
||||
t.Helper()
|
||||
var merr any
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
merr = r
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
restore, err := enterNetns(nsName)
|
||||
if err != nil {
|
||||
merr = err
|
||||
return
|
||||
}
|
||||
defer restore()
|
||||
*dst = link.NewMe(cfg)
|
||||
}()
|
||||
<-done
|
||||
if merr != nil {
|
||||
t.Fatalf("initMeInNetns(%s): %v", nsName, merr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTunnelICMP(t *testing.T) {
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("skipping ICMP test: requires root")
|
||||
}
|
||||
for i := 1; i <= 4; i++ {
|
||||
sz := 1024 * i
|
||||
if !t.Run(strconv.Itoa(sz), func(t *testing.T) {
|
||||
testTunnelICMP(t, uint16(sz))
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTunnelICMP(t *testing.T, mtu uint16) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
cleanup := setupICMPNetns(t)
|
||||
defer cleanup()
|
||||
|
||||
testICMPTunnel(t, true, false, nil, mtu) // plain text
|
||||
testICMPTunnel(t, false, false, nil, mtu) // normal
|
||||
|
||||
testICMPTunnel(t, true, true, nil, mtu) // plain text + base14
|
||||
testICMPTunnel(t, false, true, nil, mtu) // normal + base14
|
||||
|
||||
var buf [32]byte
|
||||
if _, err := rand.Read(buf[:]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testICMPTunnel(t, false, false, &buf, mtu) // preshared
|
||||
testICMPTunnel(t, false, true, &buf, mtu) // preshared + base14
|
||||
}
|
||||
|
||||
func testICMPTunnel(t *testing.T, isplain, isbase14 bool, pshk *[32]byte, mtu uint16) {
|
||||
nw := "icmp"
|
||||
fmt.Println("start", nw, "testing, mtu", mtu, "plain", isplain, "b14", isbase14, "pshk", pshk != nil)
|
||||
|
||||
selfpk, err := curve.New(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
peerpk, err := curve.New(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("my priv key:", hex.EncodeToString(selfpk.Private()[:]))
|
||||
t.Log("my publ key:", hex.EncodeToString(selfpk.Public()[:]))
|
||||
t.Log("peer priv key:", hex.EncodeToString(peerpk.Private()[:]))
|
||||
t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:]))
|
||||
|
||||
var m link.Me
|
||||
initMeInNetns(t, icmpNS1, &link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.2/32",
|
||||
MyEndpoint: icmpIP1,
|
||||
Network: nw,
|
||||
PrivateKey: selfpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: mtu,
|
||||
Base14: isbase14,
|
||||
}, &m)
|
||||
defer m.Close()
|
||||
|
||||
var p link.Me
|
||||
initMeInNetns(t, icmpNS2, &link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.3/32",
|
||||
MyEndpoint: icmpIP2,
|
||||
Network: nw,
|
||||
PrivateKey: peerpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: mtu,
|
||||
Base14: isbase14,
|
||||
}, &p)
|
||||
defer p.Close()
|
||||
|
||||
ppp := peerpk.Public()
|
||||
spp := selfpk.Public()
|
||||
if isplain {
|
||||
ppp = nil
|
||||
spp = nil
|
||||
}
|
||||
|
||||
m.AddPeer(&link.PeerConfig{
|
||||
PeerIP: "192.168.1.3",
|
||||
EndPoint: icmpIP2,
|
||||
AllowedIPs: []string{"192.168.1.3/32"},
|
||||
PubicKey: ppp,
|
||||
PresharedKey: pshk,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
DoublePacket: true,
|
||||
})
|
||||
p.AddPeer(&link.PeerConfig{
|
||||
PeerIP: "192.168.1.2",
|
||||
EndPoint: icmpIP1,
|
||||
AllowedIPs: []string{"192.168.1.2/32"},
|
||||
PubicKey: spp,
|
||||
PresharedKey: pshk,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
})
|
||||
|
||||
tunnme, err := Create(&m, "192.168.1.3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tunnme.Start(1, 1, 4096)
|
||||
tunnpeer, err := Create(&p, "192.168.1.2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tunnpeer.Start(1, 1, 4096)
|
||||
|
||||
time.Sleep(time.Second) // wait link up
|
||||
|
||||
sendb := ([]byte)("1234")
|
||||
go tunnme.Write(sendb)
|
||||
buf := make([]byte, 4)
|
||||
tunnpeer.Read(buf)
|
||||
if string(sendb) != string(buf) {
|
||||
logrus.Errorln("error: recv", buf, "expect", sendb)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
sendb = make([]byte, mtu+4)
|
||||
for i := 0; i < len(sendb); i++ {
|
||||
sendb[i] = byte(i)
|
||||
}
|
||||
|
||||
for i := 1; i < len(sendb); i++ {
|
||||
rand.Read(sendb[:i])
|
||||
go tunnme.Write(sendb[:i])
|
||||
rbuf := make([]byte, i)
|
||||
_, err = io.ReadFull(&tunnpeer, rbuf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(sendb[:i], rbuf) {
|
||||
t.Fatal("error: recv", i, "bytes data")
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(sendb); i++ {
|
||||
sendb[i] = ^byte(i)
|
||||
}
|
||||
tunnme.Write(sendb)
|
||||
rd := bytes.NewBuffer(nil)
|
||||
|
||||
tm := time.AfterFunc(time.Second*2, func() {
|
||||
tunnme.Stop()
|
||||
tunnpeer.Stop()
|
||||
})
|
||||
defer tm.Stop()
|
||||
|
||||
_, err = io.CopyBuffer(rd, &tunnpeer, make([]byte, 200))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(sendb) != rd.String() {
|
||||
t.Fatal("error: recv fragmented data")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTunnelICMP(b *testing.B) {
|
||||
if os.Getuid() != 0 {
|
||||
b.Skip("skipping ICMP benchmark: requires root")
|
||||
}
|
||||
benchmarkTunnelNetworkICMP(b, 4096)
|
||||
}
|
||||
|
||||
func BenchmarkTunnelICMPSmallMTU(b *testing.B) {
|
||||
if os.Getuid() != 0 {
|
||||
b.Skip("skipping ICMP benchmark: requires root")
|
||||
}
|
||||
benchmarkTunnelNetworkICMP(b, 1024)
|
||||
}
|
||||
|
||||
func benchmarkTunnelNetworkICMP(b *testing.B, mtu uint16) {
|
||||
logrus.SetLevel(logrus.ErrorLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
cleanup := setupICMPBenchNetns(b)
|
||||
defer cleanup()
|
||||
|
||||
for i := 1; i <= 4; i++ {
|
||||
sz := 1024 * i
|
||||
b.Run(fmt.Sprintf("%d-plain-nob14", sz), func(b *testing.B) {
|
||||
benchmarkICMPTunnel(b, sz, true, false, nil, mtu)
|
||||
})
|
||||
b.Run(fmt.Sprintf("%d-normal-nob14", sz), func(b *testing.B) {
|
||||
benchmarkICMPTunnel(b, sz, false, false, nil, mtu)
|
||||
})
|
||||
b.Run(fmt.Sprintf("%d-plain-b14", sz), func(b *testing.B) {
|
||||
benchmarkICMPTunnel(b, sz, true, true, nil, mtu)
|
||||
})
|
||||
b.Run(fmt.Sprintf("%d-normal-b14", sz), func(b *testing.B) {
|
||||
benchmarkICMPTunnel(b, sz, false, true, nil, mtu)
|
||||
})
|
||||
var buf [32]byte
|
||||
if _, err := rand.Read(buf[:]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.Run(fmt.Sprintf("%d-preshared-nob14", sz), func(b *testing.B) {
|
||||
benchmarkICMPTunnel(b, sz, false, false, &buf, mtu)
|
||||
})
|
||||
b.Run(fmt.Sprintf("%d-preshared-b14", sz), func(b *testing.B) {
|
||||
benchmarkICMPTunnel(b, sz, false, true, &buf, mtu)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupICMPBenchNetns(b *testing.B) func() {
|
||||
b.Helper()
|
||||
|
||||
cmds := [][]string{
|
||||
{"ip", "netns", "add", icmpNS1},
|
||||
{"ip", "netns", "add", icmpNS2},
|
||||
{"ip", "link", "add", icmpVeth1, "type", "veth", "peer", "name", icmpVeth2},
|
||||
{"ip", "link", "set", icmpVeth1, "netns", icmpNS1},
|
||||
{"ip", "link", "set", icmpVeth2, "netns", icmpNS2},
|
||||
{"ip", "netns", "exec", icmpNS1, "ifconfig", icmpVeth1, icmpIP1, "up"},
|
||||
{"ip", "netns", "exec", icmpNS2, "ifconfig", icmpVeth2, icmpIP2, "up"},
|
||||
}
|
||||
|
||||
for _, args := range cmds {
|
||||
if out, err := exec.Command(args[0], args[1:]...).CombinedOutput(); err != nil {
|
||||
exec.Command("ip", "netns", "del", icmpNS1).Run()
|
||||
exec.Command("ip", "netns", "del", icmpNS2).Run()
|
||||
b.Fatalf("setup netns: %v failed: %v\n%s", args, err, out)
|
||||
}
|
||||
}
|
||||
|
||||
return func() {
|
||||
exec.Command("ip", "netns", "del", icmpNS1).Run()
|
||||
exec.Command("ip", "netns", "del", icmpNS2).Run()
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkICMPTunnel(b *testing.B, sz int, isplain, isbase14 bool, pshk *[32]byte, mtu uint16) {
|
||||
nw := "icmp"
|
||||
|
||||
selfpk, err := curve.New(nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
peerpk, err := curve.New(nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
var m link.Me
|
||||
initMeInNetns(b, icmpNS1, &link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.2/32",
|
||||
MyEndpoint: icmpIP1,
|
||||
Network: nw,
|
||||
PrivateKey: selfpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: mtu,
|
||||
Base14: isbase14,
|
||||
}, &m)
|
||||
defer m.Close()
|
||||
|
||||
var p link.Me
|
||||
initMeInNetns(b, icmpNS2, &link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.3/32",
|
||||
MyEndpoint: icmpIP2,
|
||||
Network: nw,
|
||||
PrivateKey: peerpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: mtu,
|
||||
Base14: isbase14,
|
||||
}, &p)
|
||||
defer p.Close()
|
||||
|
||||
ppp := peerpk.Public()
|
||||
spp := selfpk.Public()
|
||||
if isplain {
|
||||
ppp = nil
|
||||
spp = nil
|
||||
}
|
||||
|
||||
m.AddPeer(&link.PeerConfig{
|
||||
PeerIP: "192.168.1.3",
|
||||
EndPoint: icmpIP2,
|
||||
AllowedIPs: []string{"192.168.1.3/32"},
|
||||
PubicKey: ppp,
|
||||
PresharedKey: pshk,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
DoublePacket: true,
|
||||
})
|
||||
p.AddPeer(&link.PeerConfig{
|
||||
PeerIP: "192.168.1.2",
|
||||
EndPoint: icmpIP1,
|
||||
AllowedIPs: []string{"192.168.1.2/32"},
|
||||
PubicKey: spp,
|
||||
PresharedKey: pshk,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
})
|
||||
|
||||
tunnme, err := Create(&m, "192.168.1.3")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
tunnme.Start(1, 1, 4096)
|
||||
tunnpeer, err := Create(&p, "192.168.1.2")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
tunnpeer.Start(1, 1, 4096)
|
||||
|
||||
time.Sleep(time.Second) // wait link up
|
||||
|
||||
b.SetBytes(int64(sz))
|
||||
b.ResetTimer()
|
||||
sendb := make([]byte, sz)
|
||||
for i := 0; i < b.N; i++ {
|
||||
rand.Read(sendb)
|
||||
go tunnme.Write(sendb)
|
||||
buf := make([]byte, sz)
|
||||
_, err = io.ReadFull(&tunnpeer, buf)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
|
||||
time.Sleep(time.Second) // wait packets all received
|
||||
|
||||
tunnme.Stop()
|
||||
tunnpeer.Stop()
|
||||
}
|
||||
@@ -4,7 +4,10 @@ import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -13,10 +16,79 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
)
|
||||
|
||||
func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
func testTunnelMTUsNetwork(t *testing.T, nw string) {
|
||||
for i := 1; i <= 4; i++ {
|
||||
sz := 1024 * i
|
||||
if !t.Run(strconv.Itoa(sz), func(t *testing.T) {
|
||||
testTunnelNetwork(t, nw, uint16(sz))
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTunnelUDP(t *testing.T) {
|
||||
testTunnelMTUsNetwork(t, "udp")
|
||||
}
|
||||
|
||||
func TestTunnelUDPLite(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
return
|
||||
}
|
||||
testTunnelMTUsNetwork(t, "udplite")
|
||||
}
|
||||
|
||||
func TestTunnelTCP(t *testing.T) {
|
||||
testTunnelMTUsNetwork(t, "tcp")
|
||||
}
|
||||
|
||||
func TestTunnelIP(t *testing.T) {
|
||||
testTunnelMTUsNetwork(t, "ip")
|
||||
}
|
||||
|
||||
func BenchmarkTunnelUDP(b *testing.B) {
|
||||
benchmarkTunnelNetwork(b, "udp", 4096)
|
||||
}
|
||||
|
||||
func BenchmarkTunnelUDPSmallMTU(b *testing.B) {
|
||||
benchmarkTunnelNetwork(b, "udp", 1024)
|
||||
}
|
||||
|
||||
func BenchmarkTunnelUDPLite(b *testing.B) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
return
|
||||
}
|
||||
benchmarkTunnelNetwork(b, "udplite", 4096)
|
||||
}
|
||||
|
||||
func BenchmarkTunnelUDPLiteSmallMTU(b *testing.B) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
return
|
||||
}
|
||||
benchmarkTunnelNetwork(b, "udplite", 1024)
|
||||
}
|
||||
|
||||
func BenchmarkTunnelTCP(b *testing.B) {
|
||||
benchmarkTunnelNetwork(b, "tcp", 4096)
|
||||
}
|
||||
|
||||
func BenchmarkTunnelTCPSmallMTU(b *testing.B) {
|
||||
benchmarkTunnelNetwork(b, "tcp", 1024)
|
||||
}
|
||||
|
||||
func BenchmarkTunnelIP(b *testing.B) {
|
||||
benchmarkTunnelNetwork(b, "ip", 4096)
|
||||
}
|
||||
|
||||
func BenchmarkTunnelIPSmallMTU(b *testing.B) {
|
||||
benchmarkTunnelNetwork(b, "ip", 1024)
|
||||
}
|
||||
|
||||
func testTunnel(t *testing.T, nw string, isplain, isbase14 bool, pshk *[32]byte, mtu uint16) {
|
||||
fmt.Println("start", nw, "testing, mtu", mtu, "plain", isplain, "b14", isbase14, "pshk", pshk != nil)
|
||||
selfpk, err := curve.New(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -30,23 +102,39 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
t.Log("peer priv key:", hex.EncodeToString(peerpk.Private()[:]))
|
||||
t.Log("peer publ key:", hex.EncodeToString(peerpk.Public()[:]))
|
||||
|
||||
epm := "127.0.0.1"
|
||||
if nw != "ip" && nw != "icmp" {
|
||||
epm += ":0"
|
||||
}
|
||||
// under macos you need to run
|
||||
//
|
||||
// sudo ifconfig lo0 alias 127.0.0.2
|
||||
epp := "127.0.0.2"
|
||||
if nw != "ip" && nw != "icmp" {
|
||||
epp += ":0"
|
||||
}
|
||||
|
||||
m := link.NewMe(&link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.2/32",
|
||||
MyEndpoint: "127.0.0.1:0",
|
||||
MyEndpoint: epm,
|
||||
Network: nw,
|
||||
PrivateKey: selfpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: 4096,
|
||||
MTU: mtu,
|
||||
Base14: isbase14,
|
||||
})
|
||||
defer m.Close()
|
||||
|
||||
p := link.NewMe(&link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.3/32",
|
||||
MyEndpoint: "127.0.0.1:0",
|
||||
MyEndpoint: epp,
|
||||
Network: nw,
|
||||
PrivateKey: peerpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: 4096,
|
||||
MTU: mtu,
|
||||
Base14: isbase14,
|
||||
})
|
||||
defer p.Close()
|
||||
|
||||
@@ -63,9 +151,10 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
AllowedIPs: []string{"192.168.1.3/32"},
|
||||
PubicKey: ppp,
|
||||
PresharedKey: pshk,
|
||||
MTU: 4096,
|
||||
MTURandomRange: 1024,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
DoublePacket: true,
|
||||
})
|
||||
p.AddPeer(&link.PeerConfig{
|
||||
PeerIP: "192.168.1.2",
|
||||
@@ -73,8 +162,8 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
AllowedIPs: []string{"192.168.1.2/32"},
|
||||
PubicKey: spp,
|
||||
PresharedKey: pshk,
|
||||
MTU: 4096,
|
||||
MTURandomRange: 1024,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
})
|
||||
tunnme, err := Create(&m, "192.168.1.3")
|
||||
@@ -91,43 +180,35 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
time.Sleep(time.Second) // wait link up
|
||||
|
||||
sendb := ([]byte)("1234")
|
||||
tunnme.Write(sendb)
|
||||
go tunnme.Write(sendb)
|
||||
buf := make([]byte, 4)
|
||||
tunnpeer.Read(buf)
|
||||
if string(sendb) != string(buf) {
|
||||
t.Log("error: recv", buf)
|
||||
logrus.Errorln("error: recv", buf, "expect", sendb)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
sendb = make([]byte, 4096)
|
||||
rand.Read(sendb)
|
||||
tunnme.Write(sendb)
|
||||
buf = make([]byte, 4096)
|
||||
_, err = io.ReadFull(&tunnpeer, buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(sendb) != string(buf) {
|
||||
t.Fatal("error: recv 4096 bytes data")
|
||||
sendb = make([]byte, mtu+4)
|
||||
for i := 0; i < len(sendb); i++ {
|
||||
sendb[i] = byte(i)
|
||||
}
|
||||
|
||||
sendb = make([]byte, 65535)
|
||||
buf = make([]byte, 65535)
|
||||
for i := 0; i < 32; i++ {
|
||||
rand.Read(sendb)
|
||||
n, _ := tunnme.Write(sendb)
|
||||
t.Log("loop", i, "write", n, "bytes")
|
||||
n, err = io.ReadFull(&tunnpeer, buf)
|
||||
for i := 1; i < len(sendb); i++ {
|
||||
rand.Read(sendb[:i])
|
||||
go tunnme.Write(sendb[:i])
|
||||
buf := make([]byte, i)
|
||||
_, err = io.ReadFull(&tunnpeer, buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("loop", i, "read", n, "bytes")
|
||||
if string(sendb) != string(buf) {
|
||||
t.Fatal("loop", i, "error: recv 65535 bytes data")
|
||||
if !bytes.Equal(sendb[:i], buf) {
|
||||
t.Fatal("error: recv", i, "bytes data")
|
||||
}
|
||||
}
|
||||
|
||||
rand.Read(sendb)
|
||||
for i := 0; i < len(sendb); i++ {
|
||||
sendb[i] = ^byte(i)
|
||||
}
|
||||
tunnme.Write(sendb)
|
||||
rd := bytes.NewBuffer(nil)
|
||||
|
||||
@@ -142,24 +223,171 @@ func testTunnel(t *testing.T, isplain bool, pshk *[32]byte) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(sendb) != rd.String() {
|
||||
t.Fatal("error: recv fragmented 4096 bytes data")
|
||||
t.Fatal("error: recv fragmented data")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTunnel(t *testing.T) {
|
||||
func benchmarkTunnel(b *testing.B, sz int, nw string, isplain, isbase14 bool, pshk *[32]byte, mtu uint16) {
|
||||
selfpk, err := curve.New(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
peerpk, err := curve.New(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
epm := "127.0.0.1"
|
||||
if nw != "ip" && nw != "icmp" {
|
||||
epm += ":0"
|
||||
}
|
||||
// under macos you need to run
|
||||
//
|
||||
// sudo ifconfig lo0 alias 127.0.0.2
|
||||
epp := "127.0.0.2"
|
||||
if nw != "ip" && nw != "icmp" {
|
||||
epp += ":0"
|
||||
}
|
||||
|
||||
m := link.NewMe(&link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.2/32",
|
||||
MyEndpoint: epm,
|
||||
Network: nw,
|
||||
PrivateKey: selfpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: mtu,
|
||||
Base14: isbase14,
|
||||
})
|
||||
defer m.Close()
|
||||
|
||||
p := link.NewMe(&link.MyConfig{
|
||||
MyIPwithMask: "192.168.1.3/32",
|
||||
MyEndpoint: epp,
|
||||
Network: nw,
|
||||
PrivateKey: peerpk.Private(),
|
||||
SrcPort: 1,
|
||||
DstPort: 1,
|
||||
MTU: mtu,
|
||||
Base14: isbase14,
|
||||
})
|
||||
defer p.Close()
|
||||
|
||||
ppp := peerpk.Public()
|
||||
spp := selfpk.Public()
|
||||
if isplain {
|
||||
ppp = nil
|
||||
spp = nil
|
||||
}
|
||||
|
||||
m.AddPeer(&link.PeerConfig{
|
||||
PeerIP: "192.168.1.3",
|
||||
EndPoint: p.EndPoint().String(),
|
||||
AllowedIPs: []string{"192.168.1.3/32"},
|
||||
PubicKey: ppp,
|
||||
PresharedKey: pshk,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
DoublePacket: true,
|
||||
})
|
||||
p.AddPeer(&link.PeerConfig{
|
||||
PeerIP: "192.168.1.2",
|
||||
EndPoint: m.EndPoint().String(),
|
||||
AllowedIPs: []string{"192.168.1.2/32"},
|
||||
PubicKey: spp,
|
||||
PresharedKey: pshk,
|
||||
MTU: mtu,
|
||||
MTURandomRange: mtu / 2,
|
||||
UseZstd: true,
|
||||
})
|
||||
tunnme, err := Create(&m, "192.168.1.3")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
tunnme.Start(1, 1, 4096)
|
||||
tunnpeer, err := Create(&p, "192.168.1.2")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
tunnpeer.Start(1, 1, 4096)
|
||||
|
||||
time.Sleep(time.Second) // wait link up
|
||||
|
||||
b.SetBytes(int64(sz))
|
||||
b.ResetTimer()
|
||||
sendb := make([]byte, sz)
|
||||
for i := 0; i < b.N; i++ {
|
||||
rand.Read(sendb)
|
||||
go tunnme.Write(sendb)
|
||||
buf := make([]byte, sz)
|
||||
_, err = io.ReadFull(&tunnpeer, buf)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
|
||||
time.Sleep(time.Second) // wait packets all received
|
||||
|
||||
tunnme.Stop()
|
||||
tunnpeer.Stop()
|
||||
}
|
||||
|
||||
func testTunnelNetwork(t *testing.T, nw string, mtu uint16) {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
testTunnel(t, true, nil) // test plain text
|
||||
// test without base14
|
||||
testTunnel(t, nw, true, false, nil, mtu) // test plain text
|
||||
testTunnel(t, nw, false, false, nil, mtu) // test normal
|
||||
|
||||
testTunnel(t, false, nil) // test normal
|
||||
// test with base14
|
||||
testTunnel(t, nw, true, true, nil, mtu) // test plain text
|
||||
testTunnel(t, nw, false, true, nil, mtu) // test normal
|
||||
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
testTunnel(t, false, &buf) // test preshared
|
||||
// test without base14
|
||||
testTunnel(t, nw, false, false, &buf, mtu) // test preshared
|
||||
|
||||
// test with base14
|
||||
testTunnel(t, nw, false, true, &buf, mtu) // test preshared
|
||||
}
|
||||
|
||||
func benchmarkTunnelNetwork(b *testing.B, nw string, mtu uint16) {
|
||||
logrus.SetLevel(logrus.ErrorLevel)
|
||||
logrus.SetFormatter(&logFormat{enableColor: false})
|
||||
|
||||
for i := 1; i <= 4; i++ {
|
||||
sz := 1024 * i
|
||||
b.Run(fmt.Sprintf("%d-plain-nob14", sz), func(b *testing.B) {
|
||||
benchmarkTunnel(b, sz, nw, true, false, nil, mtu) // test plain text
|
||||
})
|
||||
b.Run(fmt.Sprintf("%d-normal-nob14", sz), func(b *testing.B) {
|
||||
benchmarkTunnel(b, sz, nw, false, false, nil, mtu) // test normal
|
||||
})
|
||||
b.Run(fmt.Sprintf("%d-plain-b14", sz), func(b *testing.B) {
|
||||
benchmarkTunnel(b, sz, nw, true, true, nil, mtu) // test plain text
|
||||
})
|
||||
b.Run(fmt.Sprintf("%d-normal-b14", sz), func(b *testing.B) {
|
||||
benchmarkTunnel(b, sz, nw, false, true, nil, mtu) // test normal
|
||||
})
|
||||
var buf [32]byte
|
||||
_, err := rand.Read(buf[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b.Run(fmt.Sprintf("%d-preshared-nob14", sz), func(b *testing.B) {
|
||||
benchmarkTunnel(b, sz, nw, false, false, &buf, mtu) // test preshared
|
||||
})
|
||||
b.Run(fmt.Sprintf("%d-preshared-b14", sz), func(b *testing.B) {
|
||||
benchmarkTunnel(b, sz, nw, false, true, &buf, mtu) // test preshared
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// logFormat specialize for go-cqhttp
|
||||
@@ -169,8 +397,9 @@ type logFormat struct {
|
||||
|
||||
// Format implements logrus.Formatter
|
||||
func (f logFormat) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buf := helper.SelectWriter()
|
||||
defer helper.PutWriter(buf)
|
||||
// this writer will not be put back
|
||||
|
||||
buf := bin.SelectWriter()
|
||||
|
||||
buf.WriteByte('[')
|
||||
if f.enableColor {
|
||||
@@ -184,9 +413,10 @@ func (f logFormat) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
buf.WriteString(entry.Message)
|
||||
buf.WriteString("\n")
|
||||
|
||||
ret := make([]byte, len(buf.Bytes()))
|
||||
copy(ret, buf.Bytes()) // copy buffer
|
||||
return ret, nil
|
||||
b := buf.ToBytes().Copy()
|
||||
buf.Destroy()
|
||||
|
||||
return b.Trans(), nil
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -9,10 +9,18 @@ import (
|
||||
curve "github.com/fumiama/go-x25519"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/icmp" // support icmp connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/ip" // support ip connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/tcp" // support tcp connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/udp" // support udp connection
|
||||
_ "github.com/fumiama/WireGold/gold/p2p/udplite" // support udplite connection
|
||||
_ "github.com/fumiama/WireGold/gold/proto/data" // support data proto
|
||||
_ "github.com/fumiama/WireGold/gold/proto/hello" // support hello proto
|
||||
_ "github.com/fumiama/WireGold/gold/proto/nat" // support nat proto
|
||||
|
||||
"github.com/fumiama/WireGold/config"
|
||||
"github.com/fumiama/WireGold/gold/link"
|
||||
"github.com/fumiama/WireGold/helper"
|
||||
"github.com/fumiama/WireGold/lower"
|
||||
"github.com/fumiama/WireGold/internal/bin"
|
||||
)
|
||||
|
||||
const suffix32 = "㴄"
|
||||
@@ -28,13 +36,13 @@ func NewWireGold(c *config.Config) (wg WG, err error) {
|
||||
wg.c = c
|
||||
|
||||
var k []byte
|
||||
k, err = base14.UTF82UTF16BE(helper.StringToBytes(c.PrivateKey + suffix32))
|
||||
k, err = base14.UTF82UTF16BE(bin.StringToBytes(c.PrivateKey + suffix32))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n := copy(wg.key[:], base14.Decode(k))
|
||||
if n != 32 {
|
||||
err = errors.New("private key length is not 32")
|
||||
err = errors.New("private key length != 32, got " + strconv.Itoa(n))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -43,7 +51,7 @@ func NewWireGold(c *config.Config) (wg WG, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
wg.PublicKey = helper.BytesToString(pubk[:57])
|
||||
wg.PublicKey = bin.BytesToString(pubk[:57])
|
||||
|
||||
return
|
||||
}
|
||||
@@ -55,7 +63,7 @@ func (wg *WG) Start(srcport, destport uint16) {
|
||||
func (wg *WG) Run(srcport, destport uint16) {
|
||||
wg.init(srcport, destport)
|
||||
_, _ = wg.me.ListenNIC()
|
||||
logrus.Info("[wg] stopped")
|
||||
logrus.Infoln("[wg] stopped")
|
||||
}
|
||||
|
||||
func (wg *WG) Stop() {
|
||||
@@ -69,9 +77,13 @@ func (wg *WG) init(srcport, dstport uint16) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
myip := net.ParseIP(wg.c.IP)
|
||||
if myip == nil {
|
||||
panic("invalid ip " + wg.c.IP)
|
||||
}
|
||||
for _, p := range wg.c.Peers {
|
||||
for _, ip := range p.AllowedIPs {
|
||||
if len(ip) == 0 || ip[0] == 'x' {
|
||||
if len(ip) == 0 || ip[0] == 'x' || ip[0] == 'y' {
|
||||
continue
|
||||
}
|
||||
ipnet, _, err := net.ParseCIDR(ip)
|
||||
@@ -91,20 +103,27 @@ func (wg *WG) init(srcport, dstport uint16) {
|
||||
}
|
||||
|
||||
wg.me = link.NewMe(&link.MyConfig{
|
||||
MyIPwithMask: wg.c.IP + "/32",
|
||||
MyIPwithMask: myip.String() + "/32",
|
||||
MyEndpoint: wg.c.EndPoint,
|
||||
Network: wg.c.Network,
|
||||
PrivateKey: &wg.key,
|
||||
NIC: lower.NewNIC(wg.c.IP, wg.c.SubNet, strconv.FormatInt(wg.c.MTU, 10), cidrs...),
|
||||
SrcPort: srcport,
|
||||
DstPort: dstport,
|
||||
MTU: uint16(wg.c.MTU),
|
||||
SpeedLoop: wg.c.SpeedLoop,
|
||||
Mask: wg.c.Mask,
|
||||
NICConfig: &link.NICConfig{
|
||||
IP: myip,
|
||||
SubNet: mysubnet,
|
||||
CIDRs: cidrs,
|
||||
},
|
||||
SrcPort: srcport,
|
||||
DstPort: dstport,
|
||||
MTU: uint16(wg.c.MTU),
|
||||
SpeedLoop: wg.c.SpeedLoop,
|
||||
Mask: wg.c.Mask,
|
||||
Base14: wg.c.Base14,
|
||||
MaxTTL: wg.c.MaxTTL,
|
||||
})
|
||||
|
||||
for _, peer := range wg.c.Peers {
|
||||
var peerkey [32]byte
|
||||
k, err := base14.UTF82UTF16BE(helper.StringToBytes(peer.PublicKey + suffix32))
|
||||
k, err := base14.UTF82UTF16BE(bin.StringToBytes(peer.PublicKey + suffix32))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -114,7 +133,7 @@ func (wg *WG) init(srcport, dstport uint16) {
|
||||
}
|
||||
var pshk *[32]byte
|
||||
if peer.PresharedKey != "" {
|
||||
k, err := base14.UTF82UTF16BE(helper.StringToBytes(peer.PresharedKey + suffix32))
|
||||
k, err := base14.UTF82UTF16BE(bin.StringToBytes(peer.PresharedKey + suffix32))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -127,6 +146,9 @@ func (wg *WG) init(srcport, dstport uint16) {
|
||||
if peer.MTU >= 65535 {
|
||||
panic("peer " + peer.IP + ": MTU too large")
|
||||
}
|
||||
if peer.MTU == 0 {
|
||||
peer.MTU = wg.c.MTU
|
||||
}
|
||||
if peer.MTURandomRange >= peer.MTU/2 {
|
||||
panic("peer " + peer.IP + ": MTURandomRange too large")
|
||||
}
|
||||
@@ -144,6 +166,7 @@ func (wg *WG) init(srcport, dstport uint16) {
|
||||
AllowTrans: peer.AllowTrans,
|
||||
NoPipe: true,
|
||||
UseZstd: peer.UseZstd,
|
||||
DoublePacket: peer.DoublePacket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user