diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 8e4d9354..778e5e56 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -114,6 +114,7 @@ type kcpTunOption struct { AutoExpire int `obfs:"autoexpire,omitempty"` ScavengeTTL int `obfs:"scavengettl,omitempty"` MTU int `obfs:"mtu,omitempty"` + RateLimit int `obfs:"ratelimit,omitempty"` SndWnd int `obfs:"sndwnd,omitempty"` RcvWnd int `obfs:"rcvwnd,omitempty"` DataShard int `obfs:"datashard,omitempty"` @@ -128,6 +129,7 @@ type kcpTunOption struct { SockBuf int `obfs:"sockbuf,omitempty"` SmuxVer int `obfs:"smuxver,omitempty"` SmuxBuf int `obfs:"smuxbuf,omitempty"` + FrameSize int `obfs:"framesize,omitempty"` StreamBuf int `obfs:"streambuf,omitempty"` KeepAlive int `obfs:"keepalive,omitempty"` } @@ -426,6 +428,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { AutoExpire: kcptunOpt.AutoExpire, ScavengeTTL: kcptunOpt.ScavengeTTL, MTU: kcptunOpt.MTU, + RateLimit: kcptunOpt.RateLimit, SndWnd: kcptunOpt.SndWnd, RcvWnd: kcptunOpt.RcvWnd, DataShard: kcptunOpt.DataShard, @@ -440,6 +443,7 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { SockBuf: kcptunOpt.SockBuf, SmuxVer: kcptunOpt.SmuxVer, SmuxBuf: kcptunOpt.SmuxBuf, + FrameSize: kcptunOpt.FrameSize, StreamBuf: kcptunOpt.StreamBuf, KeepAlive: kcptunOpt.KeepAlive, }) diff --git a/docs/config.yaml b/docs/config.yaml index b565fb03..1a2c2a90 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -558,12 +558,13 @@ proxies: # socks5 plugin: kcptun plugin-opts: key: it's a secrect # pre-shared secret between client and server - crypt: aes # aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none, null + crypt: aes # aes, aes-128, aes-128-gcm, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none, null mode: fast # profiles: fast3, fast2, fast, normal, manual conn: 1 # set num of UDP connections to server autoexpire: 0 # set auto expiration time(in seconds) for a single UDP connection, 0 to disable scavengettl: 600 # set how long an expired connection can live (in seconds) mtu: 1350 # set maximum transmission unit for UDP packets + ratelimit: 0 # set maximum outgoing speed (in bytes per second) for a single KCP connection, 0 to disable. Also known as packet pacing sndwnd: 128 # set send window size(num of packets) rcvwnd: 512 # set receive window size(num of packets) datashard: 10 # set reed-solomon erasure coding - datashard @@ -577,6 +578,7 @@ proxies: # socks5 sockbuf: 4194304 # per-socket buffer in bytes smuxver: 1 # specify smux version, available 1,2 smuxbuf: 4194304 # the overall de-mux buffer in bytes + framesize: 8192 # smux max frame size streambuf: 2097152 # per stream receive buffer in bytes, smux v2+ keepalive: 10 # seconds between heartbeats @@ -1404,11 +1406,12 @@ listeners: # kcp-tun: # enable: false # key: it's a secrect # pre-shared secret between client and server - # crypt: aes # aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none, null + # crypt: aes # aes, aes-128, aes-128-gcm, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none, null # mode: fast # profiles: fast3, fast2, fast, normal, manual # conn: 1 # set num of UDP connections to server # autoexpire: 0 # set auto expiration time(in seconds) for a single UDP connection, 0 to disable # scavengettl: 600 # set how long an expired connection can live (in seconds) + # ratelimit: 0 # set maximum outgoing speed (in bytes per second) for a single KCP connection, 0 to disable. Also known as packet pacing # mtu: 1350 # set maximum transmission unit for UDP packets # sndwnd: 128 # set send window size(num of packets) # rcvwnd: 512 # set receive window size(num of packets) @@ -1423,6 +1426,7 @@ listeners: # sockbuf: 4194304 # per-socket buffer in bytes # smuxver: 1 # specify smux version, available 1,2 # smuxbuf: 4194304 # the overall de-mux buffer in bytes + # framesize: 8192 # smux max frame size # streambuf: 2097152 # per stream receive buffer in bytes, smux v2+ # keepalive: 10 # seconds between heartbeats diff --git a/go.mod b/go.mod index c991e421..3177967d 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/fswatch v0.1.1 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/http v0.1.0 - github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9 + github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 github.com/metacubex/mlkem v0.1.0 github.com/metacubex/quic-go v0.57.1-0.20251217071004-e89f497a2e72 github.com/metacubex/randv2 v0.2.0 @@ -38,7 +38,7 @@ require ( github.com/metacubex/sing-tun v0.4.11 github.com/metacubex/sing-vmess v0.2.4 github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f - github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1 + github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 github.com/metacubex/tls v0.1.0 github.com/metacubex/utls v1.8.3 diff --git a/go.sum b/go.sum index 4786479f..e670096f 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,8 @@ github.com/metacubex/hpke v0.1.0 h1:gu2jUNhraehWi0P/z5HX2md3d7L1FhPQE6/Q0E9r9xQ= github.com/metacubex/hpke v0.1.0/go.mod h1:vfDm6gfgrwlXUxKDkWbcE44hXtmc1uxLDm2BcR11b3U= github.com/metacubex/http v0.1.0 h1:Jcy0I9zKjYijSUaksZU34XEe2xNdoFkgUTB7z7K5q0o= github.com/metacubex/http v0.1.0/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg= -github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9 h1:7m3tRPrLpKOLOvZ/Lp4XCxz0t7rg9t9K35x6TahjR8o= -github.com/metacubex/kcp-go v0.0.0-20251111012849-7455698490e9/go.mod h1:HIJZW4QMhbBqXuqC1ly6Hn0TEYT2SzRw58ns1yGhXTs= +github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI= +github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc= github.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I= github.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ= github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo= @@ -120,7 +120,6 @@ github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiL github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k= github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g= -github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= github.com/metacubex/sing v0.5.6 h1:mEPDCadsCj3DB8gn+t/EtposlYuALEkExa/LUguw6/c= github.com/metacubex/sing v0.5.6/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w= github.com/metacubex/sing-mux v0.3.4 h1:tf4r27CIkzaxq9kBlAXQkgMXq2HPp5Mta60Kb4RCZF0= @@ -139,8 +138,8 @@ github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkA github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80= -github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1 h1:a6DF0ze9miXes+rdwl8a4Wkvfpe0lXYU82sPJfDzz6s= -github.com/metacubex/smux v0.0.0-20251111013112-03f8d12dafc1/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE= +github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2BhiAPgbJygiWhesPlfGmF+9Vw6ARdk= +github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg= github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o= github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= github.com/metacubex/tls v0.1.0 h1:1kjR/1q2uU1cZIwiHYEnWzS4L+0Cu1/X3yfIQ76BzNY= @@ -183,16 +182,9 @@ github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= @@ -245,7 +237,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= diff --git a/listener/inbound/kcptun.go b/listener/inbound/kcptun.go index e4098f96..36c29298 100644 --- a/listener/inbound/kcptun.go +++ b/listener/inbound/kcptun.go @@ -14,6 +14,7 @@ type KcpTun struct { AutoExpire int `inbound:"autoexpire,omitempty"` ScavengeTTL int `inbound:"scavengettl,omitempty"` MTU int `inbound:"mtu,omitempty"` + RateLimit int `inbound:"ratelimit,omitempty"` SndWnd int `inbound:"sndwnd,omitempty"` RcvWnd int `inbound:"rcvwnd,omitempty"` DataShard int `inbound:"datashard,omitempty"` @@ -28,6 +29,7 @@ type KcpTun struct { SockBuf int `inbound:"sockbuf,omitempty"` SmuxVer int `inbound:"smuxver,omitempty"` SmuxBuf int `inbound:"smuxbuf,omitempty"` + FrameSize int `inbound:"framesize,omitempty"` StreamBuf int `inbound:"streambuf,omitempty"` KeepAlive int `inbound:"keepalive,omitempty"` } @@ -43,6 +45,7 @@ func (c KcpTun) Build() LC.KcpTun { AutoExpire: c.AutoExpire, ScavengeTTL: c.ScavengeTTL, MTU: c.MTU, + RateLimit: c.RateLimit, SndWnd: c.SndWnd, RcvWnd: c.RcvWnd, DataShard: c.DataShard, @@ -57,6 +60,7 @@ func (c KcpTun) Build() LC.KcpTun { SockBuf: c.SockBuf, SmuxVer: c.SmuxVer, SmuxBuf: c.SmuxBuf, + FrameSize: c.FrameSize, StreamBuf: c.StreamBuf, KeepAlive: c.KeepAlive, }, diff --git a/transport/kcptun/client.go b/transport/kcptun/client.go index 46731ca9..56d7f39e 100644 --- a/transport/kcptun/client.go +++ b/transport/kcptun/client.go @@ -70,6 +70,7 @@ func (c *Client) createConn(ctx context.Context, dial DialFn) (*smux.Session, er kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd) kcpconn.SetMtu(config.MTU) kcpconn.SetACKNoDelay(config.AckNodelay) + kcpconn.SetRateLimit(uint32(config.RateLimit)) _ = kcpconn.SetDSCP(config.DSCP) _ = kcpconn.SetReadBuffer(config.SockBuf) @@ -78,6 +79,7 @@ func (c *Client) createConn(ctx context.Context, dial DialFn) (*smux.Session, er smuxConfig.Version = config.SmuxVer smuxConfig.MaxReceiveBuffer = config.SmuxBuf smuxConfig.MaxStreamBuffer = config.StreamBuf + smuxConfig.MaxFrameSize = config.FrameSize smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second if smuxConfig.KeepAliveInterval >= smuxConfig.KeepAliveTimeout { smuxConfig.KeepAliveTimeout = 3 * smuxConfig.KeepAliveInterval diff --git a/transport/kcptun/common.go b/transport/kcptun/common.go index ec260f06..60e23953 100644 --- a/transport/kcptun/common.go +++ b/transport/kcptun/common.go @@ -26,6 +26,7 @@ type Config struct { AutoExpire int `json:"autoexpire"` ScavengeTTL int `json:"scavengettl"` MTU int `json:"mtu"` + RateLimit int `json:"ratelimit"` SndWnd int `json:"sndwnd"` RcvWnd int `json:"rcvwnd"` DataShard int `json:"datashard"` @@ -40,6 +41,7 @@ type Config struct { SockBuf int `json:"sockbuf"` SmuxVer int `json:"smuxver"` SmuxBuf int `json:"smuxbuf"` + FrameSize int `json:"framesize"` StreamBuf int `json:"streambuf"` KeepAlive int `json:"keepalive"` } @@ -87,6 +89,9 @@ func (config *Config) FillDefaults() { if config.SmuxBuf == 0 { config.SmuxBuf = 4194304 } + if config.FrameSize == 0 { + config.FrameSize = 8192 + } if config.StreamBuf == 0 { config.StreamBuf = 2097152 } @@ -144,6 +149,8 @@ func (config *Config) NewBlock() (block kcp.BlockCrypt) { block, _ = kcp.NewXTEABlockCrypt(pass[:16]) case "salsa20": block, _ = kcp.NewSalsa20BlockCrypt(pass) + case "aes-128-gcm": + block, _ = kcp.NewAESGCMCrypt(pass[:16]) default: config.Crypt = "aes" block, _ = kcp.NewAESBlockCrypt(pass) diff --git a/transport/kcptun/doc.go b/transport/kcptun/doc.go index e0801d28..fce889ec 100644 --- a/transport/kcptun/doc.go +++ b/transport/kcptun/doc.go @@ -1,5 +1,5 @@ // Package kcptun copy and modify from: -// https://github.com/xtaci/kcptun/tree/52492c72592627d0005cbedbc4ba37fc36a95c3f +// https://github.com/xtaci/kcptun/tree/f54f35175bed6ddda4e47aa35c9d7ae8b7e7eb85 // adopt for mihomo // without SM4,QPP,tcpraw support package kcptun diff --git a/transport/kcptun/server.go b/transport/kcptun/server.go index 5fcd440c..91d311ce 100644 --- a/transport/kcptun/server.go +++ b/transport/kcptun/server.go @@ -43,6 +43,7 @@ func (s *Server) Serve(pc net.PacketConn, handler func(net.Conn)) error { conn.SetMtu(s.config.MTU) conn.SetWindowSize(s.config.SndWnd, s.config.RcvWnd) conn.SetACKNoDelay(s.config.AckNodelay) + conn.SetRateLimit(uint32(s.config.RateLimit)) var netConn net.Conn = conn if !s.config.NoComp { @@ -55,6 +56,7 @@ func (s *Server) Serve(pc net.PacketConn, handler func(net.Conn)) error { smuxConfig.Version = s.config.SmuxVer smuxConfig.MaxReceiveBuffer = s.config.SmuxBuf smuxConfig.MaxStreamBuffer = s.config.StreamBuf + smuxConfig.MaxFrameSize = s.config.FrameSize smuxConfig.KeepAliveInterval = time.Duration(s.config.KeepAlive) * time.Second if smuxConfig.KeepAliveInterval >= smuxConfig.KeepAliveTimeout { smuxConfig.KeepAliveTimeout = 3 * smuxConfig.KeepAliveInterval