Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add -tcp-keep-alive and -tcp-fast-open flags #238

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/bin
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ go 1.16

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

var config struct {
Verbose bool
UDPTimeout time.Duration
TCPCork bool
Verbose bool
UDPTimeout time.Duration
TCPCork bool
TCPKeepAlive time.Duration
TCPFastOpen bool
TCPFastOpenQlen int
}

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

Expand Down
27 changes: 22 additions & 5 deletions tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net"
"os"
"sync"
"syscall"
"time"

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

// Listen on addr and proxy to server to reach target from getAddr.
func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func(net.Conn) (socks.Addr, error)) {
l, err := net.Listen("tcp", addr)
l, err := tcpListen(addr)
if err != nil {
logf("failed to listen on %s: %v", addr, err)
return
}

for {
c, err := l.Accept()
if err != nil {
Expand Down Expand Up @@ -68,7 +68,7 @@ func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func(
return
}

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

// Listen on addr for incoming connections.
func tcpRemote(addr string, shadow func(net.Conn) net.Conn) {
l, err := net.Listen("tcp", addr)
l, err := tcpListen(addr)
if err != nil {
logf("failed to listen on %s: %v", addr, err)
return
Expand Down Expand Up @@ -127,7 +127,7 @@ func tcpRemote(addr string, shadow func(net.Conn) net.Conn) {
return
}

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

// tcpDial opens a connecion socket
func tcpDial(addr string) (net.Conn, error) {
d := net.Dialer{
Control: func(network, address string, c syscall.RawConn) error {
var sockErr error
if err := c.Control(func(fd uintptr) { sockErr = tcpSetDialOpts(fd) }); err != nil {
return err
}
if sockErr != nil {
logf("failed to set up dialing socket: %s", sockErr)
}
return nil
},
}
return d.Dial("tcp", addr)
}

// relay copies between left and right bidirectionally
func relay(left, right net.Conn) error {
var err, err1 error
Expand Down
32 changes: 31 additions & 1 deletion tcp_darwin.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
package main

import (
"fmt"
"net"
"runtime"

"github.com/shadowsocks/go-shadowsocks2/pfutil"
"github.com/shadowsocks/go-shadowsocks2/socks"
"golang.org/x/sys/unix"
)

const (
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/bsd/netinet/tcp_var.h#L1483
TCP_FASTOPEN_SERVER = 1
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/bsd/netinet/tcp_var.h#L1484
TCP_FASTOPEN_CLIENT = 2
)

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

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

func natLookup(c net.Conn) (socks.Addr, error) {
Expand All @@ -22,3 +32,23 @@ func natLookup(c net.Conn) (socks.Addr, error) {
}
panic("not TCP connection")
}

// tcpSetListenOpts sets listening socket options.
func tcpSetListenOpts(fd uintptr) error {
if config.TCPFastOpen {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, TCP_FASTOPEN_SERVER); err != nil {
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
}
}
return nil
}

// tcpSetDialOpts sets dialing socket options.
func tcpSetDialOpts(fd uintptr) error {
if config.TCPFastOpen {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_FASTOPEN, TCP_FASTOPEN_CLIENT); err != nil {
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
}
}
return nil
}
22 changes: 22 additions & 0 deletions tcp_linux.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package main

import (
"fmt"
"net"

"github.com/shadowsocks/go-shadowsocks2/nfutil"
"github.com/shadowsocks/go-shadowsocks2/socks"
"golang.org/x/sys/unix"
)

func getOrigDst(c net.Conn, ipv6 bool) (socks.Addr, error) {
Expand All @@ -26,3 +28,23 @@ func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
logf("TCP6 redirect %s <-> %s", addr, server)
tcpLocal(addr, server, shadow, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, true) })
}

// tcpSetListenOpts sets listening socket options.
func tcpSetListenOpts(fd uintptr) error {
if config.TCPFastOpen {
if err := unix.SetsockoptInt(int(fd), unix.SOL_TCP, unix.TCP_FASTOPEN, config.TCPFastOpenQlen); err != nil {
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
}
}
return nil
}

// tcpSetDialOpts sets dialing socket options.
func tcpSetDialOpts(fd uintptr) error {
if config.TCPFastOpen {
if err := unix.SetsockoptInt(int(fd), unix.SOL_TCP, unix.TCP_FASTOPEN_CONNECT, 1); err != nil {
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
}
}
return nil
}
24 changes: 21 additions & 3 deletions tcp_other.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
// +build !linux,!darwin
//go:build !linux && !darwin && !windows
// +build !linux,!darwin,!windows

package main

import (
"net"
"runtime"
)

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

func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
logf("TCP6 redirect not supported")
logf("TCP6 redirect not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
}

// tcpSetListenOpts sets listening socket options.
func tcpSetListenOpts(fd uintptr) error {
if config.TCPFastOpen {
return fmt.Errorf("tcp-fast-open is not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
}
return nil
}

// tcpSetDialOpts sets dialing socket options.
func tcpSetDialOpts(fd uintptr) error {
if config.TCPFastOpen {
return fmt.Errorf("tcp-fast-open is not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
}
return nil
}
42 changes: 42 additions & 0 deletions tcp_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"fmt"
"net"
"runtime"

"golang.org/x/sys/windows"
)

const (
// https://github.com/shadowsocks/shadowsocks-libev/blob/89b5f987d6a5329de9713704615581d363f0cfed/src/winsock.h#L82
TCP_FASTOPEN = 15
)

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

func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) {
logf("TCP6 redirect not supported on %s-%s", runtime.GOOS, runtime.GOARCH)
}

// tcpSetListenOpts sets listening socket options.
func tcpSetListenOpts(fd uintptr) error {
if config.TCPFastOpen {
if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_TCP, TCP_FASTOPEN, 1); err != nil {
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
}
}
return nil
}

// tcpSetDialOpts sets dialing socket options.
func tcpSetDialOpts(fd uintptr) error {
if config.TCPFastOpen {
if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_TCP, TCP_FASTOPEN, 1); err != nil {
return fmt.Errorf("failed to set TCP_FASTOPEN: %s", err)
}
}
return nil
}
39 changes: 39 additions & 0 deletions tcplisten_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import (
"context"
"net"
"syscall"
)

// tcpListen binds a listening socket
func tcpListen(addr string) (net.Listener, error) {
var rawConn syscall.RawConn
lc := net.ListenConfig{
KeepAlive: config.TCPKeepAlive,
Control: func(network, address string, c syscall.RawConn) error {
rawConn = c
return nil
},
}

l, err := lc.Listen(context.Background(), "tcp", addr)
if err != nil {
return nil, err
}

// On MacOS we have to call Control() after the bind() and listen() are complete,
// otherwise setsockopt(TCP_FASTOPEN) fails with EINVAL (invalid argument).
// See https://github.com/h2o/h2o/commit/ec58f59f5e9a6c6a8a38087eb87fdc4b1763f080
var ctrlErr error
if err := rawConn.Control(func(fd uintptr) { ctrlErr = tcpSetListenOpts(fd) }); err != nil {
l.Close()
return nil, err
}

if ctrlErr != nil {
logf("failed to set up listening socket: %s", ctrlErr)
}

return l, nil
}
28 changes: 28 additions & 0 deletions tcplisten_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//go:build !darwin
// +build !darwin

package main

import (
"context"
"net"
"syscall"
)

// tcpListen binds a listening socket
func tcpListen(addr string) (net.Listener, error) {
lc := net.ListenConfig{
KeepAlive: config.TCPKeepAlive,
Control: func(network, address string, c syscall.RawConn) error {
var ctrlErr error
if err := c.Control(func(fd uintptr) { ctrlErr = tcpSetListenOpts(fd) }); err != nil {
return err
}
if ctrlErr != nil {
logf("failed to set up listening socket: %s", ctrlErr)
}
return nil
},
}
return lc.Listen(context.Background(), "tcp", addr)
}