kcp: add fake TCP support

This commit is contained in:
ginuerzh 2020-01-11 18:43:54 +08:00
parent 62a17f07f7
commit c5fc56d151
3 changed files with 64 additions and 57 deletions

View File

@ -139,6 +139,13 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if config == nil {
conf := gost.DefaultKCPConfig
if node.GetBool("tcp") {
conf.TCP = true
}
config = &conf
}
tr = gost.KCPTransporter(config) tr = gost.KCPTransporter(config)
case "ssh": case "ssh":
if node.Protocol == "direct" || node.Protocol == "remote" { if node.Protocol == "direct" || node.Protocol == "remote" {
@ -322,6 +329,13 @@ func (r *route) GenRouters() ([]router, error) {
if er != nil { if er != nil {
return nil, er return nil, er
} }
if config == nil {
conf := gost.DefaultKCPConfig
if node.GetBool("tcp") {
conf.TCP = true
}
config = &conf
}
ln, err = gost.KCPListener(node.Addr, config) ln, err = gost.KCPListener(node.Addr, config)
case "ssh": case "ssh":
config := &gost.SSHConfig{ config := &gost.SSHConfig{

34
ftcp.go
View File

@ -99,11 +99,7 @@ func (l *fakeTCPListener) listenLoop() {
conn, ok := l.connMap.Get(raddr.String()) conn, ok := l.connMap.Get(raddr.String())
if !ok { if !ok {
cc := &fakeTCPConn{ conn = newUDPServerConn(l.ln, raddr, l.config.TTL, l.config.QueueSize)
raddr: raddr,
PacketConn: l.ln,
}
conn = newUDPServerConn(cc, raddr, l.config.TTL, l.config.QueueSize)
conn.onClose = func() { conn.onClose = func() {
l.connMap.Delete(raddr.String()) l.connMap.Delete(raddr.String())
log.Logf("[ftcp] %s closed (%d)", raddr, l.connMap.Size()) log.Logf("[ftcp] %s closed (%d)", raddr, l.connMap.Size())
@ -157,7 +153,6 @@ func (l *fakeTCPListener) Close() error {
} }
type fakeTCPConn struct { type fakeTCPConn struct {
mss int
raddr net.Addr raddr net.Addr
net.PacketConn net.PacketConn
} }
@ -171,33 +166,6 @@ func (c *fakeTCPConn) Write(b []byte) (n int, err error) {
return c.WriteTo(b, c.raddr) return c.WriteTo(b, c.raddr)
} }
func (c *fakeTCPConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
mss := c.mss
if mss <= 0 {
mss = 1460
}
for {
if len(b) == 0 {
break
}
var nn int
if len(b) <= mss {
nn, err = c.PacketConn.WriteTo(b, addr)
n += nn
break
}
nn, err = c.PacketConn.WriteTo(b[:mss], addr)
n += nn
if err != nil {
break
}
b = b[mss:]
}
return
}
func (c *fakeTCPConn) RemoteAddr() net.Addr { func (c *fakeTCPConn) RemoteAddr() net.Addr {
return c.raddr return c.raddr
} }

73
kcp.go
View File

@ -15,6 +15,7 @@ import (
"github.com/go-log/log" "github.com/go-log/log"
"github.com/klauspost/compress/snappy" "github.com/klauspost/compress/snappy"
"github.com/xtaci/tcpraw"
"gopkg.in/xtaci/kcp-go.v4" "gopkg.in/xtaci/kcp-go.v4"
"gopkg.in/xtaci/smux.v1" "gopkg.in/xtaci/smux.v1"
) )
@ -46,6 +47,7 @@ type KCPConfig struct {
SnmpLog string `json:"snmplog"` SnmpLog string `json:"snmplog"`
SnmpPeriod int `json:"snmpperiod"` SnmpPeriod int `json:"snmpperiod"`
Signal bool `json:"signal"` // Signal enables the signal SIGUSR1 feature. Signal bool `json:"signal"` // Signal enables the signal SIGUSR1 feature.
TCP bool `json:"tcp"`
} }
// Init initializes the KCP config. // Init initializes the KCP config.
@ -85,6 +87,7 @@ var (
SnmpLog: "", SnmpLog: "",
SnmpPeriod: 60, SnmpPeriod: 60,
Signal: false, Signal: false,
TCP: false,
} }
) )
@ -129,13 +132,24 @@ func (tr *kcpTransporter) Dial(addr string, options ...DialOption) (conn net.Con
ok = false ok = false
} }
if !ok { if !ok {
timeout := opts.Timeout raddr, err := net.ResolveUDPAddr("udp", addr)
if timeout <= 0 {
timeout = DialTimeout
}
conn, err = net.DialTimeout("udp", addr, timeout)
if err != nil { if err != nil {
return return nil, err
}
if tr.config.TCP {
pc, err := tcpraw.Dial("tcp", addr)
if err != nil {
return nil, err
}
conn = &fakeTCPConn{
raddr: raddr,
PacketConn: pc,
}
} else {
conn, err = net.ListenUDP("udp", nil)
if err != nil {
return nil, err
}
} }
session = &muxSession{conn: conn} session = &muxSession{conn: conn}
tr.sessions[addr] = session tr.sessions[addr] = session
@ -184,14 +198,14 @@ func (tr *kcpTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (
} }
func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPConfig) (*muxSession, error) { func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPConfig) (*muxSession, error) {
udpConn, ok := conn.(*net.UDPConn) pc, ok := conn.(net.PacketConn)
if !ok { if !ok {
return nil, errors.New("kcp: wrong connection type") return nil, errors.New("kcp: wrong connection type")
} }
kcpconn, err := kcp.NewConn(addr, kcpconn, err := kcp.NewConn(addr,
blockCrypt(config.Key, config.Crypt, KCPSalt), blockCrypt(config.Key, config.Crypt, KCPSalt),
config.DataShard, config.ParityShard, &connectedUDPConn{udpConn}) config.DataShard, config.ParityShard, pc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -203,9 +217,11 @@ func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPCon
kcpconn.SetMtu(config.MTU) kcpconn.SetMtu(config.MTU)
kcpconn.SetACKNoDelay(config.AckNodelay) kcpconn.SetACKNoDelay(config.AckNodelay)
// if err := kcpconn.SetDSCP(config.DSCP); err != nil { if config.DSCP > 0 {
// log.Log("[kcp]", err) if err := kcpconn.SetDSCP(config.DSCP); err != nil {
// } log.Log("[kcp]", err)
}
}
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil { if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
log.Log("[kcp]", err) log.Log("[kcp]", err)
} }
@ -247,14 +263,31 @@ func KCPListener(addr string, config *KCPConfig) (Listener, error) {
} }
config.Init() config.Init()
ln, err := kcp.ListenWithOptions(addr, var err error
blockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard) var ln *kcp.Listener
if config.TCP {
var conn net.PacketConn
conn, err = tcpraw.Listen("tcp", addr)
if err != nil {
return nil, err
}
ln, err = kcp.ServeConn(
blockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard, conn)
if err != nil {
return nil, err
}
} else {
ln, err = kcp.ListenWithOptions(addr,
blockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
// if err = ln.SetDSCP(config.DSCP); err != nil { if config.DSCP > 0 {
// log.Log("[kcp]", err) if err = ln.SetDSCP(config.DSCP); err != nil {
// } log.Log("[kcp]", err)
}
}
if err = ln.SetReadBuffer(config.SockBuf); err != nil { if err = ln.SetReadBuffer(config.SockBuf); err != nil {
log.Log("[kcp]", err) log.Log("[kcp]", err)
} }
@ -468,11 +501,3 @@ func (c *compStreamConn) SetReadDeadline(t time.Time) error {
func (c *compStreamConn) SetWriteDeadline(t time.Time) error { func (c *compStreamConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t) return c.conn.SetWriteDeadline(t)
} }
// connectedUDPConn is a wrapper for net.UDPConn which converts WriteTo syscalls
// to Write syscalls that are 4 times faster on some OS'es. This should only be
// used for connections that were produced by a net.Dial* call.
type connectedUDPConn struct{ *net.UDPConn }
// WriteTo redirects all writes to the Write syscall, which is 4 times faster.
func (c *connectedUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) { return c.Write(b) }