Compare commits

...
136 Commits
Author SHA1 Message Date
xjasonlyu 883915ab2c Chore: update README 2022-04-04 17:14:03 +08:00
xjasonlyu 4ca3c90b8c Chore: update modules 2022-04-04 17:06:11 +08:00
xjasonlyu 2e758d1960 Chore(core/tcp): add a comment 2022-04-04 17:06:06 +08:00
xjasonlyu 9797cb31c0 Refactor: return metadata.Addr only 2022-04-03 23:16:36 +08:00
xjasonlyu 8f97bda4f5 Chore: amend log messages 2022-04-03 22:31:59 +08:00
xjasonlyu 6a53c52167 Chore: add udp-timeout constraint 2022-04-03 22:09:41 +08:00
xjasonlyu 992e716216 Improve: use interface index for macos 2022-04-02 16:08:55 +08:00
xjasonlyu 289ea82829 Feature(docker): add tcp options env 2022-04-02 15:15:06 +08:00
xjasonlyu a0d31261b9 Chore: remove redundant alias 2022-04-02 15:06:30 +08:00
xjasonlyu 31e19a0690 Change: udp timeout = time.Duration 2022-04-02 15:05:47 +08:00
xjasonlyu 596056676c Chore: rename to netstack 2022-04-02 00:23:49 +08:00
xjasonlyu bff32beb73 Chore: add/update modules 2022-04-02 00:03:28 +08:00
xjasonlyu 551e2c345c Feature: cli tcp options 2022-04-01 23:40:17 +08:00
xjasonlyu dc2c555865 Fix: correctly apply recv buffer size 2022-04-01 22:34:44 +08:00
xjasonlyu 81c2d6963b Chore: reorder const and func 2022-04-01 22:26:05 +08:00
xjasonlyu b7c3c9001b Chore: add benchmark.png 2022-04-01 22:10:09 +08:00
xjasonlyu 9f239d146b Chore: separate send/recv buffer 2022-04-01 21:48:17 +08:00
xjasonlyu 3c326c01ed Change: disable tcp recv buffer auto-tuning by default 2022-04-01 21:11:33 +08:00
xjasonlyu 21eb99a37e Fix: should apply default option firstly 2022-04-01 14:28:12 +08:00
xjasonlyu e0b0a1e94b Chore(core): amend message 2022-03-31 23:56:06 +08:00
xjasonlyu 096117dda1 Chore: minor declaration 2022-03-31 23:12:11 +08:00
xjasonlyu 21232703af Chore(core): use config.PrintFunc 2022-03-31 17:48:52 +08:00
xjasonlyu ccf53dcb88 Chore: use mirror.Tunnel 2022-03-31 17:28:12 +08:00
xjasonlyu 8d3f8d7631 Fix: potential panic on windows (#110)
This is only a temporary solution.
2022-03-31 15:11:34 +08:00
xjasonlyu 9f7989a9d0 Fix: import missing 2022-03-31 14:06:56 +08:00
xjasonlyu b5f61c0999 Fix(core): race cond when init 2022-03-31 00:04:12 +08:00
xjasonlyu e6fc4adccd Feature(restapi): add netstats 2022-03-30 23:38:31 +08:00
xjasonlyu abdbaa6b83 Refactor(engine): remove struct 2022-03-30 23:09:50 +08:00
xjasonlyu cba7e19d22 Chore(restapi): minor adjustment 2022-03-30 17:56:18 +08:00
xjasonlyu 2aea811072 Chore: with engine 2022-03-29 19:42:44 +08:00
xjasonlyu b166ed5e66 Refactor: stats -> restapi 2022-03-29 19:25:45 +08:00
xjasonlyu e6911cb6fb Chore: remove config example 2022-03-29 18:45:38 +08:00
xjasonlyu 3999c5d66b Refactor(core): with error callback 2022-03-29 17:19:54 +08:00
xjasonlyu 0a9f7f123c Chore: use tcpip.Error 2022-03-29 16:18:52 +08:00
xjasonlyu ba7a7ddc95 Chore(core/adapter): rename to TransportHandler 2022-03-29 15:26:00 +08:00
xjasonlyu 20fe2e4cd8 Chore: minor rename 2022-03-29 14:12:25 +08:00
xjasonlyu 201e79ac71 Fix: nil options panic 2022-03-29 13:39:18 +08:00
xjasonlyu a4bedf6080 Refactor: new dialer impl 2022-03-29 13:39:18 +08:00
xjasonlyu 42d6c96b6b Fix: wrong wait logic 2022-03-29 13:39:18 +08:00
xjasonlyu 9d7dacbea1 Chore(core/device): tun constants 2022-03-29 13:39:18 +08:00
xjasonlyu 0e8b16f9a1 Fix: decrease packet reference (#106)
ref: https://github.com/google/gvisor/commit/f375784d83852b1e3ff20cc9de0648b3c0cf8525
2022-03-29 13:39:18 +08:00
xjasonlyu c2af4c0c7c Fix: wait for device to close 2022-03-29 13:39:18 +08:00
xjasonlyu ba0a4acbda Refactor: core.CreateStackWithOptions 2022-03-29 13:39:18 +08:00
xjasonlyu 898e648cb5 Chore: close stack when stop 2022-03-29 13:39:18 +08:00
xjasonlyu 620d5ac834 Chore: put log in engine 2022-03-29 13:39:18 +08:00
xjasonlyu 7df522a91f Revert: GOAMD64v1 as default (#104)
GOAMD64=v3 will not run on older x86 processors that don't support AVX2.
2022-03-29 13:39:18 +08:00
xjasonlyu 376a1eac2c Fix: early stopped engine (#105) 2022-03-29 13:39:18 +08:00
xjasonlyu 584c9c7805 Chore: explicit resolver 2022-03-29 13:39:18 +08:00
xjasonlyu c6ca52326a Refactor: main.go 2022-03-29 13:39:18 +08:00
xjasonlyu ae07fbdb68 Chore: delete common/automaxprocs 2022-03-29 13:39:18 +08:00
xjasonlyu 531125ee1f Chore: minor refactor 2022-03-29 13:39:18 +08:00
xjasonlyu 7ac97016fe Chore: move all to version 2022-03-29 13:39:18 +08:00
xjasonlyu d3fc3abbb7 Migration: go 1.18 2022-03-29 13:38:50 +08:00
xjasonlyu 8bb8423e50 Chore: fix lint 2022-03-26 15:06:42 +08:00
xjasonlyu 82fd2f91c0 Chore: update action workflows 2022-03-26 14:49:57 +08:00
xjasonlyu 7bbae5549d Chore: adjust Dockerfile 2022-03-26 14:34:26 +08:00
xjasonlyu c68dd0771e Chore: make tcpKeepaliveCount explicit 2022-02-13 14:57:31 +08:00
xjasonlyu 5679d15442 Fix: nil addr panic (#99) 2022-02-12 21:16:43 +08:00
xjasonlyu 4be2734b19 Chore: make internal/version 2022-02-09 17:29:10 +08:00
xjasonlyu 283008536b Feature: more fields in stats version 2022-02-09 17:25:55 +08:00
xjasonlyu 2f21e10be6 Feature: show debug in version 2022-02-09 17:21:20 +08:00
xjasonlyu 8d2170832c Chore(engine): improve log message 2022-02-09 16:49:54 +08:00
xjasonlyu 211831b3e6 Fix(iobased): exit outboundLoop to prevent goroutine leak 2022-02-08 20:17:30 +08:00
xjasonlyu 6a8bc0fd79 Chore(socks5): revise handshake error message 2022-02-08 18:55:03 +08:00
xjasonlyu 9d8251ac43 Fix: google/gvisor@f33d034fec (google/gvisor#7125) 2022-02-08 14:38:24 +08:00
xjasonlyu bf35298289 Fix: suppress panic when wintun.dll not found 2022-02-07 17:31:28 +08:00
xjasonlyu b5794661b5 Chore: update go modules 2022-02-06 21:39:56 +08:00
xjasonlyu dc2794ae1e Improve(statistic): use google/uuid 2022-02-06 21:02:49 +08:00
xjasonlyu 6cfbf4d0e6 Chore: add TODOs 2022-02-06 20:41:49 +08:00
xjasonlyu edec658cd0 Refactor: use core/adapter 2022-02-06 20:26:09 +08:00
xjasonlyu 93a5ff5d86 Improve(stack): add TCP recovery option 2022-02-06 20:17:45 +08:00
xjasonlyu 1b38ce2d25 Improve(socks5): unify addr parsing 2022-02-06 19:48:12 +08:00
xjasonlyu cc56100f15 Chore(metadata): add TCPAddr method 2022-02-05 20:14:05 +08:00
xjasonlyu 575a2a66ac Chore(socks5): add rfc annotation 2022-02-05 19:16:57 +08:00
xjasonlyu d552de237f Fix: reset timeout in udp tunnel 2022-02-05 16:59:23 +08:00
xjasonlyu 6603c1f334 Refactor: improve metadata structure 2022-02-05 16:51:17 +08:00
xjasonlyu dd0cde04b4 Refactor: optimize UDP module
Symmetric NAT support for now.
2022-02-05 15:49:03 +08:00
xjasonlyu 14c663c40e Improve: Makefile debug target 2022-02-03 21:45:16 +08:00
xjasonlyu 6547625688 Fix: UDP packet buffer leak (#82) 2022-02-02 21:29:27 +08:00
xjasonlyu bdf85afa3e Chore: use versionize 2022-02-02 20:29:43 +08:00
xjasonlyu c85cf60a45 Improve: reduce bytes copy 2022-02-02 19:48:38 +08:00
xjasonlyu 47e74ed8c2 Fix: release packet buffer (#82) 2022-02-02 17:17:59 +08:00
xjasonlyu e36c2eb226 Improve: use *channel.Endpoint for underlying I/O 2022-02-02 17:17:43 +08:00
xjasonlyu b28349235f Chore(stack): update opts.go 2022-02-01 20:34:43 +08:00
xjasonlyu faed47da40 Chore: fix lint 2022-02-01 17:30:34 +08:00
xjasonlyu 13b5cc71d7 Improve(stack): specify NIC options 2022-02-01 17:19:51 +08:00
xjasonlyu d415ed35d7 Improve(iobased): impl stack.GSOEndpoint 2022-02-01 17:18:17 +08:00
xjasonlyu cd5b9e8954 Chore: rename to fdbased 2022-02-01 17:17:09 +08:00
xjasonlyu c2ec509cfa Fix(core): update gVisor (#82, #95) 2022-02-01 17:16:39 +08:00
xjasonlyu b581c2e877 Style(workflows): auto reformat 2022-02-01 15:47:10 +08:00
xjasonlyu 5d81f455bd Chore(stack): update comments 2022-02-01 13:18:25 +08:00
xjasonlyu 7e268cfc7a Chore: update issue template 2022-01-31 23:18:32 +08:00
xjasonlyu 3480680806 Chore: chmod -x .go files 2022-01-31 18:32:11 +08:00
xjasonlyu fd000c6617 Refactor: standalone metadata module 2022-01-31 16:03:10 +08:00
xjasonlyu 830c231c43 Refactor: standalone version module 2022-01-31 16:00:17 +08:00
xjasonlyu 0e5dafd36b Chore(docker): re-tag to dev 2022-01-31 14:26:31 +08:00
xjasonlyu 40dfe8807a Chore(iobased): use MTU() 2022-01-29 21:00:43 +08:00
xjasonlyu e3007f0498 Chore: import with newline 2022-01-29 21:00:19 +08:00
xjasonlyu e23837aa6d Style: use gofumpt for fmt 2022-01-29 17:26:24 +08:00
xjasonlyu 1d229ac859 Chore: update workflows 2022-01-29 17:06:01 +08:00
Jason LyuandGitHub 300401ad76 Chore: create codeql-analysis.yml 2022-01-29 17:04:54 +08:00
xjasonlyu e3b57bb8d8 Chore: update issue template 2022-01-29 16:56:03 +08:00
xjasonlyu f114b435d3 Chore: issue template config 2022-01-29 16:34:00 +08:00
xjasonlyu b394b09790 Chore: adjust entrypoint.sh 2022-01-29 14:09:59 +08:00
xjasonlyu 59f682dfab Chore: rename to iobased 2022-01-28 21:10:10 +08:00
xjasonlyu 3b82a085d7 Chore: rename to config.yml 2022-01-28 19:59:32 +08:00
xjasonlyu 4b4b01a507 Improve: use bytes.Buffer pool 2022-01-28 15:30:23 +08:00
xjasonlyu 1bbb51b332 Chore: use automaxprocs 2022-01-28 15:01:04 +08:00
xjasonlyu 2a2420f89d Fix: revert module
Temporarily revert to previous gVisor version due to unstable bugs.
2022-01-20 15:07:42 +08:00
xjasonlyu 95f6464174 Chore: impl interface 2022-01-19 19:52:45 +08:00
xjasonlyu 3fa0820552 Chore: go mod update 2022-01-19 19:52:03 +08:00
Fan LinandGitHub c068fbd626 Fix: set major version to v2 (#88) 2022-01-03 16:06:18 +08:00
xjasonlyu 9370983c63 Chore: upgrade go-chi/chi to v5 2021-12-24 11:31:55 +08:00
xjasonlyu 5fe7c9a5bb Chore: update README_ZH 2021-12-23 20:01:50 +08:00
xjasonlyu c1ba254957 Improve: add pprof debug 2021-12-08 17:00:28 +08:00
xjasonlyu 4ffe32fd89 Improve: add default mtu for fd device 2021-12-08 16:27:45 +08:00
xjasonlyu 8e00168914 Chore: regular module updating 2021-12-08 12:32:23 +08:00
xjasonlyu 0d51d2db54 Chore: update go modules 2021-11-27 13:36:13 +08:00
xjasonlyu cf557f0eb1 Fix: control should return error 2021-11-27 13:27:44 +08:00
xjasonlyu 6bb44f9eb1 Change: use nop for reject 2021-11-27 13:15:01 +08:00
Jason LyuandGitHub 40824d8350 Update issue templates 2021-11-22 11:09:06 +08:00
xjasonlyu 5ee4c676a1 Feature: YAML config support 2021-11-10 13:38:20 +08:00
xjasonlyu 733c4cb779 Chore: grammar correction 2021-11-08 21:11:35 +08:00
xjasonlyu af55e8517c Chore: rename to SerializeSocksAddr 2021-11-08 21:06:24 +08:00
xjasonlyu 8fcd8fee85 Fix(fd): compile error 2021-11-01 14:01:21 +08:00
xjasonlyu e06cce1dd4 Chore: update go modules
$ go mod tidy -compat=1.17
2021-11-01 13:07:07 +08:00
xjasonlyu 867cfefa76 Chore(workflows/go): ignore docs 2021-10-19 21:13:14 +08:00
xjasonlyu 176324412b Chore: update README 2021-10-19 21:11:29 +08:00
xjasonlyu 66e9c6d4e9 Chore: update modules 2021-10-17 14:30:19 +08:00
xjasonlyu 3928350e57 Chore: minor reposition 2021-10-17 14:29:08 +08:00
xjasonlyu f6ba31f121 Feature: support fd device
Experimental support for file descriptor based device, may be used like `fd://3`
2021-10-17 14:21:37 +08:00
xjasonlyu bf3f4599cc Chore(tun): remove with options 2021-10-17 14:12:54 +08:00
xjasonlyu dc8e4f1ba1 Feature: new fd driver 2021-10-17 14:07:11 +08:00
xjasonlyu ad53cc7ffe Chore: correct a typo 2021-10-12 19:55:21 +08:00
xjasonlyu 10022388a0 Chore(workflows): ignore GSC tags 2021-10-04 13:32:18 +08:00
xjasonlyu 377aae12c7 Platform: Windows ARM64 2021-10-04 13:13:31 +08:00
117 changed files with 2194 additions and 2682 deletions
-33
View File
@@ -1,33 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Environment**
- OS: [e.g. `Ubuntu-20.04`]
- Version: [e.g. `v2.1.0`]
- Network: [e.g. route tables, iptables rules]
**Log**
Paste the tun2socks log below with the log level set to `DEBUG`.
```
```
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. [First Step]
2. [Second Step]
3. ……
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional context**
Add any other context about the problem here.
+55
View File
@@ -0,0 +1,55 @@
name: Bug report
description: Create a report to help us improve
title: "[Bug] "
body:
- type: checkboxes
id: ensure
attributes:
label: Verify steps
description: Please verify that you've followed these steps
options:
- label: Is this something you can **debug and fix**? Send a pull request! Bug fixes and documentation fixes are welcome.
required: true
- label: I have searched on the [issue tracker](……/) for a related issue.
required: true
- type: input
attributes:
label: Version
validations:
required: true
- type: dropdown
id: os
attributes:
label: What OS are you seeing the problem on?
multiple: true
options:
- Windows
- Linux
- macOS
- OpenBSD/FreeBSD
- Other
- type: textarea
attributes:
label: Description
validations:
required: true
- type: textarea
attributes:
label: CLI or Config
description: Paste the command line parameters or configuration below.
- type: textarea
attributes:
render: shell
label: Logs
description: Paste the logs below with the log level set to `DEBUG`.
- type: textarea
attributes:
label: How to Reproduce
description: Steps to reproduce the behavior, if any.
+9
View File
@@ -0,0 +1,9 @@
blank_issues_enabled: true
contact_links:
- name: tun2socks GitHub Wiki
url: https://github.com/xjasonlyu/tun2socks/wiki
about: Please see the wiki for common configurations
- name: tun2socks GitHub Discussions
url: https://github.com/xjasonlyu/tun2socks/discussions
about: Ask questions and get help on GitHub Discussions
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem?**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
@@ -0,0 +1,25 @@
name: Feature request
description: Suggest an idea or improvement
title: "[Feature] "
body:
- type: textarea
id: description
attributes:
label: Description
placeholder: A clear description of the feature or enhancement.
validations:
required: true
- type: textarea
id: related
attributes:
label: Is this feature related to a specific bug?
description: Please include a bug references if yes.
- type: textarea
id: solution
attributes:
label: Do you have a specific solution in mind?
description: >
Please include any details about a solution that you have in mind,
including any alternatives considered.
+30
View File
@@ -0,0 +1,30 @@
name: "CodeQL"
on:
push:
branches: [ main ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+6 -9
View File
@@ -6,9 +6,6 @@ on:
- 'main'
tags:
- '*'
paths-ignore:
- '**.md'
- 'docs/**'
jobs:
@@ -18,7 +15,7 @@ jobs:
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
@@ -50,16 +47,16 @@ jobs:
run: |
echo ::set-output name=version::$(git describe --tags --abbrev=0)
- name: Build and Push (nightly)
- name: Build and Push (dev)
if: github.ref == 'refs/heads/main'
uses: docker/build-push-action@v2
with:
context: .
push: true
platforms: linux/amd64,linux/arm/v7,linux/arm64
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
tags: |
xjasonlyu/tun2socks:nightly
ghcr.io/xjasonlyu/tun2socks:nightly
xjasonlyu/tun2socks:dev
ghcr.io/xjasonlyu/tun2socks:dev
- name: Build and Push (latest)
if: startsWith(github.ref, 'refs/tags/')
@@ -67,7 +64,7 @@ jobs:
with:
context: .
push: true
platforms: linux/amd64,linux/arm/v7,linux/arm64
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
tags: |
xjasonlyu/tun2socks:latest
xjasonlyu/tun2socks:${{ steps.shell.outputs.version }}
-35
View File
@@ -1,35 +0,0 @@
name: Go Static Check
on:
push:
pull_request:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.17.x
- name: Cache go module
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Get dependencies, run test and static check
run: |
go test ./...
go vet ./...
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck -- $(go list ./...)
+22
View File
@@ -0,0 +1,22 @@
name: Linter
on: [ push, pull_request ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get latest go version
id: version
run: |
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ steps.version.outputs.go_version }}
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
+20 -8
View File
@@ -4,10 +4,6 @@ on:
push:
tags:
- '*'
paths-ignore:
- '**.md'
- 'docs/**'
- 'docker/**'
jobs:
build:
@@ -15,23 +11,39 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get latest Go version
id: version
run: |
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.17.x
go-version: ${{ steps.version.outputs.go_version }}
- name: Cache go module
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run test
run: |
go test ./...
- name: Build
if: startsWith(github.ref, 'refs/tags/')
run: make -j releases
- name: Upload Releases
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: build/*
draft: true
+16
View File
@@ -0,0 +1,16 @@
linters:
disable-all: true
enable:
- gofumpt
- staticcheck
- govet
- gci
linters-settings:
gci:
sections:
- standard
- prefix(github.com/xjasonlyu/tun2socks)
- default
staticcheck:
go: '1.18'
+9 -8
View File
@@ -1,17 +1,16 @@
FROM golang:alpine AS builder
WORKDIR /tun2socks-src
COPY . /tun2socks-src
WORKDIR /src
COPY . /src
RUN apk add --no-cache make git \
&& make tun2socks \
&& mv ./build/tun2socks /tun2socks
RUN apk add --update --no-cache make git \
&& make tun2socks
FROM alpine:latest
LABEL org.opencontainers.image.source="https://github.com/xjasonlyu/tun2socks"
COPY docker/entrypoint.sh /entrypoint.sh
COPY --from=builder /tun2socks /usr/bin/tun2socks
COPY --from=builder /src/build/tun2socks /usr/bin/tun2socks
RUN apk add --update --no-cache iptables iproute2 \
&& chmod +x /entrypoint.sh
@@ -21,9 +20,11 @@ ENV ADDR=198.18.0.1/15
ENV LOGLEVEL=info
ENV PROXY=direct://
ENV MTU=9000
ENV STATS=
ENV TOKEN=
ENV RESTAPI=
ENV UDP_TIMEOUT=
ENV TCP_SNDBUF=
ENV TCP_RCVBUF=
ENV TCP_AUTO_TUNING=
ENV EXTRA_COMMANDS=
ENV TUN_INCLUDED_ROUTES=
ENV TUN_EXCLUDED_ROUTES=
+34 -4
View File
@@ -1,5 +1,5 @@
BINARY := tun2socks
MODULE := github.com/xjasonlyu/tun2socks
MODULE := github.com/xjasonlyu/tun2socks/v2
BUILD_DIR := build
BUILD_TAGS :=
@@ -11,20 +11,23 @@ CGO_ENABLED := 0
GO111MODULE := on
LDFLAGS += -w -s -buildid=
LDFLAGS += -X "$(MODULE)/constant.Version=$(BUILD_VERSION)"
LDFLAGS += -X "$(MODULE)/constant.GitCommit=$(BUILD_COMMIT)"
LDFLAGS += -X "$(MODULE)/internal/version.Version=$(BUILD_VERSION)"
LDFLAGS += -X "$(MODULE)/internal/version.GitCommit=$(BUILD_COMMIT)"
GO_BUILD = GO111MODULE=$(GO111MODULE) CGO_ENABLED=$(CGO_ENABLED) \
go build $(BUILD_FLAGS) -ldflags '$(LDFLAGS)' -tags '$(BUILD_TAGS)' -trimpath
UNIX_ARCH_LIST = \
darwin-amd64 \
darwin-amd64-v3 \
darwin-arm64 \
freebsd-386 \
freebsd-amd64 \
freebsd-amd64-v3 \
freebsd-arm64 \
linux-386 \
linux-amd64 \
linux-amd64-v3 \
linux-arm64 \
linux-armv5 \
linux-armv6 \
@@ -40,14 +43,20 @@ UNIX_ARCH_LIST = \
linux-s390x \
openbsd-386 \
openbsd-amd64 \
openbsd-amd64-v3 \
openbsd-arm64
WINDOWS_ARCH_LIST = \
windows-386 \
windows-amd64 \
windows-amd64-v3 \
windows-arm64 \
windows-arm32v7
all: linux-amd64 darwin-amd64 windows-amd64
all: linux-amd64 linux-arm64 darwin-amd64 darwin-arm64 windows-amd64
debug: BUILD_TAGS += debug
debug: all
tun2socks:
$(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)
@@ -55,6 +64,9 @@ tun2socks:
darwin-amd64:
GOARCH=amd64 GOOS=darwin $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
darwin-amd64-v3:
GOARCH=amd64 GOOS=darwin GOAMD64=v3 $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
darwin-arm64:
GOARCH=arm64 GOOS=darwin $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
@@ -64,6 +76,9 @@ freebsd-386:
freebsd-amd64:
GOARCH=amd64 GOOS=freebsd $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
freebsd-amd64-v3:
GOARCH=amd64 GOOS=freebsd GOAMD64=v3 $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
freebsd-arm64:
GOARCH=arm64 GOOS=freebsd $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
@@ -73,6 +88,9 @@ linux-386:
linux-amd64:
GOARCH=amd64 GOOS=linux $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
linux-amd64-v3:
GOARCH=amd64 GOOS=linux GOAMD64=v3 $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
linux-arm64:
GOARCH=arm64 GOOS=linux $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
@@ -118,6 +136,9 @@ openbsd-386:
openbsd-amd64:
GOARCH=amd64 GOOS=openbsd $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
openbsd-amd64-v3:
GOARCH=amd64 GOOS=openbsd GOAMD64=v3 $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
openbsd-arm64:
GOARCH=arm64 GOOS=openbsd $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@
@@ -127,6 +148,12 @@ windows-386:
windows-amd64:
GOARCH=amd64 GOOS=windows $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@.exe
windows-amd64-v3:
GOARCH=amd64 GOOS=windows GOAMD64=v3 $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@.exe
windows-arm64:
GOARCH=arm64 GOOS=windows $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@.exe
windows-arm32v7:
GOARCH=arm GOARM=7 GOOS=windows $(GO_BUILD) -o $(BUILD_DIR)/$(BINARY)-$@.exe
@@ -143,5 +170,8 @@ all-arch: $(UNIX_ARCH_LIST) $(WINDOWS_ARCH_LIST)
releases: $(unix_releases) $(windows_releases)
lint:
golangci-lint run --disable-all -E govet -E gofumpt -E megacheck ./...
clean:
rm -rf $(BUILD_DIR)
+32 -23
View File
@@ -1,44 +1,49 @@
![tun2socks](docs/logo.png)
![tun2socks](docs/wordmark.png)
[![GitHub Workflow][1]](https://github.com/xjasonlyu/tun2socks/actions)
[![Go Version][2]](https://github.com/xjasonlyu/tun2socks/blob/main/go.mod)
[![Go Report][3]](https://goreportcard.com/badge/github.com/xjasonlyu/tun2socks)
[![GitHub License][4]](https://github.com/xjasonlyu/tun2socks/blob/main/LICENSE)
[![Releases][5]](https://github.com/xjasonlyu/tun2socks/releases)
[![Maintainability][4]](https://codeclimate.com/github/xjasonlyu/tun2socks/maintainability)
[![GitHub License][5]](https://github.com/xjasonlyu/tun2socks/blob/main/LICENSE)
[![Docker Pulls][6]](https://hub.docker.com/r/xjasonlyu/tun2socks)
[![Releases][7]](https://github.com/xjasonlyu/tun2socks/releases)
[1]: https://img.shields.io/github/workflow/status/xjasonlyu/tun2socks/Go?style=flat-square
[2]: https://img.shields.io/github/go-mod/go-version/xjasonlyu/tun2socks/main?style=flat-square
[3]: https://goreportcard.com/badge/github.com/xjasonlyu/tun2socks?style=flat-square
[4]: https://img.shields.io/github/license/xjasonlyu/tun2socks?style=flat-square
[5]: https://img.shields.io/github/v/release/xjasonlyu/tun2socks?include_prereleases&style=flat-square
[1]: https://img.shields.io/github/workflow/status/xjasonlyu/tun2socks/Go?logo=github
[2]: https://img.shields.io/github/go-mod/go-version/xjasonlyu/tun2socks?logo=go
[3]: https://goreportcard.com/badge/github.com/xjasonlyu/tun2socks
[4]: https://api.codeclimate.com/v1/badges/b5b30239174fc6603aca/maintainability
[5]: https://img.shields.io/github/license/xjasonlyu/tun2socks
[6]: https://img.shields.io/docker/pulls/xjasonlyu/tun2socks?logo=docker
[7]: https://img.shields.io/github/v/release/xjasonlyu/tun2socks
English | [简体中文](README_ZH.md)
## Features
- **Fully support:** IPv4/IPv6/ICMP/TCP/UDP
- **Proxy protocol:** HTTP/Socks4/Socks5/Shadowsocks
- **Game ready:** optimized UDP transmission
- **Pure Go:** no CGO required, stability improved
- **Router mode:** forwarding packets in LAN
- **TCP/IP stack:** powered by **[gVisor](https://github.com/google/gvisor)**
- **High performance:** >2.5Gbps throughput
- Proxy Everything: Handle all network traffic of any internet programs sent by the device through a proxy.
- Proxy Protocols: HTTP/Socks4/Socks5/Shadowsocks with authentication support for remote connections.
- Run Everywhere: Linux/macOS/Windows/FreeBSD/OpenBSD multi-platform support with specific optimization.
- Gateway Mode: Act as a layer three gateway to handle network traffic from other devices in the same network.
- Full IPv6 Support: All functions work in IPv6, tunnel IPv4 connections through IPv6 proxy and vice versa.
- Network Stack: Powered by user-space TCP/IP stack from Google container application kernel **[gVisor](https://github.com/google/gvisor)**.
## Requirements
## Benchmarks
| Target | Minimum | Recommended |
| :----- | :-----: | :---------: |
| System | Linux MacOS Freebsd OpenBSD Windows | Linux or MacOS |
| Memory | >20MB | >128MB |
| CPU | ANY | AMD64 or ARM64 |
For all scenarios of usage, tun2socks performs best. See [here](https://github.com/xjasonlyu/tun2socks/wiki/Benchmarks) for more details.
![benchmark](docs/benchmark.png)
## Documentation
Documentations and quick start guides can be found at [Github Wiki](https://github.com/xjasonlyu/tun2socks/wiki).
- [Install from Source](https://github.com/xjasonlyu/tun2socks/wiki/Install-from-Source)
- [Quickstart Examples](https://github.com/xjasonlyu/tun2socks/wiki/Examples)
- [Memory Optimization](https://github.com/xjasonlyu/tun2socks/wiki/Memory-Optimization)
Full documentation and technical guides can be found at [Wiki](https://github.com/xjasonlyu/tun2socks/wiki).
## Community
Welcome and feel free to ask any questions at [Github Discussions](https://github.com/xjasonlyu/tun2socks/discussions).
Welcome and feel free to ask any questions at [Discussions](https://github.com/xjasonlyu/tun2socks/discussions).
## Credits
@@ -51,3 +56,7 @@ Welcome and feel free to ask any questions at [Github Discussions](https://githu
[GPL-3.0](https://github.com/xjasonlyu/tun2socks/blob/main/LICENSE)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fxjasonlyu%2Ftun2socks.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fxjasonlyu%2Ftun2socks?ref=badge_large)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/xjasonlyu/tun2socks.svg)](https://starchart.cc/xjasonlyu/tun2socks)
+33 -39
View File
@@ -1,59 +1,49 @@
![tun2socks](docs/logo.png)
![tun2socks](docs/wordmark.png)
[![GitHub Workflow][1]](https://github.com/xjasonlyu/tun2socks/actions)
[![Go Version][2]](https://github.com/xjasonlyu/tun2socks/blob/main/go.mod)
[![Go Report][3]](https://goreportcard.com/badge/github.com/xjasonlyu/tun2socks)
[![GitHub License][4]](https://github.com/xjasonlyu/tun2socks/blob/main/LICENSE)
[![Releases][5]](https://github.com/xjasonlyu/tun2socks/releases)
[![Maintainability][4]](https://codeclimate.com/github/xjasonlyu/tun2socks/maintainability)
[![GitHub License][5]](https://github.com/xjasonlyu/tun2socks/blob/main/LICENSE)
[![Docker Pulls][6]](https://hub.docker.com/r/xjasonlyu/tun2socks)
[![Releases][7]](https://github.com/xjasonlyu/tun2socks/releases)
[1]: https://img.shields.io/github/workflow/status/xjasonlyu/tun2socks/Go?style=flat-square
[2]: https://img.shields.io/github/go-mod/go-version/xjasonlyu/tun2socks/main?style=flat-square
[3]: https://goreportcard.com/badge/github.com/xjasonlyu/tun2socks?style=flat-square
[4]: https://img.shields.io/github/license/xjasonlyu/tun2socks?style=flat-square
[5]: https://img.shields.io/github/v/release/xjasonlyu/tun2socks?include_prereleases&style=flat-square
[1]: https://img.shields.io/github/workflow/status/xjasonlyu/tun2socks/Go?logo=github
[2]: https://img.shields.io/github/go-mod/go-version/xjasonlyu/tun2socks?logo=go
[3]: https://goreportcard.com/badge/github.com/xjasonlyu/tun2socks
[4]: https://api.codeclimate.com/v1/badges/b5b30239174fc6603aca/maintainability
[5]: https://img.shields.io/github/license/xjasonlyu/tun2socks
[6]: https://img.shields.io/docker/pulls/xjasonlyu/tun2socks?logo=docker
[7]: https://img.shields.io/github/v/release/xjasonlyu/tun2socks
[English](README.md) | 简体中文
## 为什么使用 tun2socks
通过在主机上运行`tun2socks`,可以轻松地接管所有的`TCP/UDP`流量,同时提供诸多专业的功能特性,这包括:
- 强制使不支持代理的程序走代理
- 配合Clash、V2Ray等工具实现全局代理上网
- 配合Burp、Charles等工具进行应用层数据的调试
- 配合DHCP、CoreDNS等工具部署路由模式代理局域网流量
## 特性介绍
- **全面支持:** IPv4/IPv6/ICMP/TCP/UDP
- **代理协议** HTTP/Socks4/Socks5/Shadowsocks
- **游戏加速:** 针对UDP传输的优化
- **纯Go实现:** 无需CGO,稳定性提升
- **路由模式:** 转发代理局域网内所有流量
- **TCP/IP栈:** 由 **[gVisor](https://github.com/google/gvisor)** 强力驱动
- **高性能:** >2.5Gbps 的带宽吞吐量
- 全局代理: 处理来自本设备的任意网络应用的所有网络流量并通过代理转发。
- 代理协议: 通过 HTTP/Socks4/Socks5/Shadowsocks 远程连接且支持鉴权。
- 跨平台性: 具有 Linux/macOS/Windows/FreeBSD/OpenBSD 特定优化的多平台支持。
- 网关模式: 作为第三层网关处理来自同一网络中其他设备的所有网络流量。
- IPv6 支持: 所有功能都可以在 IPv6 中工作,允许通过 IPv6 代理转发 IPv4 连接,反之亦然。
- TCP/IP 栈: 由来自 Google 容器应用程序内核 **[gVisor](https://github.com/google/gvisor)** 的用户空间 TCP/IP 网络栈强力驱动
## 硬件需求
## 性能测试
| 目标 | 最小 | 建议 |
| :--- | :---: | :---: |
| 系统 | Linux MacOS Freebsd OpenBSD Windows | Linux or MacOS |
| 内存 | >20MB | >128MB |
| 架构 | ANY | AMD64 or ARM64 |
对于任意的使用场景,tun2socks 表现最佳。更多细节看[这里](https://github.com/xjasonlyu/tun2socks/wiki/Benchmarks)。
![benchmark](docs/benchmark.png)
## 使用文档
文档以及使用方式,请看 [Github Wiki](https://github.com/xjasonlyu/tun2socks/wiki)
- [源码安装](https://github.com/xjasonlyu/tun2socks/wiki/Install-from-Source)
- [使用例子](https://github.com/xjasonlyu/tun2socks/wiki/Examples)
- [内存优化](https://github.com/xjasonlyu/tun2socks/wiki/Memory-Optimization)
文档以及使用方式可以在 [Wiki](https://github.com/xjasonlyu/tun2socks/wiki) 里找到。
## 交流讨论
欢迎来讨论区交流提问,[Github Discussions](https://github.com/xjasonlyu/tun2socks/discussions)。
## 注意事项
1. 由于采用了纯Go实现,所以这一版本的`tun2socks`在有大量连接时内存消耗通常较多。如果您的需求对内存消耗极为敏感,请继续使用 [v1](https://github.com/xjasonlyu/tun2socks/tree/v1) 版本。
2. `tun2socks`只应该专注于将网络层的TCP/UDP流量转发给SOCKS服务器,其他的如DNS(DoH)、DHCP等模块功能应该交由第三方应用实现,所以弃用了DNS模块。
3. 因为是通过用户空间的网络栈接管所有流量并处理转发,在高吞吐时CPU的使用量会剧增,所以CPU的性能直接与可以达到的最大带宽挂钩。
欢迎来讨论区 [Discussions](https://github.com/xjasonlyu/tun2socks/discussions) 交流提问
## 特别感谢
@@ -61,8 +51,12 @@
- [google/gvisor](https://github.com/google/gvisor) - Application Kernel for Containers
- [wireguard-go](https://git.zx2c4.com/wireguard-go) - Go Implementation of WireGuard
## License
## 许可协议
[GPL-3.0](https://github.com/xjasonlyu/tun2socks/blob/main/LICENSE)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fxjasonlyu%2Ftun2socks.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fxjasonlyu%2Ftun2socks?ref=badge_large)
## 星星走向
[![Stargazers over time](https://starchart.cc/xjasonlyu/tun2socks.svg)](https://starchart.cc/xjasonlyu/tun2socks)
Executable → Regular
+1 -1
View File
@@ -1,3 +1,3 @@
package observable
type Iterable <-chan interface{}
type Iterable <-chan any
Executable → Regular
View File
+13 -13
View File
@@ -9,8 +9,8 @@ import (
"go.uber.org/atomic"
)
func iterator(item []interface{}) chan interface{} {
ch := make(chan interface{})
func iterator(item []any) chan any {
ch := make(chan any)
go func() {
time.Sleep(100 * time.Millisecond)
for _, elm := range item {
@@ -22,7 +22,7 @@ func iterator(item []interface{}) chan interface{} {
}
func TestObservable(t *testing.T) {
iter := iterator([]interface{}{1, 2, 3, 4, 5})
iter := iterator([]any{1, 2, 3, 4, 5})
src := NewObservable(iter)
data, err := src.Subscribe()
assert.Nil(t, err)
@@ -34,15 +34,15 @@ func TestObservable(t *testing.T) {
}
func TestObservable_MultiSubscribe(t *testing.T) {
iter := iterator([]interface{}{1, 2, 3, 4, 5})
iter := iterator([]any{1, 2, 3, 4, 5})
src := NewObservable(iter)
ch1, _ := src.Subscribe()
ch2, _ := src.Subscribe()
var count = atomic.NewInt32(0)
count := atomic.NewInt32(0)
var wg sync.WaitGroup
wg.Add(2)
waitCh := func(ch <-chan interface{}) {
waitCh := func(ch <-chan any) {
for range ch {
count.Inc()
}
@@ -55,7 +55,7 @@ func TestObservable_MultiSubscribe(t *testing.T) {
}
func TestObservable_UnSubscribe(t *testing.T) {
iter := iterator([]interface{}{1, 2, 3, 4, 5})
iter := iterator([]any{1, 2, 3, 4, 5})
src := NewObservable(iter)
data, err := src.Subscribe()
assert.Nil(t, err)
@@ -65,7 +65,7 @@ func TestObservable_UnSubscribe(t *testing.T) {
}
func TestObservable_SubscribeClosedSource(t *testing.T) {
iter := iterator([]interface{}{1})
iter := iterator([]any{1})
src := NewObservable(iter)
data, _ := src.Subscribe()
<-data
@@ -75,14 +75,14 @@ func TestObservable_SubscribeClosedSource(t *testing.T) {
}
func TestObservable_UnSubscribeWithNotExistSubscription(t *testing.T) {
sub := Subscription(make(chan interface{}))
iter := iterator([]interface{}{1})
sub := Subscription(make(chan any))
iter := iterator([]any{1})
src := NewObservable(iter)
src.UnSubscribe(sub)
}
func TestObservable_SubscribeGoroutineLeak(t *testing.T) {
iter := iterator([]interface{}{1, 2, 3, 4, 5})
iter := iterator([]any{1, 2, 3, 4, 5})
src := NewObservable(iter)
max := 100
@@ -94,7 +94,7 @@ func TestObservable_SubscribeGoroutineLeak(t *testing.T) {
var wg sync.WaitGroup
wg.Add(max)
waitCh := func(ch <-chan interface{}) {
waitCh := func(ch <-chan any) {
for range ch {
}
wg.Done()
@@ -117,7 +117,7 @@ func TestObservable_SubscribeGoroutineLeak(t *testing.T) {
}
func Benchmark_Observable_1000(b *testing.B) {
ch := make(chan interface{})
ch := make(chan any)
o := NewObservable(ch)
num := 1000
Executable → Regular
+4 -4
View File
@@ -4,14 +4,14 @@ import (
"sync"
)
type Subscription <-chan interface{}
type Subscription <-chan any
type Subscriber struct {
buffer chan interface{}
buffer chan any
once sync.Once
}
func (s *Subscriber) Emit(item interface{}) {
func (s *Subscriber) Emit(item any) {
s.buffer <- item
}
@@ -27,7 +27,7 @@ func (s *Subscriber) Close() {
func newSubscriber() *Subscriber {
sub := &Subscriber{
buffer: make(chan interface{}, 200),
buffer: make(chan any, 200),
}
return sub
}
Executable → Regular
+2 -1
View File
@@ -22,7 +22,7 @@ func NewAllocator() *Allocator {
alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K
for k := range alloc.buffers {
i := k
alloc.buffers[k].New = func() interface{} {
alloc.buffers[k].New = func() any {
return make([]byte, 1<<uint32(i))
}
}
@@ -52,6 +52,7 @@ func (alloc *Allocator) Put(buf []byte) error {
}
//lint:ignore SA6002 ignore temporarily
//nolint
alloc.buffers[b].Put(buf)
return nil
}
Executable → Regular
View File
+17
View File
@@ -0,0 +1,17 @@
package pool
import (
"bytes"
"sync"
)
var bufferPool = sync.Pool{New: func() any { return &bytes.Buffer{} }}
func GetBuffer() *bytes.Buffer {
return bufferPool.Get().(*bytes.Buffer)
}
func PutBuffer(buf *bytes.Buffer) {
buf.Reset()
bufferPool.Put(buf)
}
Executable → Regular
View File
-21
View File
@@ -1,21 +0,0 @@
package dialer
import (
"net"
"sync"
)
var _bindOnce sync.Once
// BindToInterface binds dialer to specific interface.
func BindToInterface(name string) error {
i, err := net.InterfaceByName(name)
if err != nil {
return err
}
_bindOnce.Do(func() {
addControl(bindToInterface(i))
})
return nil
}
-26
View File
@@ -1,26 +0,0 @@
package dialer
import (
"net"
"syscall"
"golang.org/x/sys/unix"
)
func bindToInterface(i *net.Interface) controlFunc {
return func(network, address string, c syscall.RawConn) error {
ipStr, _, _ := net.SplitHostPort(address)
if ip := net.ParseIP(ipStr); ip != nil && !ip.IsGlobalUnicast() {
return nil
}
return c.Control(func(fd uintptr) {
switch network {
case "tcp4", "udp4":
unix.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, i.Index)
case "tcp6", "udp6":
unix.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, i.Index)
}
})
}
}
-21
View File
@@ -1,21 +0,0 @@
package dialer
import (
"net"
"syscall"
"golang.org/x/sys/unix"
)
func bindToInterface(i *net.Interface) controlFunc {
return func(network, address string, c syscall.RawConn) error {
ipStr, _, _ := net.SplitHostPort(address)
if ip := net.ParseIP(ipStr); ip != nil && !ip.IsGlobalUnicast() {
return nil
}
return c.Control(func(fd uintptr) {
unix.BindToDevice(int(fd), i.Name)
})
}
}
-15
View File
@@ -1,15 +0,0 @@
//go:build !linux && !darwin
package dialer
import (
"errors"
"net"
"syscall"
)
func bindToInterface(_ *net.Interface) controlFunc {
return func(string, string, syscall.RawConn) error {
return errors.New("unsupported platform")
}
}
-37
View File
@@ -1,37 +0,0 @@
package dialer
import (
"errors"
"net"
"syscall"
)
type controlFunc func(string, string, syscall.RawConn) error
var (
_controlPool = make([]controlFunc, 0, 2)
)
func addControl(f controlFunc) {
_controlPool = append(_controlPool, f)
}
func setControl(i interface{}) {
control := func(address, network string, c syscall.RawConn) error {
for _, f := range _controlPool {
if err := f(address, network, c); err != nil {
return err
}
}
return nil
}
switch v := i.(type) {
case *net.Dialer:
v.Control = control
case *net.ListenConfig:
v.Control = control
default:
panic(errors.New("wrong type"))
}
}
Executable → Regular
+50 -6
View File
@@ -3,20 +3,64 @@ package dialer
import (
"context"
"net"
"syscall"
"go.uber.org/atomic"
)
func Dial(network, address string) (net.Conn, error) {
return DialContext(context.Background(), network, address)
var (
DefaultInterfaceName = atomic.NewString("")
DefaultInterfaceIndex = atomic.NewInt32(0)
DefaultRoutingMark = atomic.NewInt32(0)
)
type Options struct {
// InterfaceName is the name of interface/device to bind.
// If a socket is bound to an interface, only packets received
// from that particular interface are processed by the socket.
InterfaceName string
// InterfaceIndex is the index of interface/device to bind.
// It is almost the same as InterfaceName except it uses the
// index of the interface instead of the name.
InterfaceIndex int
// RoutingMark is the mark for each packet sent through this
// socket. Changing the mark can be used for mark-based routing
// without netfilter or for packet filtering.
RoutingMark int
}
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
d := &net.Dialer{}
setControl(d)
return DialContextWithOptions(ctx, network, address, &Options{
InterfaceName: DefaultInterfaceName.Load(),
InterfaceIndex: int(DefaultInterfaceIndex.Load()),
RoutingMark: int(DefaultRoutingMark.Load()),
})
}
func DialContextWithOptions(ctx context.Context, network, address string, opts *Options) (net.Conn, error) {
d := &net.Dialer{
Control: func(network, address string, c syscall.RawConn) error {
return setSocketOptions(network, address, c, opts)
},
}
return d.DialContext(ctx, network, address)
}
func ListenPacket(network, address string) (net.PacketConn, error) {
lc := &net.ListenConfig{}
setControl(lc)
return ListenPacketWithOptions(network, address, &Options{
InterfaceName: DefaultInterfaceName.Load(),
InterfaceIndex: int(DefaultInterfaceIndex.Load()),
RoutingMark: int(DefaultRoutingMark.Load()),
})
}
func ListenPacketWithOptions(network, address string, opts *Options) (net.PacketConn, error) {
lc := &net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
return setSocketOptions(network, address, c, opts)
},
}
return lc.ListenPacket(context.Background(), network, address)
}
-14
View File
@@ -1,14 +0,0 @@
package dialer
import (
"sync"
)
var _setOnce sync.Once
// SetMark sets the mark for each packet sent through this dialer(socket).
func SetMark(i int) {
_setOnce.Do(func() {
addControl(setMark(i))
})
}
-15
View File
@@ -1,15 +0,0 @@
package dialer
import (
"syscall"
"golang.org/x/sys/unix"
)
func setMark(i int) controlFunc {
return func(_, _ string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, i)
})
}
}
-14
View File
@@ -1,14 +0,0 @@
//go:build !linux
package dialer
import (
"errors"
"syscall"
)
func setMark(_ int) controlFunc {
return func(string, string, syscall.RawConn) error {
return errors.New("fwmark: linux only")
}
}
+19
View File
@@ -0,0 +1,19 @@
package dialer
func isTCPSocket(network string) bool {
switch network {
case "tcp", "tcp4", "tcp6":
return true
default:
return false
}
}
func isUDPSocket(network string) bool {
switch network {
case "udp", "udp4", "udp6":
return true
default:
return false
}
}
+45
View File
@@ -0,0 +1,45 @@
package dialer
import (
"net"
"syscall"
"golang.org/x/sys/unix"
)
func setSocketOptions(network, address string, c syscall.RawConn, opts *Options) (err error) {
if opts == nil || !isTCPSocket(network) && !isUDPSocket(network) {
return
}
var innerErr error
err = c.Control(func(fd uintptr) {
host, _, _ := net.SplitHostPort(address)
if ip := net.ParseIP(host); ip != nil && !ip.IsGlobalUnicast() {
return
}
if opts.InterfaceIndex == 0 && opts.InterfaceName != "" {
if iface, err := net.InterfaceByName(opts.InterfaceName); err == nil {
opts.InterfaceIndex = iface.Index
}
}
if opts.InterfaceIndex != 0 {
switch network {
case "tcp4", "udp4":
innerErr = unix.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, opts.InterfaceIndex)
case "tcp6", "udp6":
innerErr = unix.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, opts.InterfaceIndex)
}
if innerErr != nil {
return
}
}
})
if innerErr != nil {
err = innerErr
}
return
}
+44
View File
@@ -0,0 +1,44 @@
package dialer
import (
"net"
"syscall"
"golang.org/x/sys/unix"
)
func setSocketOptions(network, address string, c syscall.RawConn, opts *Options) (err error) {
if opts == nil || !isTCPSocket(network) && !isUDPSocket(network) {
return
}
var innerErr error
err = c.Control(func(fd uintptr) {
host, _, _ := net.SplitHostPort(address)
if ip := net.ParseIP(host); ip != nil && !ip.IsGlobalUnicast() {
return
}
if opts.InterfaceName == "" && opts.InterfaceIndex != 0 {
if iface, err := net.InterfaceByIndex(opts.InterfaceIndex); err == nil {
opts.InterfaceName = iface.Name
}
}
if opts.InterfaceName != "" {
if innerErr = unix.BindToDevice(int(fd), opts.InterfaceName); innerErr != nil {
return
}
}
if opts.RoutingMark != 0 {
if innerErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, opts.RoutingMark); innerErr != nil {
return
}
}
})
if innerErr != nil {
err = innerErr
}
return
}
+9
View File
@@ -0,0 +1,9 @@
//go:build !linux && !darwin
package dialer
import "syscall"
func setSocketOptions(network, address string, c syscall.RawConn, opts *Options) error {
return nil
}
Executable → Regular
View File
Executable → Regular
View File
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"net"
"net/http"
"github.com/xjasonlyu/tun2socks/common/pool"
"github.com/xjasonlyu/tun2socks/v2/common/pool"
)
// HTTPObfs is shadowsocks http simple-obfs implementation
+6 -4
View File
@@ -8,7 +8,7 @@ import (
"net"
"time"
"github.com/xjasonlyu/tun2socks/common/pool"
"github.com/xjasonlyu/tun2socks/v2/common/pool"
)
func init() {
@@ -68,8 +68,8 @@ func (to *TLSObfs) Read(b []byte) (int, error) {
}
if to.firstResponse {
// type + ver + len_size + 91 = 96
// type + ver + len_size + 1 = 6
// type + ver + lensize + 91 = 96
// type + ver + lensize + 1 = 6
// type + ver = 3
to.firstResponse = false
return to.read(b, 105)
@@ -78,6 +78,7 @@ func (to *TLSObfs) Read(b []byte) (int, error) {
// type + ver = 3
return to.read(b, 3)
}
func (to *TLSObfs) Write(b []byte) (int, error) {
length := len(b)
for i := 0; i < length; i += chunkSize {
@@ -102,7 +103,8 @@ func (to *TLSObfs) write(b []byte) (int, error) {
return len(b), err
}
buf := &bytes.Buffer{}
buf := pool.GetBuffer()
defer pool.PutBuffer(buf)
buf.Write([]byte{0x17, 0x03, 0x03})
binary.Write(buf, binary.BigEndian, uint16(len(b)))
buf.Write(b)
-89
View File
@@ -1,89 +0,0 @@
package constant
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"strconv"
"github.com/xjasonlyu/tun2socks/transport/socks5"
)
const (
TCP Network = iota
UDP
)
type Network uint8
func (n Network) String() string {
switch n {
case TCP:
return "tcp"
case UDP:
return "udp"
default:
return fmt.Sprintf("network(%d)", n)
}
}
func (n Network) MarshalText() ([]byte, error) {
return []byte(n.String()), nil
}
// Metadata implements the net.Addr interface.
type Metadata struct {
Net Network `json:"network"`
SrcIP net.IP `json:"sourceIP"`
MidIP net.IP `json:"dialerIP"`
DstIP net.IP `json:"destinationIP"`
SrcPort uint16 `json:"sourcePort"`
MidPort uint16 `json:"dialerPort"`
DstPort uint16 `json:"destinationPort"`
}
func (m *Metadata) DestinationAddress() string {
return net.JoinHostPort(m.DstIP.String(), strconv.FormatUint(uint64(m.DstPort), 10))
}
func (m *Metadata) SourceAddress() string {
return net.JoinHostPort(m.SrcIP.String(), strconv.FormatUint(uint64(m.SrcPort), 10))
}
func (m *Metadata) UDPAddr() *net.UDPAddr {
if m.Net != UDP || m.DstIP == nil {
return nil
}
return &net.UDPAddr{
IP: m.DstIP,
Port: int(m.DstPort),
}
}
func (m *Metadata) SerializesSocksAddr() socks5.Addr {
var (
buf [][]byte
port [2]byte
)
binary.BigEndian.PutUint16(port[:], m.DstPort)
if m.DstIP.To4() != nil /* IPv4 */ {
aType := socks5.AtypIPv4
buf = [][]byte{{aType}, m.DstIP.To4(), port[:]}
} else /* IPv6 */ {
aType := socks5.AtypIPv6
buf = [][]byte{{aType}, m.DstIP.To16(), port[:]}
}
return bytes.Join(buf, nil)
}
func (m *Metadata) Network() string {
return m.Net.String()
}
// String returns destination address of this metadata.
// Also for implementing net.Addr interface.
func (m *Metadata) String() string {
return m.DestinationAddress()
}
-10
View File
@@ -1,10 +0,0 @@
package constant
const (
Name = "tun2socks"
)
var (
Version string
GitCommit string
)
-34
View File
@@ -1,34 +0,0 @@
package core
import (
"net"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
type TCPConn interface {
net.Conn
ID() *stack.TransportEndpointID
}
type UDPPacket interface {
// Data get the payload of UDP Packet.
Data() []byte
// Drop call after packet is used, could release resources in this function.
Drop()
// ID returns the transport endpoint id of packet.
ID() *stack.TransportEndpointID
// LocalAddr returns the source IP/Port of packet.
LocalAddr() net.Addr
// RemoteAddr returns the destination IP/Port of packet.
RemoteAddr() net.Addr
// WriteBack writes the payload with source IP/Port equals addr
// - variable source IP/Port is important to STUN
// - if addr is not provided, WriteBack will write out UDP packet with SourceIP/Port equals to original Target.
WriteBack([]byte, net.Addr) (int, error)
}
+24
View File
@@ -0,0 +1,24 @@
package adapter
import (
"net"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
// TCPConn implements the net.Conn interface.
type TCPConn interface {
net.Conn
// ID returns the transport endpoint id of TCPConn.
ID() *stack.TransportEndpointID
}
// UDPConn implements net.Conn and net.PacketConn.
type UDPConn interface {
net.Conn
net.PacketConn
// ID returns the transport endpoint id of UDPConn.
ID() *stack.TransportEndpointID
}
+8
View File
@@ -0,0 +1,8 @@
package adapter
// TransportHandler is a TCP/UDP connection handler that implements
// HandleTCP and HandleUDP methods.
type TransportHandler interface {
HandleTCP(TCPConn)
HandleUDP(UDPConn)
}
Executable → Regular
View File
+5
View File
@@ -0,0 +1,5 @@
package fdbased
const Driver = "fd"
const defaultMTU = 1500
+45
View File
@@ -0,0 +1,45 @@
//go:build !windows
package fdbased
import (
"fmt"
"strconv"
"github.com/xjasonlyu/tun2socks/v2/core/device"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
type FD struct {
stack.LinkEndpoint
fd int
mtu uint32
}
func Open(name string, mtu uint32) (device.Device, error) {
fd, err := strconv.Atoi(name)
if err != nil {
return nil, fmt.Errorf("cannot open fd: %s", name)
}
if mtu == 0 {
mtu = defaultMTU
}
return open(fd, mtu)
}
func (f *FD) Type() string {
return Driver
}
func (f *FD) Name() string {
return strconv.Itoa(f.fd)
}
func (f *FD) Close() error {
return unix.Close(f.fd)
}
var _ device.Device = (*FD)(nil)
+11
View File
@@ -0,0 +1,11 @@
package fdbased
import (
"errors"
"github.com/xjasonlyu/tun2socks/v2/core/device"
)
func Open(name string, mtu uint32) (device.Device, error) {
return nil, errors.New("not supported")
}
+26
View File
@@ -0,0 +1,26 @@
package fdbased
import (
"fmt"
"github.com/xjasonlyu/tun2socks/v2/core/device"
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
)
func open(fd int, mtu uint32) (device.Device, error) {
f := &FD{fd: fd, mtu: mtu}
ep, err := fdbased.New(&fdbased.Options{
FDs: []int{fd},
MTU: mtu,
// TUN only, ignore ethernet header.
EthernetHeader: false,
})
if err != nil {
return nil, fmt.Errorf("create endpoint: %w", err)
}
f.LinkEndpoint = ep
return f, nil
}
+23
View File
@@ -0,0 +1,23 @@
//go:build !linux && !windows
package fdbased
import (
"fmt"
"os"
"github.com/xjasonlyu/tun2socks/v2/core/device"
"github.com/xjasonlyu/tun2socks/v2/core/device/iobased"
)
func open(fd int, mtu uint32) (device.Device, error) {
f := &FD{fd: fd, mtu: mtu}
ep, err := iobased.New(os.NewFile(uintptr(fd), f.Name()), mtu, 0)
if err != nil {
return nil, fmt.Errorf("create endpoint: %w", err)
}
f.LinkEndpoint = ep
return f, nil
}
+155
View File
@@ -0,0 +1,155 @@
// Package iobased provides the implementation of io.ReadWriter
// based data-link layer endpoints.
package iobased
import (
"context"
"errors"
"io"
"sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
// Queue length for outbound packet, arriving for read. Overflow
// causes packet drops.
defaultOutQueueLen = 1 << 10
)
// Endpoint implements the interface of stack.LinkEndpoint from io.ReadWriter.
type Endpoint struct {
*channel.Endpoint
// rw is the io.ReadWriter for reading and writing packets.
rw io.ReadWriter
// mtu (maximum transmission unit) is the maximum size of a packet.
mtu uint32
// offset can be useful when perform TUN device I/O with TUN_PI enabled.
offset int
// once is used to perform the init action once when attaching.
once sync.Once
// wg keeps track of running goroutines.
wg sync.WaitGroup
}
// New returns stack.LinkEndpoint(.*Endpoint) and error.
func New(rw io.ReadWriter, mtu uint32, offset int) (*Endpoint, error) {
if mtu == 0 {
return nil, errors.New("MTU size is zero")
}
if rw == nil {
return nil, errors.New("RW interface is nil")
}
if offset < 0 {
return nil, errors.New("offset must be non-negative")
}
return &Endpoint{
Endpoint: channel.New(defaultOutQueueLen, mtu, ""),
rw: rw,
mtu: mtu,
offset: offset,
}, nil
}
// Attach launches the goroutine that reads packets from io.Reader and
// dispatches them via the provided dispatcher.
func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
e.Endpoint.Attach(dispatcher)
e.once.Do(func() {
ctx, cancel := context.WithCancel(context.Background())
e.wg.Add(2)
go func() {
e.outboundLoop(ctx)
e.wg.Done()
}()
go func() {
e.dispatchLoop(cancel)
e.wg.Done()
}()
})
}
func (e *Endpoint) Wait() {
e.wg.Wait()
}
// dispatchLoop dispatches packets to upper layer.
func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
// Call cancel() to ensure (*Endpoint).outboundLoop(context.Context) exits
// gracefully after (*Endpoint).dispatchLoop(context.CancelFunc) returns.
defer cancel()
offset, mtu := e.offset, int(e.mtu)
for {
data := make([]byte, offset+mtu)
n, err := e.rw.Read(data)
if err != nil {
break
}
if n == 0 || n > mtu {
continue
}
if !e.IsAttached() {
continue /* unattached, drop packet */
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.View(data[offset : offset+n]).ToVectorisedView(),
})
switch header.IPVersion(data[offset:]) {
case header.IPv4Version:
e.InjectInbound(header.IPv4ProtocolNumber, pkt)
case header.IPv6Version:
e.InjectInbound(header.IPv6ProtocolNumber, pkt)
}
pkt.DecRef()
}
}
// outboundLoop reads outbound packets from channel, and then it calls
// writePacket to send those packets back to lower layer.
func (e *Endpoint) outboundLoop(ctx context.Context) {
for {
pkt := e.ReadContext(ctx)
if pkt == nil {
break
}
e.writePacket(pkt)
}
}
// writePacket writes outbound packets to the io.Writer.
func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
defer pkt.DecRef()
size := pkt.Size()
views := pkt.Views()
if e.offset != 0 {
views = append([]buffer.View{
make(buffer.View, e.offset),
}, views...)
}
vView := buffer.NewVectorisedView(size, views)
if _, err := e.rw.Write(vView.ToView()); err != nil {
return &tcpip.ErrInvalidEndpointState{}
}
return nil
}
-144
View File
@@ -1,144 +0,0 @@
// Package rwbased provides the implementation of io.ReadWriter
// based data-link layer endpoints.
package rwbased
import (
"errors"
"io"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
var _ stack.LinkEndpoint = (*Endpoint)(nil)
// Endpoint implements the interface of stack.LinkEndpoint from io.ReadWriter.
type Endpoint struct {
// rw is the io.ReadWriter for reading and writing packets.
rw io.ReadWriter
// mtu (maximum transmission unit) is the maximum size of a packet.
mtu uint32
dispatcher stack.NetworkDispatcher
}
// New returns stack.LinkEndpoint(.*Endpoint) and error.
func New(rw io.ReadWriter, mtu uint32) (*Endpoint, error) {
if mtu == 0 {
return nil, errors.New("MTU size is zero")
}
if rw == nil {
return nil, errors.New("RW interface is nil")
}
return &Endpoint{
rw: rw,
mtu: mtu,
}, nil
}
// Attach launches the goroutine that reads packets from io.ReadWriter and
// dispatches them via the provided dispatcher.
func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
go e.dispatchLoop()
e.dispatcher = dispatcher
}
// IsAttached implements stack.LinkEndpoint.IsAttached.
func (e *Endpoint) IsAttached() bool {
return e.dispatcher != nil
}
// dispatchLoop dispatches packets to upper layer.
func (e *Endpoint) dispatchLoop() {
for {
packet := make([]byte, e.mtu)
n, err := e.rw.Read(packet)
if err != nil {
break
}
if !e.IsAttached() {
continue
}
pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.NewVectorisedView(n, []buffer.View{buffer.NewViewFromBytes(packet)}),
})
switch header.IPVersion(packet) {
case header.IPv4Version:
e.dispatcher.DeliverNetworkPacket("", "", header.IPv4ProtocolNumber, pkb)
case header.IPv6Version:
e.dispatcher.DeliverNetworkPacket("", "", header.IPv6ProtocolNumber, pkb)
}
}
}
func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
vView := buffer.NewVectorisedView(pkt.Size(), pkt.Views())
if _, err := e.rw.Write(vView.ToView()); err != nil {
return &tcpip.ErrInvalidEndpointState{}
}
return nil
}
// WritePacket writes packet back into io.ReadWriter.
func (e *Endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
return e.writePacket(pkt)
}
// WritePackets writes packets back into io.ReadWriter.
func (e *Endpoint) WritePackets(_ stack.RouteInfo, pkts stack.PacketBufferList, _ tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
n := 0
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
if err := e.writePacket(pkt); err != nil {
break
}
n++
}
return n, nil
}
func (e *Endpoint) WriteRawPacket(packetBuffer *stack.PacketBuffer) tcpip.Error {
return &tcpip.ErrNotSupported{}
}
// MTU implements stack.LinkEndpoint.MTU.
func (e *Endpoint) MTU() uint32 {
return e.mtu
}
// Capabilities implements stack.LinkEndpoint.Capabilities.
func (e *Endpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityNone
}
// MaxHeaderLength returns the maximum size of the link layer header. Given it
// doesn't have a header, it just returns 0.
func (*Endpoint) MaxHeaderLength() uint16 {
return 0
}
// LinkAddress returns the link address of this endpoint.
func (*Endpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
// ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType.
func (*Endpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
// AddHeader implements stack.LinkEndpoint.AddHeader.
func (e *Endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
}
// Wait implements stack.LinkEndpoint.Wait.
func (e *Endpoint) Wait() {}
-33
View File
@@ -1,33 +0,0 @@
//go:build darwin || freebsd || openbsd
package tun
import (
"github.com/xjasonlyu/tun2socks/common/pool"
)
const (
offset = 4 /* 4 bytes TUN_PI */
defaultMTU = 1500
)
func (t *TUN) Read(packet []byte) (n int, err error) {
buf := pool.Get(offset + len(packet))
defer pool.Put(buf)
if n, err = t.nt.Read(buf, offset); err != nil {
return
}
copy(packet, buf[offset:offset+n])
return
}
func (t *TUN) Write(packet []byte) (int, error) {
buf := pool.Get(offset + len(packet))
defer pool.Put(buf)
copy(buf[offset:], packet)
return t.nt.Write(buf[:offset+len(packet)], offset)
}
-15
View File
@@ -1,15 +0,0 @@
package tun
const (
offset = 0
defaultMTU = 0 /* auto */
)
func (t *TUN) Read(packet []byte) (int, error) {
return t.nt.Read(packet, offset)
}
func (t *TUN) Write(packet []byte) (int, error) {
return t.nt.Write(packet, offset)
}
-15
View File
@@ -1,15 +0,0 @@
package tun
type Option func(*TUN)
func WithName(name string) Option {
return func(t *TUN) {
t.name = name
}
}
func WithMTU(mtu uint32) Option {
return func(t *TUN) {
t.mtu = mtu
}
}
Executable → Regular
+1 -1
View File
@@ -2,7 +2,7 @@
package tun
import (
"github.com/xjasonlyu/tun2socks/core/device"
"github.com/xjasonlyu/tun2socks/v2/core/device"
)
const Driver = "tun"
+19 -11
View File
@@ -6,7 +6,7 @@ import (
"fmt"
"unsafe"
"github.com/xjasonlyu/tun2socks/core/device"
"github.com/xjasonlyu/tun2socks/v2/core/device"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
@@ -23,12 +23,8 @@ type TUN struct {
name string
}
func Open(opts ...Option) (device.Device, error) {
t := &TUN{}
for _, opt := range opts {
opt(t)
}
func Open(name string, mtu uint32) (device.Device, error) {
t := &TUN{name: name, mtu: mtu}
if len(t.name) >= unix.IFNAMSIZ {
return nil, fmt.Errorf("interface name too long: %s", t.name)
@@ -46,17 +42,28 @@ func Open(opts ...Option) (device.Device, error) {
}
}
mtu, err := rawfile.GetMTU(t.name)
_mtu, err := rawfile.GetMTU(t.name)
if err != nil {
return nil, fmt.Errorf("get mtu: %w", err)
}
t.mtu = mtu
t.mtu = _mtu
ep, err := fdbased.New(&fdbased.Options{
MTU: t.mtu,
FDs: []int{fd},
// TUN only
MTU: t.mtu,
// TUN only, ignore ethernet header.
EthernetHeader: false,
// SYS_READV support only for TUN fd.
PacketDispatchMode: fdbased.Readv,
// TAP/TUN fd's are not sockets and using the WritePackets calls results
// in errors as it always defaults to using SendMMsg which is not supported
// for tap/tun device fds.
//
// This CL changes WritePackets to gracefully degrade to using writev instead
// of sendmmsg if the underlying fd is not a socket.
//
// Fixed: https://github.com/google/gvisor/commit/f33d034fecd7723a1e560ccc62aeeba328454fd0
MaxSyscallHeaderBytes: 0x00,
})
if err != nil {
return nil, fmt.Errorf("create endpoint: %w", err)
@@ -74,6 +81,7 @@ func (t *TUN) Close() error {
return unix.Close(t.fd)
}
// Ref: wireguard tun/tun_linux.go setMTU.
func setMTU(name string, n uint32) error {
// open datagram socket
fd, err := unix.Socket(
Executable → Regular
+26 -14
View File
@@ -5,26 +5,29 @@ package tun
import (
"fmt"
"github.com/xjasonlyu/tun2socks/core/device"
"github.com/xjasonlyu/tun2socks/core/device/rwbased"
"github.com/xjasonlyu/tun2socks/v2/core/device"
"github.com/xjasonlyu/tun2socks/v2/core/device/iobased"
"golang.zx2c4.com/wireguard/tun"
)
type TUN struct {
*rwbased.Endpoint
*iobased.Endpoint
nt *tun.NativeTun
mtu uint32
name string
nt *tun.NativeTun
mtu uint32
name string
offset int
}
func Open(opts ...Option) (device.Device, error) {
t := &TUN{}
func Open(name string, mtu uint32) (_ device.Device, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("open tun: %v", r)
}
}()
for _, opt := range opts {
opt(t)
}
t := &TUN{name: name, mtu: mtu, offset: offset}
forcedMTU := defaultMTU
if t.mtu > 0 {
@@ -37,13 +40,13 @@ func Open(opts ...Option) (device.Device, error) {
}
t.nt = nt.(*tun.NativeTun)
mtu, err := nt.MTU()
tunMTU, err := nt.MTU()
if err != nil {
return nil, fmt.Errorf("get mtu: %w", err)
}
t.mtu = uint32(mtu)
t.mtu = uint32(tunMTU)
ep, err := rwbased.New(t, t.mtu)
ep, err := iobased.New(t, t.mtu, offset)
if err != nil {
return nil, fmt.Errorf("create endpoint: %w", err)
}
@@ -52,11 +55,20 @@ func Open(opts ...Option) (device.Device, error) {
return t, nil
}
func (t *TUN) Read(packet []byte) (int, error) {
return t.nt.Read(packet, t.offset)
}
func (t *TUN) Write(packet []byte) (int, error) {
return t.nt.Write(packet, t.offset)
}
func (t *TUN) Name() string {
name, _ := t.nt.Name()
return name
}
func (t *TUN) Close() error {
defer t.Endpoint.Close()
return t.nt.Close()
}
+8
View File
@@ -0,0 +1,8 @@
//go:build !linux && !windows
package tun
const (
offset = 4 /* 4 bytes TUN_PI */
defaultMTU = 1500
)
+6
View File
@@ -0,0 +1,6 @@
package tun
const (
offset = 0
defaultMTU = 0 /* auto */
)
-6
View File
@@ -1,6 +0,0 @@
package core
type Handler interface {
Add(TCPConn)
AddPacket(UDPPacket)
}
+58
View File
@@ -0,0 +1,58 @@
package core
import (
"fmt"
"github.com/xjasonlyu/tun2socks/v2/core/option"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
// nicPromiscuousModeEnabled is the value used by stack to enable
// or disable NIC's promiscuous mode.
nicPromiscuousModeEnabled = true
// nicSpoofingEnabled is the value used by stack to enable or disable
// NIC's spoofing.
nicSpoofingEnabled = true
)
// withCreatingNIC creates NIC for stack.
func withCreatingNIC(nicID tcpip.NICID, ep stack.LinkEndpoint) option.Option {
return func(s *stack.Stack) error {
if err := s.CreateNICWithOptions(nicID, ep,
stack.NICOptions{
Disabled: false,
// If no queueing discipline was specified
// provide a stub implementation that just
// delegates to the lower link endpoint.
QDisc: nil,
}); err != nil {
return fmt.Errorf("create NIC: %s", err)
}
return nil
}
}
// withPromiscuousMode sets promiscuous mode in the given NICs.
func withPromiscuousMode(nicID tcpip.NICID, v bool) option.Option {
return func(s *stack.Stack) error {
if err := s.SetPromiscuousMode(nicID, v); err != nil {
return fmt.Errorf("set promiscuous mode: %s", err)
}
return nil
}
}
// withSpoofing sets address spoofing in the given NICs, allowing
// endpoints to bind to any address in the NIC.
func withSpoofing(nicID tcpip.NICID, v bool) option.Option {
return func(s *stack.Stack) error {
if err := s.SetSpoofing(nicID, v); err != nil {
return fmt.Errorf("set spoofing: %s", err)
}
return nil
}
}
+93 -34
View File
@@ -1,4 +1,4 @@
package stack
package option
import (
"fmt"
@@ -7,23 +7,18 @@ import (
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
)
const (
// maxBufferSize is the maximum permitted size of a send/receive buffer.
maxBufferSize = 4 << 20 // 4 MiB
// minBufferSize is the smallest size of a receive or send buffer.
minBufferSize = 4 << 10 // 4 KiB
// defaultBufferSize is the default size of the send/recv buffer for
// a transport endpoint.
defaultBufferSize = 212 << 10 // 212 KiB
// defaultTimeToLive specifies the default TTL used by stack.
defaultTimeToLive uint8 = 64
// ipForwardingEnabled is the value used by stack to enable packet
// forwarding between NICs.
ipForwardingEnabled = true
// icmpBurst is the default number of ICMP messages that can be sent in
// a single burst.
icmpBurst = 50
@@ -32,10 +27,6 @@ const (
// by this rate limiter.
icmpLimit rate.Limit = 1000
// ipForwardingEnabled is the value used by stack to enable packet
// forwarding between NICs.
ipForwardingEnabled = true
// tcpCongestionControl is the congestion control algorithm used by
// stack. ccReno is the default option in gVisor stack.
tcpCongestionControlAlgorithm = "reno" // "reno" or "cubic"
@@ -46,18 +37,35 @@ const (
// tcpModerateReceiveBufferEnabled is the value used by stack to
// enable or disable tcp receive buffer auto-tuning option.
tcpModerateReceiveBufferEnabled = true
tcpModerateReceiveBufferEnabled = false
// tcpSACKEnabled is the value used by stack to enable or disable
// tcp selective ACK.
tcpSACKEnabled = true
// tcpRecovery is the loss detection algorithm used by TCP.
tcpRecovery = tcpip.TCPRACKLossDetection
// tcpMinBufferSize is the smallest size of a send/recv buffer.
tcpMinBufferSize = tcp.MinBufferSize
// tcpMaxBufferSize is the maximum permitted size of a send/recv buffer.
tcpMaxBufferSize = tcp.MaxBufferSize
// tcpDefaultBufferSize is the default size of the send buffer for
// a transport endpoint.
tcpDefaultSendBufferSize = stack.DefaultBufferSize
// tcpDefaultReceiveBufferSize is the default size of the receive buffer
// for a transport endpoint.
tcpDefaultReceiveBufferSize = stack.DefaultBufferSize
)
type Option func(*Stack) error
type Option func(*stack.Stack) error
// WithDefault sets all default values for stack.
func WithDefault() Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
opts := []Option{
WithDefaultTTL(defaultTimeToLive),
WithForwarding(ipForwardingEnabled),
@@ -69,8 +77,9 @@ func WithDefault() Option {
// Too large buffers thrash cache, so there is little point
// in too large buffers.
//
// Ref: https://github.com/majek/slirpnetstack/blob/master/stack.go
WithTCPBufferSizeRange(minBufferSize, defaultBufferSize, maxBufferSize),
// Ref: https://github.com/cloudflare/slirpnetstack/blob/master/stack.go
WithTCPSendBufferSizeRange(tcpMinBufferSize, tcpDefaultSendBufferSize, tcpMaxBufferSize),
WithTCPReceiveBufferSizeRange(tcpMinBufferSize, tcpDefaultReceiveBufferSize, tcpMaxBufferSize),
WithTCPCongestionControl(tcpCongestionControlAlgorithm),
WithTCPDelay(tcpDelayEnabled),
@@ -82,6 +91,17 @@ func WithDefault() Option {
// TCP selective ACK Option, see:
// https://tools.ietf.org/html/rfc2018
WithTCPSACKEnabled(tcpSACKEnabled),
// TCPRACKLossDetection: indicates RACK is used for loss detection and
// recovery.
//
// TCPRACKStaticReoWnd: indicates the reordering window should not be
// adjusted when DSACK is received.
//
// TCPRACKNoDupTh: indicates RACK should not consider the classic three
// duplicate acknowledgements rule to mark the segments as lost. This
// is used when reordering is not detected.
WithTCPRecovery(tcpRecovery),
}
for _, opt := range opts {
@@ -96,7 +116,7 @@ func WithDefault() Option {
// WithDefaultTTL sets the default TTL used by stack.
func WithDefaultTTL(ttl uint8) Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
opt := tcpip.DefaultTTLOption(ttl)
if err := s.SetNetworkProtocolOption(ipv4.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set ipv4 default TTL: %s", err)
@@ -110,7 +130,7 @@ func WithDefaultTTL(ttl uint8) Option {
// WithForwarding sets packet forwarding between NICs for IPv4 & IPv6.
func WithForwarding(v bool) Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
if err := s.SetForwardingDefaultAndAllNICs(ipv4.ProtocolNumber, v); err != nil {
return fmt.Errorf("set ipv4 forwarding: %s", err)
}
@@ -124,7 +144,7 @@ func WithForwarding(v bool) Option {
// WithICMPBurst sets the number of ICMP messages that can be sent
// in a single burst.
func WithICMPBurst(burst int) Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
s.SetICMPBurst(burst)
return nil
}
@@ -133,19 +153,26 @@ func WithICMPBurst(burst int) Option {
// WithICMPLimit sets the maximum number of ICMP messages permitted
// by rate limiter.
func WithICMPLimit(limit rate.Limit) Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
s.SetICMPLimit(limit)
return nil
}
}
// WithTCPBufferSizeRange sets the receive and send buffer size range for TCP.
func WithTCPBufferSizeRange(a, b, c int) Option {
return func(s *Stack) error {
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
return fmt.Errorf("set TCP receive buffer size range: %s", err)
// WithTCPSendBufferSize sets default the send buffer size for TCP.
func WithTCPSendBufferSize(size int) Option {
return func(s *stack.Stack) error {
sndOpt := tcpip.TCPSendBufferSizeRangeOption{Min: tcpMinBufferSize, Default: size, Max: tcpMaxBufferSize}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sndOpt); err != nil {
return fmt.Errorf("set TCP send buffer size range: %s", err)
}
return nil
}
}
// WithTCPSendBufferSizeRange sets the send buffer size range for TCP.
func WithTCPSendBufferSizeRange(a, b, c int) Option {
return func(s *stack.Stack) error {
sndOpt := tcpip.TCPSendBufferSizeRangeOption{Min: a, Default: b, Max: c}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sndOpt); err != nil {
return fmt.Errorf("set TCP send buffer size range: %s", err)
@@ -154,9 +181,31 @@ func WithTCPBufferSizeRange(a, b, c int) Option {
}
}
// WithTCPReceiveBufferSize sets the default receive buffer size for TCP.
func WithTCPReceiveBufferSize(size int) Option {
return func(s *stack.Stack) error {
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: tcpMinBufferSize, Default: size, Max: tcpMaxBufferSize}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
return fmt.Errorf("set TCP receive buffer size range: %s", err)
}
return nil
}
}
// WithTCPReceiveBufferSizeRange sets the receive buffer size range for TCP.
func WithTCPReceiveBufferSizeRange(a, b, c int) Option {
return func(s *stack.Stack) error {
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
return fmt.Errorf("set TCP receive buffer size range: %s", err)
}
return nil
}
}
// WithTCPCongestionControl sets the current congestion control algorithm.
func WithTCPCongestionControl(cc string) Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
opt := tcpip.CongestionControlOption(cc)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set TCP congestion control algorithm: %s", err)
@@ -167,7 +216,7 @@ func WithTCPCongestionControl(cc string) Option {
// WithTCPDelay enables or disables Nagle's algorithm in TCP.
func WithTCPDelay(v bool) Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
opt := tcpip.TCPDelayEnabled(v)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set TCP delay: %s", err)
@@ -178,7 +227,7 @@ func WithTCPDelay(v bool) Option {
// WithTCPModerateReceiveBuffer sets receive buffer moderation for TCP.
func WithTCPModerateReceiveBuffer(v bool) Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
opt := tcpip.TCPModerateReceiveBufferOption(v)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set TCP moderate receive buffer: %s", err)
@@ -189,7 +238,7 @@ func WithTCPModerateReceiveBuffer(v bool) Option {
// WithTCPSACKEnabled sets the SACK option for TCP.
func WithTCPSACKEnabled(v bool) Option {
return func(s *Stack) error {
return func(s *stack.Stack) error {
opt := tcpip.TCPSACKEnabled(v)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set TCP SACK: %s", err)
@@ -197,3 +246,13 @@ func WithTCPSACKEnabled(v bool) Option {
return nil
}
}
// WithTCPRecovery sets the recovery option for TCP.
func WithTCPRecovery(v tcpip.TCPRecovery) Option {
return func(s *stack.Stack) error {
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &v); err != nil {
return fmt.Errorf("set TCP Recovery: %s", err)
}
return nil
}
}
+8 -7
View File
@@ -1,22 +1,23 @@
package stack
package core
import (
"github.com/xjasonlyu/tun2socks/v2/core/option"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
func withICMPHandler() Option {
return func(s *Stack) error {
// Add default route table for IPv4 and IPv6.
// This will handle all incoming ICMP packets.
func withRouteTable(nicID tcpip.NICID) option.Option {
return func(s *stack.Stack) error {
s.SetRouteTable([]tcpip.Route{
{
Destination: header.IPv4EmptySubnet,
NIC: s.nicID,
NIC: nicID,
},
{
Destination: header.IPv6EmptySubnet,
NIC: s.nicID,
NIC: nicID,
},
})
return nil
+107
View File
@@ -0,0 +1,107 @@
package core
import (
"github.com/xjasonlyu/tun2socks/v2/core/adapter"
"github.com/xjasonlyu/tun2socks/v2/core/option"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
// Config is the configuration to create *stack.Stack.
type Config struct {
// LinkEndpoints is the interface implemented by
// data link layer protocols.
LinkEndpoint stack.LinkEndpoint
// TransportHandler is the handler used by internal
// stack to set transport handlers.
TransportHandler adapter.TransportHandler
// PrintFunc is the function that will be called
// to print internal stack events.
PrintFunc func(string, ...any)
// Options are supplement options to apply settings
// for the internal stack.
Options []option.Option
}
// CreateStack creates *stack.Stack with given config.
func CreateStack(cfg *Config) (*stack.Stack, error) {
if cfg.PrintFunc == nil {
cfg.PrintFunc = func(string, ...any) {}
}
opts := []option.Option{option.WithDefault()}
if len(opts) > 0 {
opts = append(opts, cfg.Options...)
}
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{
ipv4.NewProtocol,
ipv6.NewProtocol,
},
TransportProtocols: []stack.TransportProtocolFactory{
tcp.NewProtocol,
udp.NewProtocol,
icmp.NewProtocol4,
icmp.NewProtocol6,
},
})
// Generate unique NIC id.
nicID := tcpip.NICID(s.UniqueID())
opts = append(opts,
// Important: We must initiate transport protocol handlers
// before creating NIC, otherwise NIC would dispatch packets
// to stack and cause race condition.
// Initiate transport protocol (TCP/UDP) with given handler.
withTCPHandler(cfg.TransportHandler.HandleTCP, cfg.PrintFunc),
withUDPHandler(cfg.TransportHandler.HandleUDP, cfg.PrintFunc),
// Create stack NIC and then bind link endpoint to it.
withCreatingNIC(nicID, cfg.LinkEndpoint),
// In the past we did s.AddAddressRange to assign 0.0.0.0/0
// onto the interface. We need that to be able to terminate
// all the incoming connections - to any ip. AddressRange API
// has been removed and the suggested workaround is to use
// Promiscuous mode. https://github.com/google/gvisor/issues/3876
//
// Ref: https://github.com/cloudflare/slirpnetstack/blob/master/stack.go
withPromiscuousMode(nicID, nicPromiscuousModeEnabled),
// Enable spoofing if a stack may send packets from unowned
// addresses. This change required changes to some netgophers
// since previously, promiscuous mode was enough to let the
// netstack respond to all incoming packets regardless of the
// packet's destination address. Now that a stack.Route is not
// held for each incoming packet, finding a route may fail with
// local addresses we don't own but accepted packets for while
// in promiscuous mode. Since we also want to be able to send
// from any address (in response the received promiscuous mode
// packets), we need to enable spoofing.
//
// Ref: https://github.com/google/gvisor/commit/8c0701462a84ff77e602f1626aec49479c308127
withSpoofing(nicID, nicSpoofingEnabled),
// Add default route table for IPv4 and IPv6. This will handle
// all incoming ICMP packets.
withRouteTable(nicID),
)
for _, opt := range opts {
if err := opt(s); err != nil {
return nil, err
}
}
return s, nil
}
-52
View File
@@ -1,52 +0,0 @@
package stack
import (
"fmt"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
// defaultNICID is the ID of default NIC used by DefaultStack.
defaultNICID tcpip.NICID = 0x01
// nicPromiscuousModeEnabled is the value used by stack to enable
// or disable NIC's promiscuous mode.
nicPromiscuousModeEnabled = true
// nicSpoofingEnabled is the value used by stack to enable or disable
// NIC's spoofing.
nicSpoofingEnabled = true
)
// withCreatingNIC creates NIC for stack.
func withCreatingNIC(ep stack.LinkEndpoint) Option {
return func(s *Stack) error {
if err := s.CreateNIC(s.nicID, ep); err != nil {
return fmt.Errorf("create NIC: %s", err)
}
return nil
}
}
// withPromiscuousMode sets promiscuous mode in the given NIC.
func withPromiscuousMode(v bool) Option {
return func(s *Stack) error {
if err := s.SetPromiscuousMode(s.nicID, v); err != nil {
return fmt.Errorf("set promiscuous mode: %s", err)
}
return nil
}
}
// withSpoofing sets address spoofing in the given NIC, allowing
// endpoints to bind to any address in the NIC.
func withSpoofing(v bool) Option {
return func(s *Stack) error {
if err := s.SetSpoofing(s.nicID, v); err != nil {
return fmt.Errorf("set spoofing: %s", err)
}
return nil
}
}
-82
View File
@@ -1,82 +0,0 @@
// Package stack provides a thin wrapper around a gVisor's stack.
package stack
import (
"github.com/xjasonlyu/tun2socks/core"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
type Stack struct {
*stack.Stack
handler core.Handler
nicID tcpip.NICID
}
// New allocates a new *Stack with given options.
func New(ep stack.LinkEndpoint, handler core.Handler, opts ...Option) (*Stack, error) {
s := &Stack{
Stack: stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{
ipv4.NewProtocol,
ipv6.NewProtocol,
},
TransportProtocols: []stack.TransportProtocolFactory{
tcp.NewProtocol,
udp.NewProtocol,
icmp.NewProtocol4,
icmp.NewProtocol6,
},
}),
handler: handler,
nicID: defaultNICID,
}
opts = append(opts,
// Important: We must initiate transport protocol handlers
// before creating NIC, otherwise NIC would dispatch packets
// to stack and cause race condition.
withICMPHandler(), withTCPHandler(), withUDPHandler(),
// Create stack NIC and then bind link endpoint.
withCreatingNIC(ep),
// In past we did s.AddAddressRange to assign 0.0.0.0/0 onto
// the interface. We need that to be able to terminate all the
// incoming connections - to any ip. AddressRange API has been
// removed and the suggested workaround is to use Promiscuous
// mode. https://github.com/google/gvisor/issues/3876
//
// Ref: https://github.com/majek/slirpnetstack/blob/master/stack.go
withPromiscuousMode(nicPromiscuousModeEnabled),
// Enable spoofing if a stack may send packets from unowned addresses.
// This change required changes to some netgophers since previously,
// promiscuous mode was enough to let the netstack respond to all
// incoming packets regardless of the packet's destination address. Now
// that a stack.Route is not held for each incoming packet, finding a route
// may fail with local addresses we don't own but accepted packets for
// while in promiscuous mode. Since we also want to be able to send from
// any address (in response the received promiscuous mode packets), we need
// to enable spoofing.
//
// Ref: https://github.com/google/gvisor/commit/8c0701462a84ff77e602f1626aec49479c308127
withSpoofing(nicSpoofingEnabled),
)
for _, opt := range opts {
if err := opt(s); err != nil {
return nil, err
}
}
return s, nil
}
-83
View File
@@ -1,83 +0,0 @@
package stack
import (
"fmt"
"net"
"time"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter"
)
const (
// defaultWndSize if set to zero, the default
// receive window buffer size is used instead.
defaultWndSize = 0
// maxConnAttempts specifies the maximum number
// of in-flight tcp connection attempts.
maxConnAttempts = 2 << 10
// tcpKeepaliveIdle specifies the time a connection
// must remain idle before the first TCP keepalive
// packet is sent. Once this time is reached,
// tcpKeepaliveInterval option is used instead.
tcpKeepaliveIdle = 60 * time.Second
// tcpKeepaliveInterval specifies the interval
// time between sending TCP keepalive packets.
tcpKeepaliveInterval = 30 * time.Second
)
func withTCPHandler() Option {
return func(s *Stack) error {
tcpForwarder := tcp.NewForwarder(s.Stack, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
var wq waiter.Queue
id := r.ID()
ep, err := r.CreateEndpoint(&wq)
if err != nil {
// prevent potential half-open TCP connection leak.
r.Complete(true)
return
}
r.Complete(false)
setKeepalive(ep)
conn := &tcpConn{
Conn: gonet.NewTCPConn(&wq, ep),
id: &id,
}
s.handler.Add(conn)
})
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
return nil
}
}
func setKeepalive(ep tcpip.Endpoint) error {
ep.SocketOptions().SetKeepAlive(true)
idle := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle)
if err := ep.SetSockOpt(&idle); err != nil {
return fmt.Errorf("set keepalive idle: %s", err)
}
interval := tcpip.KeepaliveIntervalOption(tcpKeepaliveInterval)
if err := ep.SetSockOpt(&interval); err != nil {
return fmt.Errorf("set keepalive interval: %s", err)
}
return nil
}
type tcpConn struct {
net.Conn
id *stack.TransportEndpointID
}
func (c *tcpConn) ID() *stack.TransportEndpointID {
return c.id
}
-181
View File
@@ -1,181 +0,0 @@
package stack
import (
"fmt"
"net"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
const (
// udpNoChecksum disables UDP checksum.
udpNoChecksum = true
)
func withUDPHandler() Option {
return func(s *Stack) error {
udpHandlePacket := func(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
// Ref: gVisor pkg/tcpip/transport/udp/endpoint.go HandlePacket
udpHdr := header.UDP(pkt.TransportHeader().View())
if int(udpHdr.Length()) > pkt.Data().Size()+header.UDPMinimumSize {
// Malformed packet.
s.Stats().UDP.MalformedPacketsReceived.Increment()
return true
}
if !verifyChecksum(udpHdr, pkt) {
// Checksum error.
s.Stats().UDP.ChecksumErrors.Increment()
return true
}
s.Stats().UDP.PacketsReceived.Increment()
packet := &udpPacket{
s: s,
id: &id,
data: pkt.Data().ExtractVV(),
nicID: pkt.NICID,
netHdr: pkt.Network(),
netProto: pkt.NetworkProtocolNumber,
}
s.handler.AddPacket(packet)
return true
}
s.SetTransportProtocolHandler(udp.ProtocolNumber, udpHandlePacket)
return nil
}
}
type udpPacket struct {
s *Stack
id *stack.TransportEndpointID
data buffer.VectorisedView
nicID tcpip.NICID
netHdr header.Network
netProto tcpip.NetworkProtocolNumber
}
func (p *udpPacket) Data() []byte {
return p.data.ToView()
}
func (p *udpPacket) Drop() {}
func (p *udpPacket) ID() *stack.TransportEndpointID {
return p.id
}
func (p *udpPacket) LocalAddr() net.Addr {
return &net.UDPAddr{IP: net.IP(p.id.LocalAddress), Port: int(p.id.LocalPort)}
}
func (p *udpPacket) RemoteAddr() net.Addr {
return &net.UDPAddr{IP: net.IP(p.id.RemoteAddress), Port: int(p.id.RemotePort)}
}
func (p *udpPacket) WriteBack(b []byte, addr net.Addr) (int, error) {
v := buffer.View(b)
if len(v) > header.UDPMaximumPacketSize {
// Payload can't possibly fit in a packet.
return 0, fmt.Errorf("%s", &tcpip.ErrMessageTooLong{})
}
var (
localAddress tcpip.Address
localPort uint16
)
if udpAddr, ok := addr.(*net.UDPAddr); !ok {
localAddress = p.netHdr.DestinationAddress()
localPort = p.id.LocalPort
} else if ipv4 := udpAddr.IP.To4(); ipv4 != nil {
localAddress = tcpip.Address(ipv4)
localPort = uint16(udpAddr.Port)
} else {
localAddress = tcpip.Address(udpAddr.IP)
localPort = uint16(udpAddr.Port)
}
route, err := p.s.FindRoute(p.nicID, localAddress, p.netHdr.SourceAddress(), p.netProto, false /* multicastLoop */)
if err != nil {
return 0, fmt.Errorf("%#v find route: %s", p.id, err)
}
defer route.Release()
data := v.ToVectorisedView()
if err = sendUDP(route, data, localPort, p.id.RemotePort, udpNoChecksum); err != nil {
return 0, fmt.Errorf("%v", err)
}
return data.Size(), nil
}
// sendUDP sends a UDP segment via the provided network endpoint and under the
// provided identity.
func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, noChecksum bool) tcpip.Error {
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()),
Data: data,
})
// Initialize the UDP header.
udpHdr := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize))
pkt.TransportProtocolNumber = udp.ProtocolNumber
length := uint16(pkt.Size())
udpHdr.Encode(&header.UDPFields{
SrcPort: localPort,
DstPort: remotePort,
Length: length,
})
// Set the checksum field unless TX checksum offload is enabled.
// On IPv4, UDP checksum is optional, and a zero value indicates the
// transmitter skipped the checksum generation (RFC768).
// On IPv6, UDP checksum is not optional (RFC2460 Section 8.1).
if r.RequiresTXTransportChecksum() &&
(!noChecksum || r.NetProto() == header.IPv6ProtocolNumber) {
xsum := r.PseudoHeaderChecksum(udp.ProtocolNumber, length)
for _, v := range data.Views() {
xsum = header.Checksum(v, xsum)
}
udpHdr.SetChecksum(^udpHdr.CalculateChecksum(xsum))
}
ttl := r.DefaultTTL()
if err := r.WritePacket(stack.NetworkHeaderParams{
Protocol: udp.ProtocolNumber,
TTL: ttl,
TOS: 0, /* default */
}, pkt); err != nil {
r.Stats().UDP.PacketSendErrors.Increment()
return err
}
// Track count of packets sent.
r.Stats().UDP.PacketsSent.Increment()
return nil
}
// verifyChecksum verifies the checksum unless RX checksum offload is enabled.
// On IPv4, UDP checksum is optional, and a zero value means the transmitter
// omitted the checksum generation (RFC768).
// On IPv6, UDP checksum is not optional (RFC2460 Section 8.1).
func verifyChecksum(hdr header.UDP, pkt *stack.PacketBuffer) bool {
if !pkt.RXTransportChecksumValidated &&
(hdr.Checksum() != 0 || pkt.NetworkProtocolNumber == header.IPv6ProtocolNumber) {
netHdr := pkt.Network()
xsum := header.PseudoHeaderChecksum(udp.ProtocolNumber, netHdr.DestinationAddress(), netHdr.SourceAddress(), hdr.Length())
for _, v := range pkt.Data().Views() {
xsum = header.Checksum(v, xsum)
}
return hdr.CalculateChecksum(xsum) == 0xffff
}
return true
}
+119
View File
@@ -0,0 +1,119 @@
package core
import (
"time"
"github.com/xjasonlyu/tun2socks/v2/core/adapter"
"github.com/xjasonlyu/tun2socks/v2/core/option"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter"
)
const (
// defaultWndSize if set to zero, the default
// receive window buffer size is used instead.
defaultWndSize = 0
// maxConnAttempts specifies the maximum number
// of in-flight tcp connection attempts.
maxConnAttempts = 2 << 10
// tcpKeepaliveCount is the maximum number of
// TCP keep-alive probes to send before giving up
// and killing the connection if no response is
// obtained from the other end.
tcpKeepaliveCount = 9
// tcpKeepaliveIdle specifies the time a connection
// must remain idle before the first TCP keepalive
// packet is sent. Once this time is reached,
// tcpKeepaliveInterval option is used instead.
tcpKeepaliveIdle = 60 * time.Second
// tcpKeepaliveInterval specifies the interval
// time between sending TCP keepalive packets.
tcpKeepaliveInterval = 30 * time.Second
)
func withTCPHandler(handle func(adapter.TCPConn), printf func(string, ...any)) option.Option {
return func(s *stack.Stack) error {
tcpForwarder := tcp.NewForwarder(s, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
var (
wq waiter.Queue
ep tcpip.Endpoint
err tcpip.Error
)
defer func() {
if err != nil {
printf("tcp forwarder request: %s", err)
}
}()
// Perform a TCP three-way handshake.
ep, err = r.CreateEndpoint(&wq)
if err != nil {
// RST: prevent potential half-open TCP connection leak.
r.Complete(true)
return
}
defer r.Complete(false)
err = setSocketOptions(s, ep)
conn := &tcpConn{
TCPConn: gonet.NewTCPConn(&wq, ep),
id: r.ID(),
}
handle(conn)
})
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
return nil
}
}
func setSocketOptions(s *stack.Stack, ep tcpip.Endpoint) tcpip.Error {
{ /* TCP keepalive options */
ep.SocketOptions().SetKeepAlive(true)
idle := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle)
if err := ep.SetSockOpt(&idle); err != nil {
return err
}
interval := tcpip.KeepaliveIntervalOption(tcpKeepaliveInterval)
if err := ep.SetSockOpt(&interval); err != nil {
return err
}
if err := ep.SetSockOptInt(tcpip.KeepaliveCountOption, tcpKeepaliveCount); err != nil {
return err
}
}
{ /* TCP recv/send buffer size */
var ss tcpip.TCPSendBufferSizeRangeOption
if err := s.TransportProtocolOption(header.TCPProtocolNumber, &ss); err == nil {
ep.SocketOptions().SetReceiveBufferSize(int64(ss.Default), false)
}
var rs tcpip.TCPReceiveBufferSizeRangeOption
if err := s.TransportProtocolOption(header.TCPProtocolNumber, &rs); err == nil {
ep.SocketOptions().SetReceiveBufferSize(int64(rs.Default), false)
}
}
return nil
}
type tcpConn struct {
*gonet.TCPConn
id stack.TransportEndpointID
}
func (c *tcpConn) ID() *stack.TransportEndpointID {
return &c.id
}
+41
View File
@@ -0,0 +1,41 @@
package core
import (
"github.com/xjasonlyu/tun2socks/v2/core/adapter"
"github.com/xjasonlyu/tun2socks/v2/core/option"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
"gvisor.dev/gvisor/pkg/waiter"
)
func withUDPHandler(handle func(adapter.UDPConn), printf func(string, ...any)) option.Option {
return func(s *stack.Stack) error {
udpForwarder := udp.NewForwarder(s, func(r *udp.ForwarderRequest) {
var wq waiter.Queue
ep, err := r.CreateEndpoint(&wq)
if err != nil {
printf("udp forwarder request: %s", err)
return
}
conn := &udpConn{
UDPConn: gonet.NewUDPConn(s, &wq, ep),
id: r.ID(),
}
handle(conn)
})
s.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
return nil
}
}
type udpConn struct {
*gonet.UDPConn
id stack.TransportEndpointID
}
func (c *udpConn) ID() *stack.TransportEndpointID {
return &c.id
}
+7 -3
View File
@@ -1,10 +1,14 @@
package dialer
package dns
import "net"
import (
"net"
"github.com/xjasonlyu/tun2socks/v2/component/dialer"
)
func init() {
// We must use this DialContext to query DNS
// when using net default resolver.
net.DefaultResolver.PreferGo = true
net.DefaultResolver.Dial = DialContext
net.DefaultResolver.Dial = dialer.DialContext
}
+22 -14
View File
@@ -47,7 +47,7 @@ config_route() {
done
}
main() {
run() {
create_tun
create_table
config_route
@@ -58,27 +58,35 @@ main() {
fi
if [ -n "$MTU" ]; then
ARGS="-mtu $MTU"
ARGS="--mtu $MTU"
fi
if [ -n "$STATS" ]; then
ARGS="$ARGS -stats $STATS"
fi
if [ -n "$TOKEN" ]; then
ARGS="$ARGS -token $TOKEN"
if [ -n "$RESTAPI" ]; then
ARGS="$ARGS --restapi $RESTAPI"
fi
if [ -n "$UDP_TIMEOUT" ]; then
ARGS="$ARGS -udp-timeout $UDP_TIMEOUT"
ARGS="$ARGS --udp-timeout $UDP_TIMEOUT"
fi
if [ -n "$TCP_SNDBUF" ]; then
ARGS="$ARGS --tcp-sndbuf $TCP_SNDBUF"
fi
if [ -n "$TCP_RCVBUF" ]; then
ARGS="$ARGS --tcp-rcvbuf $TCP_RCVBUF"
fi
if [ "$TCP_AUTO_TUNING" -eq 1 ]; then
ARGS="$ARGS --tcp-auto-tuning"
fi
exec tun2socks \
-loglevel "$LOGLEVEL" \
-fwmark "$FWMARK" \
-device "$TUN" \
-proxy "$PROXY" \
--loglevel "$LOGLEVEL" \
--fwmark "$FWMARK" \
--device "$TUN" \
--proxy "$PROXY" \
$ARGS
}
main || exit 1
run || exit 1
Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 84 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Executable → Regular
+152 -111
View File
@@ -2,167 +2,208 @@ package engine
import (
"errors"
"os"
"fmt"
"net"
"sync"
"time"
"github.com/xjasonlyu/tun2socks/component/dialer"
"github.com/xjasonlyu/tun2socks/core/device"
"github.com/xjasonlyu/tun2socks/core/stack"
"github.com/xjasonlyu/tun2socks/log"
"github.com/xjasonlyu/tun2socks/proxy"
"github.com/xjasonlyu/tun2socks/stats"
"github.com/xjasonlyu/tun2socks/tunnel"
"github.com/xjasonlyu/tun2socks/v2/component/dialer"
"github.com/xjasonlyu/tun2socks/v2/core"
"github.com/xjasonlyu/tun2socks/v2/core/device"
"github.com/xjasonlyu/tun2socks/v2/core/option"
"github.com/xjasonlyu/tun2socks/v2/engine/mirror"
"github.com/xjasonlyu/tun2socks/v2/log"
"github.com/xjasonlyu/tun2socks/v2/proxy"
"github.com/xjasonlyu/tun2socks/v2/restapi"
"github.com/xjasonlyu/tun2socks/v2/tunnel"
"github.com/docker/go-units"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
var _engine = &engine{}
var (
_engineMu sync.Mutex
// _defaultKey holds the default key for the engine.
_defaultKey *Key
// _defaultProxy holds the default proxy for the engine.
_defaultProxy proxy.Proxy
// _defaultDevice holds the default device for the engine.
_defaultDevice device.Device
// _defaultStack holds the default stack for the engine.
_defaultStack *stack.Stack
)
// Start starts the default engine up.
func Start() error {
return _engine.start()
func Start() {
if err := start(); err != nil {
log.Fatalf("[ENGINE] failed to start: %v", err)
}
}
// Stop shuts the default engine down.
func Stop() error {
return _engine.stop()
func Stop() {
if err := stop(); err != nil {
log.Fatalf("[ENGINE] failed to stop: %v", err)
}
}
// Insert loads *Key to the default engine.
func Insert(k *Key) {
_engine.insert(k)
_engineMu.Lock()
_defaultKey = k
_engineMu.Unlock()
}
type Key struct {
MTU int
Mark int
UDPTimeout int
Proxy string
Stats string
Token string
Device string
LogLevel string
Interface string
Version bool
}
type engine struct {
*Key
stack *stack.Stack
proxy proxy.Proxy
device device.Device
}
func (e *engine) start() error {
if e.Key == nil {
func start() error {
_engineMu.Lock()
if _defaultKey == nil {
return errors.New("empty key")
}
if e.Version {
showVersion()
os.Exit(0)
}
for _, f := range []func() error{
e.setLogLevel,
e.setMark,
e.setInterface,
e.setStats,
e.setUDPTimeout,
e.setProxy,
e.setDevice,
e.setStack,
for _, f := range []func(*Key) error{
general,
restAPI,
netstack,
} {
if err := f(); err != nil {
if err := f(_defaultKey); err != nil {
return err
}
}
_engineMu.Unlock()
return nil
}
func (e *engine) stop() error {
if e.device != nil {
return e.device.Close()
func stop() (err error) {
_engineMu.Lock()
if _defaultDevice != nil {
err = _defaultDevice.Close()
}
return nil
if _defaultStack != nil {
_defaultStack.Close()
_defaultStack.Wait()
}
_engineMu.Unlock()
return err
}
func (e *engine) insert(k *Key) {
e.Key = k
}
func (e *engine) setLogLevel() error {
level, err := log.ParseLevel(e.LogLevel)
func general(k *Key) error {
level, err := log.ParseLevel(k.LogLevel)
if err != nil {
return err
}
log.SetLevel(level)
return nil
}
func (e *engine) setMark() error {
if e.Mark != 0 {
dialer.SetMark(e.Mark)
log.Infof("[DIALER] set fwmark: %#x", e.Mark)
}
return nil
}
func (e *engine) setInterface() error {
if e.Interface != "" {
if err := dialer.BindToInterface(e.Interface); err != nil {
if k.Interface != "" {
iface, err := net.InterfaceByName(k.Interface)
if err != nil {
return err
}
log.Infof("[DIALER] use interface: %s", e.Interface)
dialer.DefaultInterfaceName.Store(iface.Name)
dialer.DefaultInterfaceIndex.Store(int32(iface.Index))
log.Infof("[DIALER] bind to interface: %s", k.Interface)
}
if k.Mark != 0 {
dialer.DefaultRoutingMark.Store(int32(k.Mark))
log.Infof("[DIALER] set fwmark: %#x", k.Mark)
}
if k.UDPTimeout > 0 {
if k.UDPTimeout < time.Second {
return errors.New("invalid udp timeout value")
}
tunnel.SetUDPTimeout(k.UDPTimeout)
}
return nil
}
func (e *engine) setStats() error {
if e.Stats != "" {
func restAPI(k *Key) error {
if k.RestAPI != "" {
u, err := parseRestAPI(k.RestAPI)
if err != nil {
return err
}
host, token := u.Host, u.User.String()
restapi.SetStatsFunc(func() tcpip.Stats {
_engineMu.Lock()
defer _engineMu.Unlock()
// default stack is not initialized.
if _defaultStack == nil {
return tcpip.Stats{}
}
return _defaultStack.Stats()
})
go func() {
_ = stats.Start(e.Stats, e.Token)
if err := restapi.Start(host, token); err != nil {
log.Warnf("[RESTAPI] failed to start: %v", err)
}
}()
log.Infof("[STATS] serve at: http://%s", e.Stats)
log.Infof("[RESTAPI] serve at: %s", u)
}
return nil
}
func (e *engine) setUDPTimeout() error {
if e.UDPTimeout > 0 {
tunnel.SetUDPTimeout(e.UDPTimeout)
}
return nil
}
func (e *engine) setProxy() (err error) {
if e.Proxy == "" {
func netstack(k *Key) (err error) {
if k.Proxy == "" {
return errors.New("empty proxy")
}
e.proxy, err = parseProxy(e.Proxy)
proxy.SetDialer(e.proxy)
return
}
func (e *engine) setDevice() (err error) {
if e.Device == "" {
if k.Device == "" {
return errors.New("empty device")
}
e.device, err = parseDevice(e.Device, uint32(e.MTU))
return
}
if _defaultProxy, err = parseProxy(k.Proxy); err != nil {
return
}
proxy.SetDialer(_defaultProxy)
func (e *engine) setStack() (err error) {
defer func() {
if err == nil {
log.Infof(
"[STACK] %s://%s <-> %s://%s",
e.device.Type(), e.device.Name(),
e.proxy.Proto(), e.proxy.Addr(),
)
if _defaultDevice, err = parseDevice(k.Device, uint32(k.MTU)); err != nil {
return
}
var opts []option.Option
if k.TCPModerateReceiveBuffer {
opts = append(opts, option.WithTCPModerateReceiveBuffer(true))
}
if k.TCPSendBufferSize != "" {
size, err := units.RAMInBytes(k.TCPSendBufferSize)
if err != nil {
return err
}
}()
opts = append(opts, option.WithTCPSendBufferSize(int(size)))
}
e.stack, err = stack.New(e.device, &fakeTunnel{}, stack.WithDefault())
return
if k.TCPReceiveBufferSize != "" {
size, err := units.RAMInBytes(k.TCPReceiveBufferSize)
if err != nil {
return err
}
opts = append(opts, option.WithTCPReceiveBufferSize(int(size)))
}
if _defaultStack, err = core.CreateStack(&core.Config{
LinkEndpoint: _defaultDevice,
TransportHandler: &mirror.Tunnel{},
PrintFunc: func(format string, v ...any) {
log.Warnf("[STACK] %s", fmt.Sprintf(format, v...))
},
Options: opts,
}); err != nil {
return
}
log.Infof(
"[STACK] %s://%s <-> %s://%s",
_defaultDevice.Type(), _defaultDevice.Name(),
_defaultProxy.Proto(), _defaultProxy.Addr(),
)
return nil
}
+17
View File
@@ -0,0 +1,17 @@
package engine
import "time"
type Key struct {
MTU int `yaml:"mtu"`
Mark int `yaml:"fwmark"`
Proxy string `yaml:"proxy"`
RestAPI string `yaml:"restapi"`
Device string `yaml:"device"`
LogLevel string `yaml:"loglevel"`
Interface string `yaml:"interface"`
TCPModerateReceiveBuffer bool `yaml:"tcp-moderate-receive-buffer"`
TCPSendBufferSize string `yaml:"tcp-send-buffer-size"`
TCPReceiveBufferSize string `yaml:"tcp-receive-buffer-size"`
UDPTimeout time.Duration `yaml:"udp-timeout"`
}
+18
View File
@@ -0,0 +1,18 @@
package mirror
import (
"github.com/xjasonlyu/tun2socks/v2/core/adapter"
"github.com/xjasonlyu/tun2socks/v2/tunnel"
)
var _ adapter.TransportHandler = (*Tunnel)(nil)
type Tunnel struct{}
func (*Tunnel) HandleTCP(conn adapter.TCPConn) {
tunnel.TCPIn() <- conn
}
func (*Tunnel) HandleUDP(conn adapter.UDPConn) {
tunnel.UDPIn() <- conn
}
+36 -5
View File
@@ -3,15 +3,44 @@ package engine
import (
"encoding/base64"
"fmt"
"net"
"net/url"
"strings"
"github.com/xjasonlyu/tun2socks/core/device"
"github.com/xjasonlyu/tun2socks/core/device/tun"
"github.com/xjasonlyu/tun2socks/proxy"
"github.com/xjasonlyu/tun2socks/proxy/proto"
"github.com/xjasonlyu/tun2socks/v2/core/device"
"github.com/xjasonlyu/tun2socks/v2/core/device/fdbased"
"github.com/xjasonlyu/tun2socks/v2/core/device/tun"
"github.com/xjasonlyu/tun2socks/v2/proxy"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
)
func parseRestAPI(s string) (*url.URL, error) {
if !strings.Contains(s, "://") {
s = fmt.Sprintf("%s://%s", "http", s)
}
u, err := url.Parse(s)
if err != nil {
return nil, err
}
addr, err := net.ResolveTCPAddr("tcp", u.Host)
if err != nil {
return nil, err
}
if addr.IP == nil {
addr.IP = net.IPv4zero /* default: 0.0.0.0 */
}
u.Host = addr.String()
switch u.Scheme {
case "http":
return u, nil
default:
return nil, fmt.Errorf("unsupported scheme: %s", u.Scheme)
}
}
func parseDevice(s string, mtu uint32) (device.Device, error) {
if !strings.Contains(s, "://") {
s = fmt.Sprintf("%s://%s", tun.Driver /* default driver */, s)
@@ -26,8 +55,10 @@ func parseDevice(s string, mtu uint32) (device.Device, error) {
driver := strings.ToLower(u.Scheme)
switch driver {
case fdbased.Driver:
return fdbased.Open(name, mtu)
case tun.Driver:
return tun.Open(tun.WithName(name), tun.WithMTU(mtu))
return tun.Open(name, mtu)
default:
return nil, fmt.Errorf("unsupported driver: %s", driver)
}
-18
View File
@@ -1,18 +0,0 @@
package engine
import (
"github.com/xjasonlyu/tun2socks/core"
"github.com/xjasonlyu/tun2socks/tunnel"
)
var _ core.Handler = (*fakeTunnel)(nil)
type fakeTunnel struct{}
func (*fakeTunnel) Add(conn core.TCPConn) {
tunnel.Add(conn)
}
func (*fakeTunnel) AddPacket(packet core.UDPPacket) {
tunnel.AddPacket(packet)
}
-22
View File
@@ -1,22 +0,0 @@
package engine
import (
"fmt"
"runtime"
"strings"
V "github.com/xjasonlyu/tun2socks/constant"
)
func showVersion() {
fmt.Print(versionString())
fmt.Print(releaseString())
}
func versionString() string {
return fmt.Sprintf("%s-%s\n", V.Name, strings.TrimPrefix(V.Version, "v"))
}
func releaseString() string {
return fmt.Sprintf("%s/%s, %s, %s\n", runtime.GOOS, runtime.GOARCH, runtime.Version(), V.GitCommit)
}
+15 -12
View File
@@ -1,28 +1,31 @@
module github.com/xjasonlyu/tun2socks
module github.com/xjasonlyu/tun2socks/v2
go 1.17
go 1.18
require (
github.com/Dreamacro/go-shadowsocks2 v0.1.7
github.com/go-chi/chi v4.1.2+incompatible
github.com/docker/go-units v0.4.0
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.0
github.com/go-chi/render v1.0.1
github.com/gofrs/uuid v4.0.0+incompatible
github.com/gorilla/websocket v1.4.2
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.5.0
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
go.uber.org/atomic v1.9.0
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6 // indirect
golang.org/x/sys v0.0.0-20211003122950-b1ebd4e1001c
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
golang.zx2c4.com/wireguard v0.0.0-20210927201915-bb745b2ea326
gvisor.dev/gvisor v0.0.0-20211002000755-82b90506164f
go.uber.org/automaxprocs v1.4.0
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gvisor.dev/gvisor v0.0.0-20220401222657-54142f50892f
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
)
+27 -962
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
//go:build debug
package version
func init() {
_debug = true
}
+11
View File
@@ -0,0 +1,11 @@
package version
import (
"runtime/debug"
)
// Info returns project dependencies as []*debug.Module.
func Info() []*debug.Module {
bi, _ := debug.ReadBuildInfo()
return bi.Deps
}
+40
View File
@@ -0,0 +1,40 @@
package version
import (
"fmt"
"runtime"
"strings"
)
const Name = "tun2socks"
var (
_debug = false
// Version can be set at link time by executing
// the command: `git describe --abbrev=0 --tags HEAD`
Version string
// GitCommit can be set at link time by executing
// the command: `git rev-parse --short HEAD`
GitCommit string
)
func versionize(s string) string {
return strings.TrimPrefix(s, "v")
}
func Debug() bool {
return _debug
}
func String() string {
if !Debug() {
return fmt.Sprintf("%s-%s", Name, versionize(Version))
}
return fmt.Sprintf("%s-%s (debug)", Name, versionize(Version))
}
func BuildString() string {
return fmt.Sprintf("%s/%s, %s, %s", runtime.GOOS, runtime.GOARCH, runtime.Version(), GitCommit)
}
Executable → Regular
+3 -3
View File
@@ -4,11 +4,11 @@ import (
"fmt"
"time"
"github.com/xjasonlyu/tun2socks/common/observable"
"github.com/xjasonlyu/tun2socks/v2/common/observable"
)
var (
_logCh = make(chan interface{})
_logCh = make(chan any)
_source = observable.NewObservable(_logCh)
)
@@ -18,7 +18,7 @@ type Event struct {
Time time.Time `json:"time"`
}
func newEvent(level Level, format string, args ...interface{}) *Event {
func newEvent(level Level, format string, args ...any) *Event {
event := &Event{
Level: level,
Time: time.Now(),
Executable → Regular
+1 -1
View File
@@ -60,7 +60,7 @@ func ParseLevel(lvl string) (Level, error) {
return SilentLevel, nil
case "error":
return ErrorLevel, nil
case "warn":
case "warning":
return WarnLevel, nil
case "info":
return InfoLevel, nil
Executable → Regular
+8 -10
View File
@@ -7,10 +7,8 @@ import (
"go.uber.org/atomic"
)
var (
// _defaultLevel is package default logging level.
_defaultLevel = atomic.NewUint32(uint32(InfoLevel))
)
// _defaultLevel is package default logging level.
var _defaultLevel = atomic.NewUint32(uint32(InfoLevel))
func init() {
logrus.SetOutput(os.Stdout)
@@ -21,27 +19,27 @@ func SetLevel(level Level) {
_defaultLevel.Store(uint32(level))
}
func Debugf(format string, args ...interface{}) {
func Debugf(format string, args ...any) {
logf(DebugLevel, format, args...)
}
func Infof(format string, args ...interface{}) {
func Infof(format string, args ...any) {
logf(InfoLevel, format, args...)
}
func Warnf(format string, args ...interface{}) {
func Warnf(format string, args ...any) {
logf(WarnLevel, format, args...)
}
func Errorf(format string, args ...interface{}) {
func Errorf(format string, args ...any) {
logf(ErrorLevel, format, args...)
}
func Fatalf(format string, args ...interface{}) {
func Fatalf(format string, args ...any) {
logrus.Fatalf(format, args...)
}
func logf(level Level, format string, args ...interface{}) {
func logf(level Level, format string, args ...any) {
event := newEvent(level, format, args...)
if uint32(event.Level) > _defaultLevel.Load() {
return
Executable → Regular
+40 -14
View File
@@ -2,41 +2,67 @@ package main
import (
"flag"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/xjasonlyu/tun2socks/engine"
"github.com/xjasonlyu/tun2socks/log"
_ "github.com/xjasonlyu/tun2socks/v2/dns"
"github.com/xjasonlyu/tun2socks/v2/engine"
"github.com/xjasonlyu/tun2socks/v2/internal/version"
"github.com/xjasonlyu/tun2socks/v2/log"
"go.uber.org/automaxprocs/maxprocs"
"gopkg.in/yaml.v3"
)
var key = new(engine.Key)
var (
key = new(engine.Key)
configFile string
versionFlag bool
)
func init() {
flag.IntVar(&key.Mark, "fwmark", 0, "Set firewall MARK (Linux only)")
flag.IntVar(&key.MTU, "mtu", 0, "Set device maximum transmission unit (MTU)")
flag.IntVar(&key.UDPTimeout, "udp-timeout", 0, "Set timeout for each UDP session")
flag.BoolVar(&key.Version, "version", false, "Show version information and quit")
flag.DurationVar(&key.UDPTimeout, "udp-timeout", 0, "Set timeout for each UDP session")
flag.StringVar(&configFile, "config", "", "YAML format configuration file")
flag.StringVar(&key.Device, "device", "", "Use this device [driver://]name")
flag.StringVar(&key.Interface, "interface", "", "Use network INTERFACE (Linux/MacOS only)")
flag.StringVar(&key.LogLevel, "loglevel", "info", "Log level [debug|info|warn|error|silent]")
flag.StringVar(&key.LogLevel, "loglevel", "info", "Log level [debug|info|warning|error|silent]")
flag.StringVar(&key.Proxy, "proxy", "", "Use this proxy [protocol://]host[:port]")
flag.StringVar(&key.Stats, "stats", "", "HTTP statistic server listen address")
flag.StringVar(&key.Token, "token", "", "HTTP statistic server auth token")
flag.StringVar(&key.RestAPI, "restapi", "", "HTTP statistic server listen address")
flag.StringVar(&key.TCPSendBufferSize, "tcp-sndbuf", "", "Set TCP send buffer size for netstack")
flag.StringVar(&key.TCPReceiveBufferSize, "tcp-rcvbuf", "", "Set TCP receive buffer size for netstack")
flag.BoolVar(&key.TCPModerateReceiveBuffer, "tcp-auto-tuning", false, "Enable TCP receive buffer auto-tuning")
flag.BoolVar(&versionFlag, "version", false, "Show version and then quit")
flag.Parse()
}
func main() {
engine.Insert(key)
maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))
checkErr := func(msg string, f func() error) {
if err := f(); err != nil {
log.Fatalf("Failed to %s: %v", msg, err)
if versionFlag {
fmt.Println(version.String())
fmt.Println(version.BuildString())
os.Exit(0)
}
if configFile != "" {
data, err := os.ReadFile(configFile)
if err != nil {
log.Fatalf("Failed to read config file '%s': %v", configFile, err)
}
if err = yaml.Unmarshal(data, key); err != nil {
log.Fatalf("Failed to unmarshal config file '%s': %v", configFile, err)
}
}
checkErr("start engine", engine.Start)
defer checkErr("stop engine", engine.Stop)
engine.Insert(key)
engine.Start()
defer engine.Stop()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
+66
View File
@@ -0,0 +1,66 @@
package metadata
import (
"net"
"strconv"
)
// Metadata contains metadata of transport protocol sessions.
type Metadata struct {
Network Network `json:"network"`
SrcIP net.IP `json:"sourceIP"`
MidIP net.IP `json:"dialerIP"`
DstIP net.IP `json:"destinationIP"`
SrcPort uint16 `json:"sourcePort"`
MidPort uint16 `json:"dialerPort"`
DstPort uint16 `json:"destinationPort"`
}
func (m *Metadata) DestinationAddress() string {
return net.JoinHostPort(m.DstIP.String(), strconv.FormatUint(uint64(m.DstPort), 10))
}
func (m *Metadata) SourceAddress() string {
return net.JoinHostPort(m.SrcIP.String(), strconv.FormatUint(uint64(m.SrcPort), 10))
}
func (m *Metadata) Addr() net.Addr {
return &Addr{metadata: m}
}
func (m *Metadata) TCPAddr() *net.TCPAddr {
if m.Network != TCP || m.DstIP == nil {
return nil
}
return &net.TCPAddr{
IP: m.DstIP,
Port: int(m.DstPort),
}
}
func (m *Metadata) UDPAddr() *net.UDPAddr {
if m.Network != UDP || m.DstIP == nil {
return nil
}
return &net.UDPAddr{
IP: m.DstIP,
Port: int(m.DstPort),
}
}
// Addr implements the net.Addr interface.
type Addr struct {
metadata *Metadata
}
func (a *Addr) Metadata() *Metadata {
return a.metadata
}
func (a *Addr) Network() string {
return a.metadata.Network.String()
}
func (a *Addr) String() string {
return a.metadata.DestinationAddress()
}
+27
View File
@@ -0,0 +1,27 @@
package metadata
import (
"fmt"
)
const (
TCP Network = iota
UDP
)
type Network uint8
func (n Network) String() string {
switch n {
case TCP:
return "tcp"
case UDP:
return "udp"
default:
return fmt.Sprintf("network(%d)", n)
}
}
func (n Network) MarshalText() ([]byte, error) {
return []byte(n.String()), nil
}
Executable → Regular
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"errors"
"net"
M "github.com/xjasonlyu/tun2socks/constant"
"github.com/xjasonlyu/tun2socks/proxy/proto"
M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
)
var _ Proxy = (*Base)(nil)
Executable → Regular
+5 -5
View File
@@ -4,9 +4,9 @@ import (
"context"
"net"
"github.com/xjasonlyu/tun2socks/component/dialer"
M "github.com/xjasonlyu/tun2socks/constant"
"github.com/xjasonlyu/tun2socks/proxy/proto"
"github.com/xjasonlyu/tun2socks/v2/component/dialer"
M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
)
var _ Proxy = (*Direct)(nil)
@@ -45,8 +45,8 @@ type directPacketConn struct {
}
func (pc *directPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
if m, ok := addr.(*M.Metadata); ok && m.DstIP != nil {
return pc.PacketConn.WriteTo(b, m.UDPAddr())
if udpAddr, ok := addr.(*net.UDPAddr); ok {
return pc.PacketConn.WriteTo(b, udpAddr)
}
udpAddr, err := net.ResolveUDPAddr("udp", addr.String())
+3 -3
View File
@@ -13,9 +13,9 @@ import (
"net/http"
"net/url"
"github.com/xjasonlyu/tun2socks/component/dialer"
M "github.com/xjasonlyu/tun2socks/constant"
"github.com/xjasonlyu/tun2socks/proxy/proto"
"github.com/xjasonlyu/tun2socks/v2/component/dialer"
M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
)
type HTTP struct {
Executable → Regular
+3 -5
View File
@@ -6,17 +6,15 @@ import (
"net"
"time"
M "github.com/xjasonlyu/tun2socks/constant"
"github.com/xjasonlyu/tun2socks/proxy/proto"
M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
)
const (
tcpConnectTimeout = 5 * time.Second
)
var (
_defaultDialer Dialer = &Base{}
)
var _defaultDialer Dialer = &Base{}
type Dialer interface {
DialContext(context.Context, *M.Metadata) (net.Conn, error)
+27 -5
View File
@@ -2,11 +2,12 @@ package proxy
import (
"context"
"errors"
"io"
"net"
"time"
M "github.com/xjasonlyu/tun2socks/constant"
"github.com/xjasonlyu/tun2socks/proxy/proto"
M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
)
var _ Proxy = (*Reject)(nil)
@@ -24,9 +25,30 @@ func NewReject() *Reject {
}
func (r *Reject) DialContext(context.Context, *M.Metadata) (net.Conn, error) {
return nil, errors.New("TCP rejected")
return &nopConn{}, nil
}
func (r *Reject) DialUDP(*M.Metadata) (net.PacketConn, error) {
return nil, errors.New("UDP rejected")
return &nopPacketConn{}, nil
}
type nopConn struct{}
func (rw *nopConn) Read([]byte) (int, error) { return 0, io.EOF }
func (rw *nopConn) Write([]byte) (int, error) { return 0, io.EOF }
func (rw *nopConn) Close() error { return nil }
func (rw *nopConn) LocalAddr() net.Addr { return nil }
func (rw *nopConn) RemoteAddr() net.Addr { return nil }
func (rw *nopConn) SetDeadline(time.Time) error { return nil }
func (rw *nopConn) SetReadDeadline(time.Time) error { return nil }
func (rw *nopConn) SetWriteDeadline(time.Time) error { return nil }
type nopPacketConn struct{}
func (npc *nopPacketConn) WriteTo(b []byte, _ net.Addr) (n int, err error) { return len(b), nil }
func (npc *nopPacketConn) ReadFrom([]byte) (int, net.Addr, error) { return 0, nil, io.EOF }
func (npc *nopPacketConn) Close() error { return nil }
func (npc *nopPacketConn) LocalAddr() net.Addr { return &net.UDPAddr{IP: net.IPv4zero, Port: 0} }
func (npc *nopPacketConn) SetDeadline(time.Time) error { return nil }
func (npc *nopPacketConn) SetReadDeadline(time.Time) error { return nil }
func (npc *nopPacketConn) SetWriteDeadline(time.Time) error { return nil }
Executable → Regular
+9 -9
View File
@@ -6,11 +6,11 @@ import (
"fmt"
"net"
"github.com/xjasonlyu/tun2socks/component/dialer"
obfs "github.com/xjasonlyu/tun2socks/component/simple-obfs"
M "github.com/xjasonlyu/tun2socks/constant"
"github.com/xjasonlyu/tun2socks/proxy/proto"
"github.com/xjasonlyu/tun2socks/transport/socks5"
"github.com/xjasonlyu/tun2socks/v2/component/dialer"
obfs "github.com/xjasonlyu/tun2socks/v2/component/simple-obfs"
M "github.com/xjasonlyu/tun2socks/v2/metadata"
"github.com/xjasonlyu/tun2socks/v2/proxy/proto"
"github.com/xjasonlyu/tun2socks/v2/transport/socks5"
"github.com/Dreamacro/go-shadowsocks2/core"
)
@@ -61,7 +61,7 @@ func (ss *Shadowsocks) DialContext(ctx context.Context, metadata *M.Metadata) (c
}
c = ss.cipher.StreamConn(c)
_, err = c.Write(metadata.SerializesSocksAddr())
_, err = c.Write(serializeSocksAddr(metadata))
return
}
@@ -88,10 +88,10 @@ type ssPacketConn struct {
func (pc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
var packet []byte
if m, ok := addr.(*M.Metadata); ok {
packet, err = socks5.EncodeUDPPacket(m.SerializesSocksAddr(), b)
if ma, ok := addr.(*M.Addr); ok {
packet, err = socks5.EncodeUDPPacket(serializeSocksAddr(ma.Metadata()), b)
} else {
packet, err = socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
packet, err = socks5.EncodeUDPPacket(socks5.ParseAddr(addr), b)
}
if err != nil {

Some files were not shown because too many files have changed in this diff Show More