Compare commits

...

9 Commits

Author SHA1 Message Date
世界
8d044232af Update documentation 2022-09-09 15:42:33 +08:00
世界
aa7e85caa7 Update dependencies
Add half close for smux
Update gVisor to 20220905.0
2022-09-09 14:44:18 +08:00
zakuwaki
46a8f24400 Optional proxyproto header 2022-09-09 14:44:18 +08:00
世界
87bc292296 Add comment filter for config 2022-09-09 14:44:18 +08:00
世界
ac539ace70 Add system tun stack 2022-09-09 14:44:18 +08:00
世界
a15b13978f Set default tun mtu to 9000 like clash
IDK why, maybe faster in a local speed test?
2022-09-09 14:44:18 +08:00
世界
0c975db0a6 Set udp dontfrag by default 2022-09-09 14:44:18 +08:00
世界
cb4fea0240 Refactor wireguard & add tun support 2022-09-09 14:44:18 +08:00
世界
8e7957d440 Add support for use with android VPNService 2022-09-09 14:44:18 +08:00
42 changed files with 1192 additions and 511 deletions

View File

@@ -112,6 +112,10 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia
if options.TCPFastOpen {
warnTFOOnUnsupportedPlatform.Check()
}
if !options.UDPFragment {
dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment())
listener.Control = control.Append(listener.Control, control.DisableUDPFragment())
}
var bindUDPAddr string
udpDialer := dialer
var bindAddress netip.Addr

128
common/json/comment.go Normal file
View File

@@ -0,0 +1,128 @@
package json
import (
"bufio"
"io"
)
// kanged from v2ray
type commentFilterState = byte
const (
commentFilterStateContent commentFilterState = iota
commentFilterStateEscape
commentFilterStateDoubleQuote
commentFilterStateDoubleQuoteEscape
commentFilterStateSingleQuote
commentFilterStateSingleQuoteEscape
commentFilterStateComment
commentFilterStateSlash
commentFilterStateMultilineComment
commentFilterStateMultilineCommentStar
)
type CommentFilter struct {
br *bufio.Reader
state commentFilterState
}
func NewCommentFilter(reader io.Reader) io.Reader {
return &CommentFilter{br: bufio.NewReader(reader)}
}
func (v *CommentFilter) Read(b []byte) (int, error) {
p := b[:0]
for len(p) < len(b)-2 {
x, err := v.br.ReadByte()
if err != nil {
if len(p) == 0 {
return 0, err
}
return len(p), nil
}
switch v.state {
case commentFilterStateContent:
switch x {
case '"':
v.state = commentFilterStateDoubleQuote
p = append(p, x)
case '\'':
v.state = commentFilterStateSingleQuote
p = append(p, x)
case '\\':
v.state = commentFilterStateEscape
case '#':
v.state = commentFilterStateComment
case '/':
v.state = commentFilterStateSlash
default:
p = append(p, x)
}
case commentFilterStateEscape:
p = append(p, '\\', x)
v.state = commentFilterStateContent
case commentFilterStateDoubleQuote:
switch x {
case '"':
v.state = commentFilterStateContent
p = append(p, x)
case '\\':
v.state = commentFilterStateDoubleQuoteEscape
default:
p = append(p, x)
}
case commentFilterStateDoubleQuoteEscape:
p = append(p, '\\', x)
v.state = commentFilterStateDoubleQuote
case commentFilterStateSingleQuote:
switch x {
case '\'':
v.state = commentFilterStateContent
p = append(p, x)
case '\\':
v.state = commentFilterStateSingleQuoteEscape
default:
p = append(p, x)
}
case commentFilterStateSingleQuoteEscape:
p = append(p, '\\', x)
v.state = commentFilterStateSingleQuote
case commentFilterStateComment:
if x == '\n' {
v.state = commentFilterStateContent
p = append(p, '\n')
}
case commentFilterStateSlash:
switch x {
case '/':
v.state = commentFilterStateComment
case '*':
v.state = commentFilterStateMultilineComment
default:
p = append(p, '/', x)
}
case commentFilterStateMultilineComment:
switch x {
case '*':
v.state = commentFilterStateMultilineCommentStar
case '\n':
p = append(p, '\n')
}
case commentFilterStateMultilineCommentStar:
switch x {
case '/':
v.state = commentFilterStateContent
case '*':
// Stay
case '\n':
p = append(p, '\n')
default:
v.state = commentFilterStateMultilineComment
}
default:
panic("Unknown state.")
}
}
return len(p), nil
}

View File

@@ -13,6 +13,7 @@ import (
type Listener struct {
net.Listener
AcceptNoHeader bool
}
func (l *Listener) Accept() (net.Conn, error) {
@@ -22,7 +23,7 @@ func (l *Listener) Accept() (net.Conn, error) {
}
bufReader := std_bufio.NewReader(conn)
header, err := proxyproto.Read(bufReader)
if err != nil {
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) {
return nil, err
}
if bufReader.Buffered() > 0 {
@@ -33,8 +34,11 @@ func (l *Listener) Accept() (net.Conn, error) {
}
conn = bufio.NewCachedConn(conn, cache)
}
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
Source: M.SocksaddrFromNet(header.SourceAddr),
Destination: M.SocksaddrFromNet(header.DestinationAddr),
}}, nil
if header != nil {
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
Source: M.SocksaddrFromNet(header.SourceAddr),
Destination: M.SocksaddrFromNet(header.DestinationAddr),
}}, nil
}
return conn, nil
}

View File

@@ -20,7 +20,7 @@ type systemProxy struct {
isMixed bool
}
func (p *systemProxy) update() error {
func (p *systemProxy) update(event int) error {
newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
if p.interfaceName == newInterfaceName {
return nil
@@ -88,7 +88,7 @@ func SetSystemProxy(router adapter.Router, port uint16, isMixed bool) (func() er
port: port,
isMixed: isMixed,
}
err := proxy.update()
err := proxy.update(tun.EventInterfaceUpdate)
if err != nil {
return nil, err
}

View File

@@ -1,6 +1,6 @@
package constant
var (
Version = "1.0.1"
Version = "1.1-beta1"
Commit = ""
)

View File

@@ -1,3 +1,46 @@
#### 1.1-beta1
* Add support for use with android VPNService **1**
* Add tun support for WireGuard outbound **2**
* Add system tun stack **3**
* Add comment filter for config **4**
* Add option for allow optional proxy protocol header
* Add half close for smux
* Set UDP DF by default **5**
* Set default tun mtu to 9000
* Update gVisor to 20220905.0
*1*:
In previous versions, Android VPN would not work with tun enabled.
The usage of tun over VPN and VPN over tun is now supported, see [Tun Inbound](/configuration/inbound/tun#auto_route).
*2*:
In previous releases, WireGuard outbound support was backed by the lower performance gVisor virtual interface.
It achieves the same performance as wireguard-go by providing automatic system interface support.
*3*:
It does not depend on gVisor and has better performance in some cases.
It is less compatible and may not be available in some environments.
*4*:
Annotated json configuration files are now supported.
*5*:
UDP fragmentation is now blocked by default.
Including shadowsocks-libev, shadowsocks-rust and quic-go all disable segmentation by default.
See [Dial Fields](/configuration/shared/dial#udp_fragment)
and [Listen Fields](/configuration/shared/listen#udp_fragment).
#### 1.0.1
* Fix match 4in6 address in ip_cidr

View File

@@ -12,7 +12,7 @@
"interface_name": "tun0",
"inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 1500,
"mtu": 9000,
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": false,
@@ -80,6 +80,10 @@ Set the default route to the Tun.
To avoid traffic loopback, set `route.auto_detect_interface` or `route.default_interface` or `outbound.bind_interface`
!!! note "Use with Android VPN"
By default, VPN takes precedence over tun. To make tun go through VPN, enable `route.override_android_vpn`.
#### strict_route
Enforce strict routing rules in Linux when `auto_route` is enabled:
@@ -92,6 +96,10 @@ not be accessible by others.
#### endpoint_independent_nat
!!! info ""
This item is only available on the gvisor stack, other stacks are endpoint-independent NAT by default.
Enable endpoint-independent NAT.
Performance may degrade slightly, so it is not recommended to enable on when it is not needed.
@@ -104,10 +112,11 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
TCP/IP stack.
| Stack | Upstream | Status |
|------------------|-----------------------------------------------------------------------|-------------------|
| gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | recommended |
| LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
| Stack | Description | Status |
|------------------|--------------------------------------------------------------------------------|-------------------|
| gVisor (default) | Based on [google/gvisor](https://github.com/google/gvisor) | recommended |
| system | Less compatibility and sometimes better performance. | recommended |
| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived |
!!! warning ""

View File

@@ -12,7 +12,7 @@
"interface_name": "tun0",
"inet4_address": "172.19.0.1/30",
"inet6_address": "fdfe:dcba:9876::1/128",
"mtu": 1500,
"mtu": 9000,
"auto_route": true,
"strict_route": true,
"endpoint_independent_nat": false,
@@ -80,6 +80,10 @@ tun 接口的 IPv6 前缀。
为避免流量环回,请设置 `route.auto_detect_interface``route.default_interface``outbound.bind_interface`
!!! note "与 Android VPN 一起使用"
VPN 默认优先于 tun。要使 tun 经过 VPN启用 `route.override_android_vpn`
#### strict_route
在 Linux 中启用 `auto_route` 时执行严格的路由规则。
@@ -103,10 +107,11 @@ UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
TCP/IP 栈。
| 栈 | 上游 | 状态 |
|------------------|-----------------------------------------------------------------------|-------|
| gVisor (default) | [google/gvisor](https://github.com/google/gvisor) | 推荐 |
| LWIP | [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
| 栈 | 描述 | 状态 |
|------------------|--------------------------------------------------------------------------|-------|
| gVisor (default) | 基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 |
| system | 兼容性较差,有时性能更好。 | 推荐 |
| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
!!! warning ""

View File

@@ -7,8 +7,9 @@
"server": "127.0.0.1",
"server_port": 1080,
"system_interface": false,
"interface_name": "wg0",
"local_address": [
"10.0.0.1",
"10.0.0.2/32"
],
"private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=",
@@ -39,11 +40,21 @@ The server address.
The server port.
#### system_interface
Use system tun support.
Requires privileges and cannot conflict with system interfaces.
#### interface_name
Custom device name when `system_interface` enabled.
#### local_address
==Required==
List of IP (v4 or v6) addresses (optionally with CIDR masks) to be assigned to the interface.
List of IP (v4 or v6) address prefixes to be assigned to the interface.
#### private_key

View File

@@ -7,8 +7,9 @@
"server": "127.0.0.1",
"server_port": 1080,
"system_interface": false,
"interface_name": "wg0",
"local_address": [
"10.0.0.1",
"10.0.0.2/32"
],
"private_key": "YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=",
@@ -39,13 +40,23 @@
服务器端口。
#### system_interface
使用系统 tun 支持。
需要特权且不能与系统接口冲突。
#### interface_name
启用 `system_interface` 时的自定义设备名称。
#### local_address
==必填==
接口的 IPv4/IPv6 地址或地址段的列表您。
要分配给接口的 IPv4 或 v6地址列表(可以选择带有 CIDR 掩码)
要分配给接口的 IPv4 或 v6地址列表。
#### private_key

View File

@@ -10,6 +10,7 @@
"rules": [],
"final": "",
"auto_detect_interface": false,
"override_android_vpn": false,
"default_interface": "en0",
"default_mark": 233
}
@@ -34,17 +35,25 @@ Default outbound tag. the first outbound will be used if empty.
Only supported on Linux, Windows and macOS.
Bind outbound connections to the default NIC by default to prevent routing loops under Tun.
Bind outbound connections to the default NIC by default to prevent routing loops under tun.
Takes no effect if `outbound.bind_interface` is set.
#### override_android_vpn
!!! error ""
Only supported on Android.
Accept Android VPN as upstream NIC when `auto_detect_interface` enabled.
#### default_interface
!!! error ""
Only supported on Linux, Windows and macOS.
Bind outbound connections to the specified NIC by default to prevent routing loops under Tun.
Bind outbound connections to the specified NIC by default to prevent routing loops under tun.
Takes no effect if `auto_detect_interface` is set.

View File

@@ -10,6 +10,7 @@
"rules": [],
"final": "",
"auto_detect_interface": false,
"override_android_vpn": false,
"default_interface": "en0",
"default_mark": 233
}
@@ -34,17 +35,25 @@
仅支持 Linux、Windows 和 macOS。
默认将出站连接绑定到默认网卡,以防止在 Tun 下出现路由环路。
默认将出站连接绑定到默认网卡,以防止在 tun 下出现路由环路。
如果设置了 `outbound.bind_interface` 设置,则不生效。
#### override_android_vpn
!!! error ""
仅支持 Android。
启用 `auto_detect_interface` 时接受 Android VPN 作为上游网卡。
#### default_interface
!!! error ""
仅支持 Linux、Windows 和 macOS。
默认将出站连接绑定到指定网卡,以防止在 Tun 下出现路由环路。
默认将出站连接绑定到指定网卡,以防止在 tun 下出现路由环路。
如果设置了 `auto_detect_interface` 设置,则不生效。

View File

@@ -9,6 +9,7 @@
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"udp_fragment": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
@@ -16,9 +17,9 @@
### Fields
| Field | Available Context |
|-----------------------------------------------------------------------------------|-------------------|
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` /`connect_timeout` | `detour` not set |
| Field | Available Context |
|---------------------------------------------------------------------------------------------------------------------|-------------------|
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` not set |
#### detour
@@ -44,6 +45,14 @@ Set netfilter routing mark.
Reuse listener address.
#### tcp_fast_open
Enable TCP Fast Open.
#### udp_fragment
Enable UDP fragmentation.
#### connect_timeout
Connect timeout, in golang's Duration format.

View File

@@ -9,6 +9,7 @@
"reuse_addr": false,
"connect_timeout": "5s",
"tcp_fast_open": false,
"udp_fragment": false,
"domain_strategy": "prefer_ipv6",
"fallback_delay": "300ms"
}
@@ -16,6 +17,11 @@
### 字段
| 字段 | 可用上下文 |
|---------------------------------------------------------------------------------------------------------------------|--------------|
| `bind_interface` /`bind_address` /`routing_mark` /`reuse_addr` / `tcp_fast_open`/ `udp_fragment` /`connect_timeout` | `detour` 未设置 |
#### detour
上游出站的标签。
@@ -42,6 +48,14 @@
重用监听地址。
#### tcp_fast_open
启用 TCP Fast Open。
#### udp_fragment
启用 UDP 分段。
#### connect_timeout
连接超时,采用 golang 的 Duration 格式。

View File

@@ -5,24 +5,27 @@
"listen": "::",
"listen_port": 5353,
"tcp_fast_open": false,
"udp_fragment": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"proxy_protocol": false,
"proxy_protocol_accept_no_header": false,
"detour": "another-in"
}
```
### Fields
| Field | Available Context |
|------------------|-------------------------------------------------------------------|
| `listen` | Needs to listen on TCP or UDP. |
| `listen_port` | Needs to listen on TCP or UDP. |
| `tcp_fast_open` | Needs to listen on TCP. |
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
| `proxy_protocol` | Needs to listen on TCP. |
| Field | Available Context |
|-----------------------------------|-------------------------------------------------------------------|
| `listen` | Needs to listen on TCP or UDP. |
| `listen_port` | Needs to listen on TCP or UDP. |
| `tcp_fast_open` | Needs to listen on TCP. |
| `udp_timeout` | Needs to assemble UDP connections, currently Tun and Shadowsocks. |
| `proxy_protocol` | Needs to listen on TCP. |
| `proxy_protocol_accept_no_header` | When `proxy_protocol` enabled |
#### listen
@@ -36,7 +39,11 @@ Listen port.
#### tcp_fast_open
Enable tcp fast open for listener.
Enable TCP Fast Open.
#### udp_fragment
Enable UDP fragmentation.
#### sniff
@@ -66,6 +73,10 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
Parse [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) in the connection header.
#### proxy_protocol_accept_no_header
Accept connections without Proxy Protocol header.
#### detour
If set, connections will be forwarded to the specified inbound.

View File

@@ -5,21 +5,26 @@
"listen": "::",
"listen_port": 5353,
"tcp_fast_open": false,
"udp_fragment": false,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"proxy_protocol": false,
"proxy_protocol_accept_no_header": false,
"detour": "another-in"
}
```
| 字段 | 可用上下文 |
|------------------|-------------------------------------|
| `listen` | 需要监听 TCP 或 UDP。 |
| `listen_port` | 需要监听 TCP 或 UDP。 |
| `tcp_fast_open` | 需要监听 TCP |
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
| `proxy_protocol` | 需要监听 TCP。 |
| 字段 | 可用上下文 |
|-----------------------------------|-------------------------------------|
| `listen` | 需要监听 TCP 或 UDP。 |
| `listen_port` | 需要监听 TCP 或 UDP。 |
| `tcp_fast_open` | 需要监听 TCP。 |
| `udp_timeout` | 需要组装 UDP 连接, 当前为 Tun 和 Shadowsocks。 |
| `proxy_protocol` | 需要监听 TCP。 |
| `proxy_protocol_accept_no_header` | `proxy_protocol` 启用时 |
### 字段
@@ -35,7 +40,11 @@
#### tcp_fast_open
为监听器启用 TCP 快速打开
启用 TCP Fast Open
#### udp_fragment
启用 UDP 分段。
#### sniff
@@ -65,6 +74,10 @@ UDP NAT 过期时间,以秒为单位,默认为 3005 分钟)。
解析连接头中的 [代理协议](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)。
#### proxy_protocol_accept_no_header
接受没有代理协议标头的连接。
#### detour
如果设置,连接将被转发到指定的入站。

16
go.mod
View File

@@ -20,23 +20,23 @@ require (
github.com/pires/go-proxyproto v0.6.2
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133
github.com/sagernet/sing v0.0.0-20220905164441-f3d346256d4a
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83
github.com/sagernet/sing-tun v0.0.0-20220909054347-575ac2941790
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195
github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
go.uber.org/atomic v1.10.0
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b
golang.org/x/net v0.0.0-20220907135653-1e95f45603a7
golang.org/x/sys v0.0.0-20220908164124-27713097b956
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
)
//replace github.com/sagernet/sing => ../sing
@@ -59,7 +59,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
go.uber.org/multierr v1.6.0 // indirect

32
go.sum
View File

@@ -129,24 +129,24 @@ github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a h1:SE3Xn4GOQ+kx
github.com/sagernet/certmagic v0.0.0-20220819042630-4a57f8b6853a/go.mod h1:Q+ZXyesnkjV5B70B1ixk65ecKrlJ2jz0atv3fPKsVVo=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17 h1:zvm6IrIgo4rLizJCHkH+SWUBhm+jyjjozX031QdAlj8=
github.com/sagernet/netlink v0.0.0-20220826133217-3fb4ff92ea17/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb h1:wc0yQ+SBn4TaTYRwpwvEm3nc4eRdxk6vtRbouLVZAzk=
github.com/sagernet/quic-go v0.0.0-20220818150011-de611ab3e2bb/go.mod h1:MIccjRKnPTjWwAOpl+AUGWOkzyTd9tERytudxu+1ra4=
github.com/sagernet/sing v0.0.0-20220812082120-05f9836bff8f/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133 h1:krnb8wKEFIdXhmJYlhJMbEcPsJFISy2fz90uHVz7hMU=
github.com/sagernet/sing v0.0.0-20220903085538-02b9ca1cc133/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing v0.0.0-20220905164441-f3d346256d4a h1:Bqt+eYP7vJocAgAVAXC0B0ZN0uMr6g6exAoF3Ado2pg=
github.com/sagernet/sing v0.0.0-20220905164441-f3d346256d4a/go.mod h1:kZvzh1VDa/Dg/Bt5WaYKU0jl5ept8KKDpl3Ay4gRtRQ=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666 h1:XUTocA/Ek0dFxUX+xJCWMPPFZCn2GC/uLrBjTSr1vHY=
github.com/sagernet/sing-dns v0.0.0-20220822023312-3e086b06d666/go.mod h1:eDyH7AJmqBGjZQdQmpZIzlbTREudZuWDExMuGKgjRVM=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83 h1:SoWiHYuOCVedqA7T/CJSZUUrcPGKQb2wFKEq8DphiAI=
github.com/sagernet/sing-tun v0.0.0-20220828031750-185b6c880a83/go.mod h1:76r07HS1WRcEI4mE9pFsohfTBUt1j/G9Avz6DaOP3VU=
github.com/sagernet/sing-tun v0.0.0-20220909054347-575ac2941790 h1:SnIhaHWVk3tTTx/zFEor9zRItcbzTYMoC8F22FAZ/Iw=
github.com/sagernet/sing-tun v0.0.0-20220909054347-575ac2941790/go.mod h1:5AhPUv9jWDQ3pv3Mj78SL/1TSjhoaj6WNASxRKLqXqM=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f h1:6l9aXZqAl1JqXJWi89KHpWnM/moQUPGG+XiwMc+yD0A=
github.com/sagernet/sing-vmess v0.0.0-20220907073918-72d7fdf6825f/go.mod h1:u66Vv7NHXJWfeAmhh7JuJp/cwxmuQlM56QoZ7B7Mmd0=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195 h1:5VBIbVw9q7aKbrFdT83mjkyvQ+VaRsQ6yflTepfln38=
github.com/sagernet/smux v0.0.0-20220831015742-e0f1988e3195/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a h1:GCNwsN8MEckpjGJjK3qjQBQ9qHsoXB9B/KHUWBvE1V4=
github.com/sagernet/smux v0.0.0-20220907034654-1acb8471c15a/go.mod h1:yedWtra8nyGJ+SyI+ziwuaGMzBatbB10P1IOOZbbSK8=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -212,8 +212,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220907135653-1e95f45603a7 h1:1WGATo9HAhkWMbfyuVU0tEFP88OIkUvwaHFveQPvzCQ=
golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -244,8 +244,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -276,8 +276,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b h1:qgrKnOfe1zyURRNdmDlGbN32i38Zjmw0B1+TMdHcOvg=
golang.zx2c4.com/wireguard v0.0.0-20220829161405-d1d08426b27b/go.mod h1:6y4CqPAy54NwiN4nC8K+R1eMpQDB1P2d25qmunh2RSA=
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0 h1:5ZkdpbduT/g+9OtbSDvbF3KvfQG45CtH/ppO8FUmvCQ=
golang.zx2c4.com/wireguard v0.0.0-20220904105730-b51010ba13f0/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -326,8 +326,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a h1:W1h3JsEzYWg7eD4908iHv49p7AOx7JPKsoh/fsxgylM=
gvisor.dev/gvisor v0.0.0-20220819163037-ba6e795b139a/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=

View File

@@ -29,7 +29,7 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
}
if a.listenOptions.ProxyProtocol {
a.logger.Debug("proxy protocol enabled")
tcpListener = &proxyproto.Listener{Listener: tcpListener}
tcpListener = &proxyproto.Listener{Listener: tcpListener, AcceptNoHeader: a.listenOptions.ProxyProtocolAcceptNoHeader}
}
a.tcpListener = tcpListener
return tcpListener, err

View File

@@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing-dns"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -17,11 +18,15 @@ import (
func (a *myInboundAdapter) ListenUDP() (net.PacketConn, error) {
bindAddr := M.SocksaddrFrom(netip.Addr(a.listenOptions.Listen), a.listenOptions.ListenPort)
udpConn, err := net.ListenUDP(M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.UDPAddr())
var lc net.ListenConfig
if !a.listenOptions.UDPFragment {
lc.Control = control.Append(lc.Control, control.DisableUDPFragment())
}
udpConn, err := lc.ListenPacket(a.ctx, M.NetworkFromNetAddr(N.NetworkUDP, bindAddr.Addr), bindAddr.String())
if err != nil {
return nil, err
}
a.udpConn = udpConn
a.udpConn = udpConn.(*net.UDPConn)
a.udpAddr = bindAddr
a.logger.Info("udp server started at ", udpConn.LocalAddr())
return udpConn, err

View File

@@ -40,11 +40,11 @@ type Tun struct {
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions) (*Tun, error) {
tunName := options.InterfaceName
if tunName == "" {
tunName = tun.DefaultInterfaceName()
tunName = tun.CalculateInterfaceName("")
}
tunMTU := options.MTU
if tunMTU == 0 {
tunMTU = 1500
tunMTU = 9000
}
var udpTimeout int64
if options.UDPTimeout != 0 {
@@ -77,8 +77,8 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
tunOptions: tun.Options{
Name: tunName,
MTU: tunMTU,
Inet4Address: options.Inet4Address.Build(),
Inet6Address: options.Inet6Address.Build(),
Inet4Address: common.Map(options.Inet4Address, option.ListenPrefix.Build),
Inet6Address: common.Map(options.Inet6Address, option.ListenPrefix.Build),
AutoRoute: options.AutoRoute,
StrictRoute: options.StrictRoute,
IncludeUID: includeUID,
@@ -86,6 +86,8 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage,
InterfaceMonitor: router.InterfaceMonitor(),
TableIndex: 2022,
},
endpointIndependentNat: options.EndpointIndependentNat,
udpTimeout: udpTimeout,
@@ -142,7 +144,18 @@ func (t *Tun) Start() error {
return E.Cause(err, "configure tun interface")
}
t.tunIf = tunIf
t.tunStack, err = tun.NewStack(t.ctx, t.stack, tunIf, t.tunOptions.MTU, t.endpointIndependentNat, t.udpTimeout, t)
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
Context: t.ctx,
Tun: tunIf,
MTU: t.tunOptions.MTU,
Name: t.tunOptions.Name,
Inet4Address: t.tunOptions.Inet4Address,
Inet6Address: t.tunOptions.Inet6Address,
EndpointIndependentNat: t.endpointIndependentNat,
UDPTimeout: t.udpTimeout,
Handler: t,
Logger: t.logger,
})
if err != nil {
return err
}

View File

@@ -20,7 +20,7 @@ type _Options struct {
type Options _Options
func (o *Options) UnmarshalJSON(content []byte) error {
decoder := json.NewDecoder(bytes.NewReader(content))
decoder := json.NewDecoder(json.NewCommentFilter(bytes.NewReader(content)))
decoder.DisallowUnknownFields()
err := decoder.Decode((*_Options)(o))
if err == nil {

View File

@@ -111,11 +111,13 @@ type InboundOptions struct {
}
type ListenOptions struct {
Listen ListenAddress `json:"listen"`
ListenPort uint16 `json:"listen_port,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"`
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
Detour string `json:"detour,omitempty"`
Listen ListenAddress `json:"listen"`
ListenPort uint16 `json:"listen_port,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPFragment bool `json:"udp_fragment,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"`
ProxyProtocol bool `json:"proxy_protocol,omitempty"`
ProxyProtocolAcceptNoHeader bool `json:"proxy_protocol_accept_no_header,omitempty"`
Detour string `json:"detour,omitempty"`
InboundOptions
}

View File

@@ -113,6 +113,7 @@ type DialerOptions struct {
ReuseAddr bool `json:"reuse_addr,omitempty"`
ConnectTimeout Duration `json:"connect_timeout,omitempty"`
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
UDPFragment bool `json:"udp_fragment,omitempty"`
DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"`
FallbackDelay Duration `json:"fallback_delay,omitempty"`
}

View File

@@ -16,6 +16,7 @@ type RouteOptions struct {
Final string `json:"final,omitempty"`
FindProcess bool `json:"find_process,omitempty"`
AutoDetectInterface bool `json:"auto_detect_interface,omitempty"`
OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"`
DefaultInterface string `json:"default_interface,omitempty"`
DefaultMark int `json:"default_mark,omitempty"`
}

View File

@@ -1,21 +1,21 @@
package option
type TunInboundOptions struct {
InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Inet4Address *ListenPrefix `json:"inet4_address,omitempty"`
Inet6Address *ListenPrefix `json:"inet6_address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"`
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
IncludePackage Listable[string] `json:"include_package,omitempty"`
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"`
InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Inet4Address Listable[ListenPrefix] `json:"inet4_address,omitempty"`
Inet6Address Listable[ListenPrefix] `json:"inet6_address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"`
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
IncludePackage Listable[string] `json:"include_package,omitempty"`
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"`
InboundOptions
}

View File

@@ -184,9 +184,6 @@ func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
return nil
}
func (p *ListenPrefix) Build() netip.Prefix {
if p == nil {
return netip.Prefix{}
}
return netip.Prefix(*p)
func (p ListenPrefix) Build() netip.Prefix {
return netip.Prefix(p)
}

View File

@@ -3,10 +3,12 @@ package option
type WireGuardOutboundOptions struct {
DialerOptions
ServerOptions
LocalAddress Listable[string] `json:"local_address"`
PrivateKey string `json:"private_key"`
PeerPublicKey string `json:"peer_public_key"`
PreSharedKey string `json:"pre_shared_key,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Network NetworkList `json:"network,omitempty"`
SystemInterface bool `json:"system_interface,omitempty"`
InterfaceName string `json:"interface_name,omitempty"`
LocalAddress Listable[ListenPrefix] `json:"local_address"`
PrivateKey string `json:"private_key"`
PeerPublicKey string `json:"peer_public_key"`
PreSharedKey string `json:"pre_shared_key,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
Network NetworkList `json:"network,omitempty"`
}

View File

@@ -8,49 +8,30 @@ import (
"encoding/hex"
"fmt"
"net"
"net/netip"
"os"
"strings"
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/transport/wireguard"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/header"
"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"
)
var _ adapter.Outbound = (*WireGuard)(nil)
type WireGuard struct {
myOutboundAdapter
ctx context.Context
serverAddr M.Socksaddr
dialer N.Dialer
endpoint conn.Endpoint
device *device.Device
tunDevice *wireTunDevice
connAccess sync.Mutex
conn *wireConn
bind *wireguard.ClientBind
device *device.Device
tunDevice wireguard.Device
}
func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) {
@@ -62,39 +43,13 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
logger: logger,
tag: tag,
},
ctx: ctx,
serverAddr: options.ServerOptions.Build(),
dialer: dialer.New(router, options.DialerOptions),
}
var endpointIp netip.Addr
if !outbound.serverAddr.IsFqdn() {
endpointIp = outbound.serverAddr.Addr
} else {
endpointIp = netip.AddrFrom4([4]byte{127, 0, 0, 1})
}
outbound.endpoint = conn.StdNetEndpoint(netip.AddrPortFrom(endpointIp, outbound.serverAddr.Port))
localAddress := make([]tcpip.AddressWithPrefix, len(options.LocalAddress))
if len(localAddress) == 0 {
peerAddr := options.ServerOptions.Build()
outbound.bind = wireguard.NewClientBind(ctx, dialer.New(router, options.DialerOptions), peerAddr)
localPrefixes := common.Map(options.LocalAddress, option.ListenPrefix.Build)
if len(localPrefixes) == 0 {
return nil, E.New("missing local address")
}
for index, address := range options.LocalAddress {
if strings.Contains(address, "/") {
prefix, err := netip.ParsePrefix(address)
if err != nil {
return nil, E.Cause(err, "parse local address prefix ", address)
}
localAddress[index] = tcpip.AddressWithPrefix{
Address: tcpip.Address(prefix.Addr().AsSlice()),
PrefixLen: prefix.Bits(),
}
} else {
addr, err := netip.ParseAddr(address)
if err != nil {
return nil, E.Cause(err, "parse local address ", address)
}
localAddress[index] = tcpip.Address(addr.AsSlice()).WithPrefix()
}
}
var privateKey, peerPublicKey, preSharedKey string
{
bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey)
@@ -119,13 +74,13 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
}
ipcConf := "private_key=" + privateKey
ipcConf += "\npublic_key=" + peerPublicKey
ipcConf += "\nendpoint=" + outbound.endpoint.DstToString()
ipcConf += "\nendpoint=" + peerAddr.String()
if preSharedKey != "" {
ipcConf += "\npreshared_key=" + preSharedKey
}
var has4, has6 bool
for _, address := range localAddress {
if address.Address.To4() != "" {
for _, address := range localPrefixes {
if address.Addr().Is4() {
has4 = true
} else {
has6 = true
@@ -141,11 +96,17 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
if mtu == 0 {
mtu = 1408
}
wireDevice, err := newWireDevice(localAddress, mtu)
if err != nil {
return nil, err
var wireTunDevice wireguard.Device
var err error
if !options.SystemInterface {
wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu)
} else {
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, localPrefixes, mtu)
}
wgDevice := device.NewDevice(wireDevice, (*wireClientBind)(outbound), &device.Logger{
if err != nil {
return nil, E.Cause(err, "create WireGuard device")
}
wgDevice := device.NewDevice(wireTunDevice, outbound.bind, &device.Logger{
Verbosef: func(format string, args ...interface{}) {
logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
},
@@ -161,7 +122,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
return nil, E.Cause(err, "setup wireguard")
}
outbound.device = wgDevice
outbound.tunDevice = wireDevice
outbound.tunDevice = wireTunDevice
return outbound, nil
}
@@ -172,54 +133,19 @@ func (w *WireGuard) DialContext(ctx context.Context, network string, destination
case N.NetworkUDP:
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
addr := tcpip.FullAddress{
NIC: defaultNIC,
Port: destination.Port,
}
if destination.IsFqdn() {
addrs, err := w.router.LookupDefault(ctx, destination.Fqdn)
if err != nil {
return nil, err
}
addr.Addr = tcpip.Address(addrs[0].AsSlice())
} else {
addr.Addr = tcpip.Address(destination.Addr.AsSlice())
}
bind := tcpip.FullAddress{
NIC: defaultNIC,
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = w.tunDevice.addr4
} else {
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = w.tunDevice.addr6
}
switch N.NetworkName(network) {
case N.NetworkTCP:
return gonet.DialTCPWithBind(ctx, w.tunDevice.stack, bind, addr, networkProtocol)
case N.NetworkUDP:
return gonet.DialUDP(w.tunDevice.stack, &bind, &addr, networkProtocol)
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
return N.DialSerial(ctx, w.tunDevice, network, destination, addrs)
}
return w.tunDevice.DialContext(ctx, network, destination)
}
func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
bind := tcpip.FullAddress{
NIC: defaultNIC,
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() || w.tunDevice.addr6 == "" {
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = w.tunDevice.addr4
} else {
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = w.tunDevice.addr6
}
return gonet.DialUDP(w.tunDevice.stack, &bind, nil, networkProtocol)
return w.tunDevice.ListenPacket(ctx, destination)
}
func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
@@ -231,300 +157,13 @@ func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn,
}
func (w *WireGuard) Start() error {
w.tunDevice.events <- tun.EventUp
return nil
return w.tunDevice.Start()
}
func (w *WireGuard) Close() error {
return common.Close(
common.PtrOrNil(w.tunDevice),
w.tunDevice,
common.PtrOrNil(w.device),
common.PtrOrNil(w.conn),
common.PtrOrNil(w.bind),
)
}
var _ conn.Bind = (*wireClientBind)(nil)
type wireClientBind WireGuard
func (c *wireClientBind) connect() (*wireConn, error) {
c.connAccess.Lock()
defer c.connAccess.Unlock()
if c.conn != nil {
select {
case <-c.conn.done:
default:
return c.conn, nil
}
}
udpConn, err := c.dialer.DialContext(c.ctx, "udp", c.serverAddr)
if err != nil {
return nil, &wireError{err}
}
c.conn = &wireConn{
Conn: udpConn,
done: make(chan struct{}),
}
return c.conn, nil
}
func (c *wireClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
return []conn.ReceiveFunc{c.receive}, 0, nil
}
func (c *wireClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
udpConn, err := c.connect()
if err != nil {
err = &wireError{err}
return
}
n, err = udpConn.Read(b)
if err != nil {
udpConn.Close()
err = &wireError{err}
}
ep = c.endpoint
return
}
func (c *wireClientBind) Close() error {
c.connAccess.Lock()
defer c.connAccess.Unlock()
common.Close(common.PtrOrNil(c.conn))
return nil
}
func (c *wireClientBind) SetMark(mark uint32) error {
return nil
}
func (c *wireClientBind) Send(b []byte, ep conn.Endpoint) error {
udpConn, err := c.connect()
if err != nil {
return err
}
_, err = udpConn.Write(b)
if err != nil {
udpConn.Close()
}
return err
}
func (c *wireClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
return c.endpoint, nil
}
type wireError struct {
cause error
}
func (w *wireError) Error() string {
return w.cause.Error()
}
func (w *wireError) Timeout() bool {
if cause, causeNet := w.cause.(net.Error); causeNet {
return cause.Timeout()
}
return false
}
func (w *wireError) Temporary() bool {
return true
}
type wireConn struct {
net.Conn
access sync.Mutex
done chan struct{}
}
func (w *wireConn) Close() error {
w.access.Lock()
defer w.access.Unlock()
select {
case <-w.done:
return net.ErrClosed
default:
}
w.Conn.Close()
close(w.done)
return nil
}
var _ tun.Device = (*wireTunDevice)(nil)
const defaultNIC tcpip.NICID = 1
type wireTunDevice struct {
stack *stack.Stack
mtu uint32
events chan tun.Event
outbound chan *stack.PacketBuffer
dispatcher stack.NetworkDispatcher
done chan struct{}
addr4 tcpip.Address
addr6 tcpip.Address
}
func newWireDevice(localAddresses []tcpip.AddressWithPrefix, mtu uint32) (*wireTunDevice, error) {
ipStack := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6},
HandleLocal: true,
})
tunDevice := &wireTunDevice{
stack: ipStack,
mtu: mtu,
events: make(chan tun.Event, 4),
outbound: make(chan *stack.PacketBuffer, 256),
done: make(chan struct{}),
}
err := ipStack.CreateNIC(defaultNIC, (*wireEndpoint)(tunDevice))
if err != nil {
return nil, E.New(err.String())
}
for _, addr := range localAddresses {
var protoAddr tcpip.ProtocolAddress
if len(addr.Address) == net.IPv4len {
tunDevice.addr4 = addr.Address
protoAddr = tcpip.ProtocolAddress{
Protocol: ipv4.ProtocolNumber,
AddressWithPrefix: addr,
}
} else {
tunDevice.addr6 = addr.Address
protoAddr = tcpip.ProtocolAddress{
Protocol: ipv6.ProtocolNumber,
AddressWithPrefix: addr,
}
}
err = ipStack.AddProtocolAddress(defaultNIC, protoAddr, stack.AddressProperties{})
if err != nil {
return nil, E.New("parse local address ", protoAddr.AddressWithPrefix, ": ", err.String())
}
}
sOpt := tcpip.TCPSACKEnabled(true)
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
cOpt := tcpip.CongestionControlOption("cubic")
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &cOpt)
ipStack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: defaultNIC})
ipStack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: defaultNIC})
return tunDevice, nil
}
func (w *wireTunDevice) File() *os.File {
return nil
}
func (w *wireTunDevice) Read(p []byte, offset int) (n int, err error) {
packetBuffer, ok := <-w.outbound
if !ok {
return 0, os.ErrClosed
}
defer packetBuffer.DecRef()
p = p[offset:]
for _, slice := range packetBuffer.AsSlices() {
n += copy(p[n:], slice)
}
return
}
func (w *wireTunDevice) Write(p []byte, offset int) (n int, err error) {
p = p[offset:]
if len(p) == 0 {
return
}
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(p) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
}
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithData(p),
})
defer packetBuffer.DecRef()
w.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
n = len(p)
return
}
func (w *wireTunDevice) Flush() error {
return nil
}
func (w *wireTunDevice) MTU() (int, error) {
return int(w.mtu), nil
}
func (w *wireTunDevice) Name() (string, error) {
return "sing-box", nil
}
func (w *wireTunDevice) Events() chan tun.Event {
return w.events
}
func (w *wireTunDevice) Close() error {
select {
case <-w.done:
return os.ErrClosed
default:
}
close(w.done)
w.stack.Close()
for _, endpoint := range w.stack.CleanupEndpoints() {
endpoint.Abort()
}
w.stack.Wait()
close(w.outbound)
return nil
}
var _ stack.LinkEndpoint = (*wireEndpoint)(nil)
type wireEndpoint wireTunDevice
func (ep *wireEndpoint) MTU() uint32 {
return ep.mtu
}
func (ep *wireEndpoint) MaxHeaderLength() uint16 {
return 0
}
func (ep *wireEndpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityNone
}
func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
ep.dispatcher = dispatcher
}
func (ep *wireEndpoint) IsAttached() bool {
return ep.dispatcher != nil
}
func (ep *wireEndpoint) Wait() {
}
func (ep *wireEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (ep *wireEndpoint) AddHeader(buffer *stack.PacketBuffer) {
}
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
for _, packetBuffer := range list.AsSlice() {
packetBuffer.IncRef()
ep.outbound <- packetBuffer
}
return list.Len(), nil
}

View File

@@ -244,7 +244,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
needInterfaceMonitor := options.AutoDetectInterface ||
C.IsDarwin && common.Any(inbounds, func(inbound option.Inbound) bool {
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || C.IsAndroid && inbound.TunOptions.AutoRoute
})
if router.interfaceBindManager != nil || needInterfaceMonitor {
@@ -258,12 +258,24 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
}
if router.networkMonitor != nil && needInterfaceMonitor {
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor)
interfaceMonitor, err := tun.NewDefaultInterfaceMonitor(router.networkMonitor, tun.DefaultInterfaceMonitorOptions{
OverrideAndroidVPN: options.OverrideAndroidVPN,
})
if err != nil {
return nil, E.New("auto_detect_interface unsupported on current platform")
}
interfaceMonitor.RegisterCallback(func() error {
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
interfaceMonitor.RegisterCallback(func(event int) error {
if C.IsAndroid {
var vpnStatus string
if router.interfaceMonitor.AndroidVPNEnabled() {
vpnStatus = "enabled"
} else {
vpnStatus = "disabled"
}
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
} else {
router.logger.Info("updated default interface ", router.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", router.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
}
return nil
})
router.interfaceMonitor = interfaceMonitor
@@ -667,12 +679,12 @@ func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, de
}
processInfo, err := process.FindProcessInfo(r.processSearcher, ctx, metadata.Network, metadata.Source.AddrPort(), originDestination)
if err != nil {
r.logger.DebugContext(ctx, "failed to search process: ", err)
r.logger.InfoContext(ctx, "failed to search process: ", err)
} else {
if processInfo.ProcessPath != "" {
r.logger.DebugContext(ctx, "found process path: ", processInfo.ProcessPath)
r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath)
} else if processInfo.PackageName != "" {
r.logger.DebugContext(ctx, "found package name: ", processInfo.PackageName)
r.logger.InfoContext(ctx, "found package name: ", processInfo.PackageName)
} else if processInfo.UserId != -1 {
if /*needUserName &&*/ true {
osUser, _ := user.LookupId(F.ToString(processInfo.UserId))
@@ -681,9 +693,9 @@ func (r *Router) match(ctx context.Context, metadata *adapter.InboundContext, de
}
}
if processInfo.User != "" {
r.logger.DebugContext(ctx, "found user: ", processInfo.User)
r.logger.InfoContext(ctx, "found user: ", processInfo.User)
} else {
r.logger.DebugContext(ctx, "found user id: ", processInfo.UserId)
r.logger.InfoContext(ctx, "found user id: ", processInfo.UserId)
}
}
metadata.ProcessInfo = processInfo

View File

@@ -1,6 +1,6 @@
[Interface]
PrivateKey = gHWUGzTh5YCEV6k8dneVP537XhVtoQJPIlFNs2zsxlE=
Address = 10.0.0.1/24
Address = 10.0.0.1/32
ListenPort = 10000
[Peer]

View File

@@ -2,6 +2,7 @@ package main
import (
"net/netip"
"os"
"testing"
"time"
@@ -40,7 +41,7 @@ func TestWireGuard(t *testing.T) {
Server: "127.0.0.1",
ServerPort: serverPort,
},
LocalAddress: []string{"10.0.0.2/32"},
LocalAddress: []option.ListenPrefix{option.ListenPrefix(netip.MustParsePrefix("10.0.0.2/32"))},
PrivateKey: "qGnwlkZljMxeECW8fbwAWdvgntnbK7B8UmMFl3zM0mk=",
PeerPublicKey: "QsdcBm+oJw2oNv0cIFXLIq1E850lgTBonup4qnKEQBg=",
},
@@ -49,3 +50,49 @@ func TestWireGuard(t *testing.T) {
})
testSuitWg(t, clientPort, testPort)
}
func TestWireGuardSystem(t *testing.T) {
if os.Getuid() != 0 {
t.Skip("requires root")
}
startDockerContainer(t, DockerOptions{
Image: ImageBoringTun,
Cap: []string{"MKNOD", "NET_ADMIN", "NET_RAW"},
Ports: []uint16{serverPort, testPort},
Bind: map[string]string{
"wireguard.conf": "/etc/wireguard/wg0.conf",
},
Cmd: []string{"wg0"},
})
time.Sleep(5 * time.Second)
startInstance(t, option.Options{
Inbounds: []option.Inbound{
{
Type: C.TypeMixed,
MixedOptions: option.HTTPMixedInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.ListenAddress(netip.IPv4Unspecified()),
ListenPort: clientPort,
},
},
},
},
Outbounds: []option.Outbound{
{
Type: C.TypeWireGuard,
WireGuardOptions: option.WireGuardOutboundOptions{
InterfaceName: "wg",
ServerOptions: option.ServerOptions{
Server: "127.0.0.1",
ServerPort: serverPort,
},
LocalAddress: []option.ListenPrefix{option.ListenPrefix(netip.MustParsePrefix("10.0.0.2/32"))},
PrivateKey: "qGnwlkZljMxeECW8fbwAWdvgntnbK7B8UmMFl3zM0mk=",
PeerPublicKey: "QsdcBm+oJw2oNv0cIFXLIq1E850lgTBonup4qnKEQBg=",
},
},
},
})
time.Sleep(10 * time.Second)
testSuitWg(t, clientPort, testPort)
}

View File

@@ -0,0 +1,132 @@
package wireguard
import (
"context"
"net"
"sync"
"github.com/sagernet/sing/common"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"golang.zx2c4.com/wireguard/conn"
)
var _ conn.Bind = (*ClientBind)(nil)
type ClientBind struct {
ctx context.Context
dialer N.Dialer
peerAddr M.Socksaddr
connAccess sync.Mutex
conn *wireConn
}
func NewClientBind(ctx context.Context, dialer N.Dialer, peerAddr M.Socksaddr) *ClientBind {
return &ClientBind{
ctx: ctx,
dialer: dialer,
peerAddr: peerAddr,
}
}
func (c *ClientBind) connect() (*wireConn, error) {
serverConn := c.conn
if serverConn != nil {
select {
case <-serverConn.done:
serverConn = nil
default:
return serverConn, nil
}
}
c.connAccess.Lock()
defer c.connAccess.Unlock()
serverConn = c.conn
if serverConn != nil {
select {
case <-serverConn.done:
serverConn = nil
default:
return serverConn, nil
}
}
udpConn, err := c.dialer.DialContext(c.ctx, "udp", c.peerAddr)
if err != nil {
return nil, &wireError{err}
}
c.conn = &wireConn{
Conn: udpConn,
done: make(chan struct{}),
}
return c.conn, nil
}
func (c *ClientBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
return []conn.ReceiveFunc{c.receive}, 0, nil
}
func (c *ClientBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
udpConn, err := c.connect()
if err != nil {
err = &wireError{err}
return
}
n, err = udpConn.Read(b)
if err != nil {
udpConn.Close()
err = &wireError{err}
}
ep = Endpoint(c.peerAddr)
return
}
func (c *ClientBind) Close() error {
c.connAccess.Lock()
defer c.connAccess.Unlock()
common.Close(common.PtrOrNil(c.conn))
return nil
}
func (c *ClientBind) SetMark(mark uint32) error {
return nil
}
func (c *ClientBind) Send(b []byte, ep conn.Endpoint) error {
udpConn, err := c.connect()
if err != nil {
return err
}
_, err = udpConn.Write(b)
if err != nil {
udpConn.Close()
}
return err
}
func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
return Endpoint(c.peerAddr), nil
}
func (c *ClientBind) Endpoint() conn.Endpoint {
return Endpoint(c.peerAddr)
}
type wireConn struct {
net.Conn
access sync.Mutex
done chan struct{}
}
func (w *wireConn) Close() error {
w.access.Lock()
defer w.access.Unlock()
select {
case <-w.done:
return net.ErrClosed
default:
}
w.Conn.Close()
close(w.done)
return nil
}

View File

@@ -0,0 +1,14 @@
package wireguard
import (
N "github.com/sagernet/sing/common/network"
"golang.zx2c4.com/wireguard/tun"
)
type Device interface {
tun.Device
N.Dialer
Start() error
// NewEndpoint() (stack.LinkEndpoint, error)
}

View File

@@ -0,0 +1,254 @@
//go:build !no_gvisor
package wireguard
import (
"context"
"net"
"net/netip"
"os"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"golang.zx2c4.com/wireguard/tun"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/header"
"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"
)
var _ Device = (*StackDevice)(nil)
const defaultNIC tcpip.NICID = 1
type StackDevice struct {
stack *stack.Stack
mtu uint32
events chan tun.Event
outbound chan *stack.PacketBuffer
dispatcher stack.NetworkDispatcher
done chan struct{}
addr4 tcpip.Address
addr6 tcpip.Address
}
func NewStackDevice(localAddresses []netip.Prefix, mtu uint32) (*StackDevice, error) {
ipStack := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6},
HandleLocal: true,
})
tunDevice := &StackDevice{
stack: ipStack,
mtu: mtu,
events: make(chan tun.Event, 1),
outbound: make(chan *stack.PacketBuffer, 256),
done: make(chan struct{}),
}
err := ipStack.CreateNIC(defaultNIC, (*wireEndpoint)(tunDevice))
if err != nil {
return nil, E.New(err.String())
}
for _, prefix := range localAddresses {
addr := tcpip.Address(prefix.Addr().AsSlice())
protoAddr := tcpip.ProtocolAddress{
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: addr,
PrefixLen: prefix.Bits(),
},
}
if prefix.Addr().Is4() {
tunDevice.addr4 = addr
protoAddr.Protocol = ipv4.ProtocolNumber
} else {
tunDevice.addr6 = addr
protoAddr.Protocol = ipv6.ProtocolNumber
}
err = ipStack.AddProtocolAddress(defaultNIC, protoAddr, stack.AddressProperties{})
if err != nil {
return nil, E.New("parse local address ", protoAddr.AddressWithPrefix, ": ", err.String())
}
}
sOpt := tcpip.TCPSACKEnabled(true)
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &sOpt)
cOpt := tcpip.CongestionControlOption("cubic")
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &cOpt)
ipStack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: defaultNIC})
ipStack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: defaultNIC})
return tunDevice, nil
}
func (w *StackDevice) NewEndpoint() (stack.LinkEndpoint, error) {
return (*wireEndpoint)(w), nil
}
func (w *StackDevice) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
addr := tcpip.FullAddress{
NIC: defaultNIC,
Port: destination.Port,
Addr: tcpip.Address(destination.Addr.AsSlice()),
}
bind := tcpip.FullAddress{
NIC: defaultNIC,
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = w.addr4
} else {
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = w.addr6
}
switch N.NetworkName(network) {
case N.NetworkTCP:
return gonet.DialTCPWithBind(ctx, w.stack, bind, addr, networkProtocol)
case N.NetworkUDP:
return gonet.DialUDP(w.stack, &bind, &addr, networkProtocol)
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
}
func (w *StackDevice) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
bind := tcpip.FullAddress{
NIC: defaultNIC,
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() || w.addr6 == "" {
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = w.addr4
} else {
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = w.addr6
}
return gonet.DialUDP(w.stack, &bind, nil, networkProtocol)
}
func (w *StackDevice) Start() error {
w.events <- tun.EventUp
return nil
}
func (w *StackDevice) File() *os.File {
return nil
}
func (w *StackDevice) Read(p []byte, offset int) (n int, err error) {
packetBuffer, ok := <-w.outbound
if !ok {
return 0, os.ErrClosed
}
defer packetBuffer.DecRef()
p = p[offset:]
for _, slice := range packetBuffer.AsSlices() {
n += copy(p[n:], slice)
}
return
}
func (w *StackDevice) Write(p []byte, offset int) (n int, err error) {
p = p[offset:]
if len(p) == 0 {
return
}
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(p) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
}
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithData(p),
})
defer packetBuffer.DecRef()
w.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
n = len(p)
return
}
func (w *StackDevice) Flush() error {
return nil
}
func (w *StackDevice) MTU() (int, error) {
return int(w.mtu), nil
}
func (w *StackDevice) Name() (string, error) {
return "sing-box", nil
}
func (w *StackDevice) Events() chan tun.Event {
return w.events
}
func (w *StackDevice) Close() error {
select {
case <-w.done:
return os.ErrClosed
default:
}
close(w.done)
w.stack.Close()
for _, endpoint := range w.stack.CleanupEndpoints() {
endpoint.Abort()
}
w.stack.Wait()
close(w.outbound)
return nil
}
var _ stack.LinkEndpoint = (*wireEndpoint)(nil)
type wireEndpoint StackDevice
func (ep *wireEndpoint) MTU() uint32 {
return ep.mtu
}
func (ep *wireEndpoint) MaxHeaderLength() uint16 {
return 0
}
func (ep *wireEndpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityNone
}
func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
ep.dispatcher = dispatcher
}
func (ep *wireEndpoint) IsAttached() bool {
return ep.dispatcher != nil
}
func (ep *wireEndpoint) Wait() {
}
func (ep *wireEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (ep *wireEndpoint) AddHeader(buffer *stack.PacketBuffer) {
}
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
for _, packetBuffer := range list.AsSlice() {
packetBuffer.IncRef()
ep.outbound <- packetBuffer
}
return list.Len(), nil
}

View File

@@ -0,0 +1,9 @@
//go:build no_gvisor
package wireguard
import "github.com/sagernet/sing-tun"
func NewStackDevice(localAddresses []netip.Prefix, mtu uint32) (Device, error) {
return nil, tun.ErrGVisorNotIncluded
}

View File

@@ -0,0 +1,110 @@
package wireguard
import (
"context"
"net"
"net/netip"
"os"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
wgTun "golang.zx2c4.com/wireguard/tun"
)
var _ Device = (*SystemDevice)(nil)
type SystemDevice struct {
dialer N.Dialer
device tun.Tun
name string
mtu int
events chan wgTun.Event
}
/*func (w *SystemDevice) NewEndpoint() (stack.LinkEndpoint, error) {
gTun, isGTun := w.device.(tun.GVisorTun)
if !isGTun {
return nil, tun.ErrGVisorUnsupported
}
return gTun.NewEndpoint()
}*/
func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes []netip.Prefix, mtu uint32) (*SystemDevice, error) {
var inet4Addresses []netip.Prefix
var inet6Addresses []netip.Prefix
for _, prefixes := range localPrefixes {
if prefixes.Addr().Is4() {
inet4Addresses = append(inet4Addresses, prefixes)
} else {
inet6Addresses = append(inet6Addresses, prefixes)
}
}
if interfaceName == "" {
interfaceName = tun.CalculateInterfaceName("wg")
}
tunInterface, err := tun.Open(tun.Options{
Name: interfaceName,
Inet4Address: inet4Addresses,
Inet6Address: inet6Addresses,
MTU: mtu,
})
if err != nil {
return nil, err
}
return &SystemDevice{
dialer.NewDefault(router, option.DialerOptions{
BindInterface: interfaceName,
}),
tunInterface, interfaceName, int(mtu), make(chan wgTun.Event),
}, nil
}
func (w *SystemDevice) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
return w.dialer.DialContext(ctx, network, destination)
}
func (w *SystemDevice) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
return w.dialer.ListenPacket(ctx, destination)
}
func (w *SystemDevice) Start() error {
w.events <- wgTun.EventUp
return nil
}
func (w *SystemDevice) File() *os.File {
return nil
}
func (w *SystemDevice) Read(bytes []byte, index int) (int, error) {
return w.device.Read(bytes[index-tunPacketOffset:])
}
func (w *SystemDevice) Write(bytes []byte, index int) (int, error) {
return w.device.Write(bytes[index:])
}
func (w *SystemDevice) Flush() error {
return nil
}
func (w *SystemDevice) MTU() (int, error) {
return w.mtu, nil
}
func (w *SystemDevice) Name() (string, error) {
return w.name, nil
}
func (w *SystemDevice) Events() chan wgTun.Event {
return w.events
}
func (w *SystemDevice) Close() error {
return w.device.Close()
}

View File

@@ -0,0 +1,37 @@
package wireguard
import (
"net/netip"
M "github.com/sagernet/sing/common/metadata"
"golang.zx2c4.com/wireguard/conn"
)
var _ conn.Endpoint = (*Endpoint)(nil)
type Endpoint M.Socksaddr
func (e Endpoint) ClearSrc() {
}
func (e Endpoint) SrcToString() string {
return ""
}
func (e Endpoint) DstToString() string {
return (M.Socksaddr)(e).String()
}
func (e Endpoint) DstToBytes() []byte {
b, _ := (M.Socksaddr)(e).AddrPort().MarshalBinary()
return b
}
func (e Endpoint) DstIP() netip.Addr {
return (M.Socksaddr)(e).Addr
}
func (e Endpoint) SrcIP() netip.Addr {
return netip.Addr{}
}

View File

@@ -0,0 +1,22 @@
package wireguard
import "net"
type wireError struct {
cause error
}
func (w *wireError) Error() string {
return w.cause.Error()
}
func (w *wireError) Timeout() bool {
if cause, causeNet := w.cause.(net.Error); causeNet {
return cause.Timeout()
}
return false
}
func (w *wireError) Temporary() bool {
return true
}

View File

@@ -0,0 +1,96 @@
package wireguard
import (
"io"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"golang.zx2c4.com/wireguard/conn"
)
var _ conn.Bind = (*ServerBind)(nil)
type ServerBind struct {
inbound chan serverPacket
done chan struct{}
writeBack N.PacketWriter
}
func NewServerBind(writeBack N.PacketWriter) *ServerBind {
return &ServerBind{
inbound: make(chan serverPacket, 256),
done: make(chan struct{}),
writeBack: writeBack,
}
}
func (s *ServerBind) Abort() error {
select {
case <-s.done:
return io.ErrClosedPipe
default:
close(s.done)
}
return nil
}
type serverPacket struct {
buffer *buf.Buffer
source M.Socksaddr
}
func (s *ServerBind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
fns = []conn.ReceiveFunc{s.receive}
return
}
func (s *ServerBind) receive(b []byte) (n int, ep conn.Endpoint, err error) {
select {
case packet := <-s.inbound:
defer packet.buffer.Release()
n = copy(b, packet.buffer.Bytes())
ep = Endpoint(packet.source)
return
case <-s.done:
err = io.ErrClosedPipe
return
}
}
func (s *ServerBind) WriteIsThreadUnsafe() {
}
func (s *ServerBind) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
select {
case s.inbound <- serverPacket{
buffer: buffer,
source: destination,
}:
return nil
case <-s.done:
return io.ErrClosedPipe
}
}
func (s *ServerBind) Close() error {
return nil
}
func (s *ServerBind) SetMark(mark uint32) error {
return nil
}
func (s *ServerBind) Send(b []byte, ep conn.Endpoint) error {
return s.writeBack.WritePacket(buf.As(b), M.Socksaddr(ep.(Endpoint)))
}
func (s *ServerBind) ParseEndpoint(addr string) (conn.Endpoint, error) {
destination := M.ParseSocksaddr(addr)
if !destination.IsValid() || destination.Port == 0 {
return nil, E.New("invalid endpoint: ", addr)
}
return Endpoint(destination), nil
}

View File

@@ -0,0 +1,3 @@
package wireguard
const tunPacketOffset = 4

View File

@@ -0,0 +1,5 @@
//go:build !darwin
package wireguard
const tunPacketOffset = 0