Skip to content

Commit 2afe703

Browse files
committed
Add -tcp-keep-alive and -tcp-fast-open flags
1 parent 71a35d3 commit 2afe703

11 files changed

+231
-22
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/bin

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ go 1.16
44

55
require (
66
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3
7-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
7+
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
8+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
89
)

go.sum

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
22
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
3-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
4-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
5-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
6-
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
7-
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
8-
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
9-
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
10-
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
11-
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
3+
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
4+
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
5+
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
6+
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
7+
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
8+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
9+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
10+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
11+
golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49 h1:TMjZDarEwf621XDryfitp/8awEhiZNiwgphKlTMGRIg=
12+
golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
13+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
14+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
15+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
16+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
17+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

main.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ import (
1919
)
2020

2121
var config struct {
22-
Verbose bool
23-
UDPTimeout time.Duration
24-
TCPCork bool
22+
Verbose bool
23+
UDPTimeout time.Duration
24+
TCPCork bool
25+
TCPKeepAlive time.Duration
26+
TCPFastOpen bool
27+
TCPFastOpenQlen int
2528
}
2629

2730
func main() {
@@ -63,6 +66,9 @@ func main() {
6366
flag.BoolVar(&flags.UDP, "udp", false, "(server-only) enable UDP support")
6467
flag.BoolVar(&flags.TCP, "tcp", true, "(server-only) enable TCP support")
6568
flag.BoolVar(&config.TCPCork, "tcpcork", false, "coalesce writing first few packets")
69+
flag.DurationVar(&config.TCPKeepAlive, "tcp-keep-alive", time.Duration(0), "TCP Keep Alive timeout")
70+
flag.BoolVar(&config.TCPFastOpen, "tcp-fast-open", false, "Enable TCP Fast Open (TFO)")
71+
flag.IntVar(&config.TCPFastOpenQlen, "tcp-fast-open-qlen", 4096, "TFO requests queue size (linux only)")
6672
flag.DurationVar(&config.UDPTimeout, "udptimeout", 5*time.Minute, "UDP tunnel timeout")
6773
flag.Parse()
6874

tcp.go

+22-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net"
99
"os"
1010
"sync"
11+
"syscall"
1112
"time"
1213

1314
"github.com/shadowsocks/go-shadowsocks2/socks"
@@ -32,12 +33,11 @@ func tcpTun(addr, server, target string, shadow func(net.Conn) net.Conn) {
3233

3334
// Listen on addr and proxy to server to reach target from getAddr.
3435
func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func(net.Conn) (socks.Addr, error)) {
35-
l, err := net.Listen("tcp", addr)
36+
l, err := tcpListen(addr)
3637
if err != nil {
3738
logf("failed to listen on %s: %v", addr, err)
3839
return
3940
}
40-
4141
for {
4242
c, err := l.Accept()
4343
if err != nil {
@@ -68,7 +68,7 @@ func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func(
6868
return
6969
}
7070

71-
rc, err := net.Dial("tcp", server)
71+
rc, err := tcpDial(server)
7272
if err != nil {
7373
logf("failed to connect to server %v: %v", server, err)
7474
return
@@ -94,7 +94,7 @@ func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func(
9494

9595
// Listen on addr for incoming connections.
9696
func tcpRemote(addr string, shadow func(net.Conn) net.Conn) {
97-
l, err := net.Listen("tcp", addr)
97+
l, err := tcpListen(addr)
9898
if err != nil {
9999
logf("failed to listen on %s: %v", addr, err)
100100
return
@@ -127,7 +127,7 @@ func tcpRemote(addr string, shadow func(net.Conn) net.Conn) {
127127
return
128128
}
129129

130-
rc, err := net.Dial("tcp", tgt.String())
130+
rc, err := tcpDial(tgt.String())
131131
if err != nil {
132132
logf("failed to connect to target: %v", err)
133133
return
@@ -142,6 +142,23 @@ func tcpRemote(addr string, shadow func(net.Conn) net.Conn) {
142142
}
143143
}
144144

145+
// tcpDial opens a connecion socket
146+
func tcpDial(addr string) (net.Conn, error) {
147+
d := net.Dialer{
148+
Control: func(network, address string, c syscall.RawConn) error {
149+
var sockErr error
150+
if err := c.Control(func(fd uintptr) { sockErr = tcpSetDialOpts(fd) }); err != nil {
151+
return err
152+
}
153+
if sockErr != nil {
154+
logf("failed to set up dialing socket: %s", sockErr)
155+
}
156+
return nil
157+
},
158+
}
159+
return d.Dial("tcp", addr)
160+
}
161+
145162
// relay copies between left and right bidirectionally
146163
func relay(left, right net.Conn) error {
147164
var err, err1 error

tcp_darwin.go

+31-1
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
package main
22

33
import (
4+
"fmt"
45
"net"
6+
"runtime"
57

68
"github.com/shadowsocks/go-shadowsocks2/pfutil"
79
"github.com/shadowsocks/go-shadowsocks2/socks"
10+
"golang.org/x/sys/unix"
11+
)
12+
13+
const (
14+
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/bsd/netinet/tcp_var.h#L1483
15+
TCP_FASTOPEN_SERVER = 1
16+
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/bsd/netinet/tcp_var.h#L1484
17+
TCP_FASTOPEN_CLIENT = 2
818
)
919

1020
func redirLocal(addr, server string, shadow func(net.Conn) net.Conn) {
1121
tcpLocal(addr, server, shadow, natLookup)
1222
}
1323

1424
func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
15-
panic("TCP6 redirect not supported")
25+
logf("TCP6 redirect not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
1626
}
1727

1828
func natLookup(c net.Conn) (socks.Addr, error) {
@@ -22,3 +32,23 @@ func natLookup(c net.Conn) (socks.Addr, error) {
2232
}
2333
panic("not TCP connection")
2434
}
35+
36+
// tcpSetListenOpts sets listening socket options.
37+
func tcpSetListenOpts(fd uintptr) error {
38+
if config.TCPFastOpen {
39+
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, TCP_FASTOPEN_SERVER); err != nil {
40+
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
41+
}
42+
}
43+
return nil
44+
}
45+
46+
// tcpSetDialOpts sets dialing socket options.
47+
func tcpSetDialOpts(fd uintptr) error {
48+
if config.TCPFastOpen {
49+
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, TCP_FASTOPEN_CLIENT); err != nil {
50+
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
51+
}
52+
}
53+
return nil
54+
}

tcp_linux.go

+22
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package main
22

33
import (
4+
"fmt"
45
"net"
56

67
"github.com/shadowsocks/go-shadowsocks2/nfutil"
78
"github.com/shadowsocks/go-shadowsocks2/socks"
9+
"golang.org/x/sys/unix"
810
)
911

1012
func getOrigDst(c net.Conn, ipv6 bool) (socks.Addr, error) {
@@ -26,3 +28,23 @@ func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
2628
logf("TCP6 redirect %s <-> %s", addr, server)
2729
tcpLocal(addr, server, shadow, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, true) })
2830
}
31+
32+
// tcpSetListenOpts sets listening socket options.
33+
func tcpSetListenOpts(fd uintptr) error {
34+
if config.TCPFastOpen {
35+
if err := unix.SetsockoptInt(int(fd), unix.SOL_TCP, unix.TCP_FASTOPEN, config.TCPFastOpenQlen); err != nil {
36+
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
37+
}
38+
}
39+
return nil
40+
}
41+
42+
// tcpSetDialOpts sets dialing socket options.
43+
func tcpSetDialOpts(fd uintptr) error {
44+
if config.TCPFastOpen {
45+
if err := unix.SetsockoptInt(int(fd), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT, 1); err != nil {
46+
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
47+
}
48+
}
49+
return nil
50+
}

tcp_other.go

+21-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,33 @@
1-
// +build !linux,!darwin
1+
//go:build !linux && !darwin && !windows
2+
// +build !linux,!darwin,!windows
23

34
package main
45

56
import (
67
"net"
8+
"runtime"
79
)
810

911
func redirLocal(addr, server string, shadow func(net.Conn) net.Conn) {
10-
logf("TCP redirect not supported")
12+
logf("TCP redirect not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
1113
}
1214

1315
func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
14-
logf("TCP6 redirect not supported")
16+
logf("TCP6 redirect not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
17+
}
18+
19+
// tcpSetListenOpts sets listening socket options.
20+
func tcpSetListenOpts(fd uintptr) error {
21+
if config.TCPFastOpen {
22+
return fmt.Errorf("tcp-fast-open is not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
23+
}
24+
return nil
25+
}
26+
27+
// tcpSetDialOpts sets dialing socket options.
28+
func tcpSetDialOpts(fd uintptr) error {
29+
if config.TCPFastOpen {
30+
return fmt.Errorf("tcp-fast-open is not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
31+
}
32+
return nil
1533
}

tcp_windows.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net"
6+
7+
"golang.org/x/sys/windows"
8+
)
9+
10+
const (
11+
// https://github.com/shadowsocks/shadowsocks-libev/blob/89b5f987d6a5329de9713704615581d363f0cfed/src/winsock.h#L82
12+
TCP_FASTOPEN = 15
13+
)
14+
15+
func redirLocal(addr, server string, shadow func(net.Conn) net.Conn) {
16+
logf("TCP redirect not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
17+
}
18+
19+
func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
20+
logf("TCP6 redirect not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
21+
}
22+
23+
// tcpSetListenOpts sets listening socket options.
24+
func tcpSetListenOpts(fd uintptr) error {
25+
if config.TCPFastOpen {
26+
if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_TCP, TCP_FASTOPEN, 1); err != nil {
27+
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
28+
}
29+
}
30+
return nil
31+
}
32+
33+
// tcpSetDialOpts sets dialing socket options.
34+
func tcpSetDialOpts(fd uintptr) error {
35+
if config.TCPFastOpen {
36+
if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_TCP, TCP_FASTOPEN, 1); err != nil {
37+
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
38+
}
39+
}
40+
return nil
41+
}

tcplisten_darwin.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"net"
6+
"syscall"
7+
)
8+
9+
// tcpListen binds a listening socket
10+
func tcpListen(addr string) (net.Listener, error) {
11+
var rawConn syscall.RawConn
12+
lc := net.ListenConfig{
13+
KeepAlive: config.TCPKeepAlive,
14+
Control: func(network, address string, c syscall.RawConn) error {
15+
rawConn = c
16+
return nil
17+
},
18+
}
19+
20+
l, err := lc.Listen(context.Background(), "tcp", addr)
21+
if err != nil {
22+
return nil, err
23+
}
24+
25+
// On MacOS we have to call Control() after the bind() and listen() are complete,
26+
// otherwise setsockopt(TCP_FASTOPEN) fails with EINVAL (invalid argument).
27+
// See https://github.com/h2o/h2o/commit/ec58f59f5e9a6c6a8a38087eb87fdc4b1763f080
28+
var ctrlErr error
29+
if err := rawConn.Control(func(fd uintptr) { ctrlErr = tcpSetListenOpts(fd) }); err != nil {
30+
l.Close()
31+
return nil, err
32+
}
33+
34+
if ctrlErr != nil {
35+
logf("failed to set up listening socket: %s", ctrlErr)
36+
}
37+
38+
return l, err
39+
}

tcplisten_other.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//go:build !darwin
2+
// +build !darwin
3+
4+
package main
5+
6+
import (
7+
"context"
8+
"net"
9+
"syscall"
10+
)
11+
12+
// tcpListen binds a listening socket
13+
func tcpListen(addr string) (net.Listener, error) {
14+
lc := net.ListenConfig{
15+
KeepAlive: config.TCPKeepAlive,
16+
Control: func(network, address string, c syscall.RawConn) error {
17+
var ctrlErr error
18+
if err := c.Control(func(fd uintptr) { ctrlErr = tcpSetListenOpts(fd) }); err != nil {
19+
return err
20+
}
21+
if ctrlErr != nil {
22+
logf("failed to set up listening socket: %s", ctrlErr)
23+
}
24+
return nil
25+
},
26+
}
27+
return lc.Listen(context.Background(), "tcp", addr)
28+
}

0 commit comments

Comments
 (0)