diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index b4458096b458..eb90f849ed9d 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -112,6 +112,8 @@ func (c *KCPConfig) Build() (proto.Message, error) { type TCPConfig struct { HeaderConfig json.RawMessage `json:"header"` AcceptProxyProtocol bool `json:"acceptProxyProtocol"` + Rate uint64 `json:"rate"` + Cwnd uint32 `json:"cwnd"` } // Build implements Buildable. @@ -131,6 +133,8 @@ func (c *TCPConfig) Build() (proto.Message, error) { if c.AcceptProxyProtocol { config.AcceptProxyProtocol = c.AcceptProxyProtocol } + config.Rate = c.Rate + config.Cwnd = c.Cwnd return config, nil } diff --git a/transport/internet/tcp/config.pb.go b/transport/internet/tcp/config.pb.go index f773f63b3bdf..cac0eea6c9ca 100644 --- a/transport/internet/tcp/config.pb.go +++ b/transport/internet/tcp/config.pb.go @@ -26,6 +26,8 @@ type Config struct { state protoimpl.MessageState `protogen:"open.v1"` HeaderSettings *serial.TypedMessage `protobuf:"bytes,2,opt,name=header_settings,json=headerSettings,proto3" json:"header_settings,omitempty"` AcceptProxyProtocol bool `protobuf:"varint,3,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"` + Rate uint64 `protobuf:"varint,4,opt,name=rate,proto3" json:"rate,omitempty"` + Cwnd uint32 `protobuf:"varint,5,opt,name=cwnd,proto3" json:"cwnd,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -74,14 +76,30 @@ func (x *Config) GetAcceptProxyProtocol() bool { return false } +func (x *Config) GetRate() uint64 { + if x != nil { + return x.Rate + } + return 0 +} + +func (x *Config) GetCwnd() uint32 { + if x != nil { + return x.Cwnd + } + return 0 +} + var File_transport_internet_tcp_config_proto protoreflect.FileDescriptor const file_transport_internet_tcp_config_proto_rawDesc = "" + "\n" + - "#transport/internet/tcp/config.proto\x12\x1bxray.transport.internet.tcp\x1a!common/serial/typed_message.proto\"\x8d\x01\n" + + "#transport/internet/tcp/config.proto\x12\x1bxray.transport.internet.tcp\x1a!common/serial/typed_message.proto\"\xb5\x01\n" + "\x06Config\x12I\n" + "\x0fheader_settings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\x0eheaderSettings\x122\n" + - "\x15accept_proxy_protocol\x18\x03 \x01(\bR\x13acceptProxyProtocolJ\x04\b\x01\x10\x02Bs\n" + + "\x15accept_proxy_protocol\x18\x03 \x01(\bR\x13acceptProxyProtocol\x12\x12\n" + + "\x04rate\x18\x04 \x01(\x04R\x04rate\x12\x12\n" + + "\x04cwnd\x18\x05 \x01(\rR\x04cwndJ\x04\b\x01\x10\x02Bs\n" + "\x1fcom.xray.transport.internet.tcpP\x01Z0github.com/xtls/xray-core/transport/internet/tcp\xaa\x02\x1bXray.Transport.Internet.Tcpb\x06proto3" var ( diff --git a/transport/internet/tcp/config.proto b/transport/internet/tcp/config.proto index a12acdfaab87..ee62a8ca8100 100644 --- a/transport/internet/tcp/config.proto +++ b/transport/internet/tcp/config.proto @@ -12,4 +12,6 @@ message Config { reserved 1; xray.common.serial.TypedMessage header_settings = 2; bool accept_proxy_protocol = 3; + uint64 rate = 4; + uint32 cwnd = 5; } diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 759dfc35a6b7..3642308ddbc2 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -108,6 +108,12 @@ func (v *Listener) keepAccepting() { } continue } + if v.config.Rate > 0 { + err := SetBrutalRate(conn, v.config.Rate, v.config.Cwnd) + if err != nil { + errors.LogDebugInner(context.Background(), err, "failed to apply socket options to incoming connection") + } + } go func() { if v.tlsConfig != nil { conn = tls.Server(conn, v.tlsConfig) diff --git a/transport/internet/tcp/sockopt_darwin.go b/transport/internet/tcp/sockopt_darwin.go index ec0da29a88cc..10dcec4e59f8 100644 --- a/transport/internet/tcp/sockopt_darwin.go +++ b/transport/internet/tcp/sockopt_darwin.go @@ -24,3 +24,7 @@ func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { } return dest, nil } + +func SetBrutalRate(conn stat.Connection, rate uint64, cwnd uint32) error { + return nil +} diff --git a/transport/internet/tcp/sockopt_freebsd.go b/transport/internet/tcp/sockopt_freebsd.go index d7ab3ff0129e..913cb4abb5e2 100644 --- a/transport/internet/tcp/sockopt_freebsd.go +++ b/transport/internet/tcp/sockopt_freebsd.go @@ -24,3 +24,7 @@ func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { } return dest, nil } + +func SetBrutalRate(conn stat.Connection, rate uint64, cwnd uint32) error { + return nil +} diff --git a/transport/internet/tcp/sockopt_linux.go b/transport/internet/tcp/sockopt_linux.go index f5648e8fc0e7..8450ae6012f9 100644 --- a/transport/internet/tcp/sockopt_linux.go +++ b/transport/internet/tcp/sockopt_linux.go @@ -5,6 +5,7 @@ package tcp import ( "context" + "encoding/binary" "syscall" "unsafe" @@ -50,3 +51,36 @@ func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { } return dest, nil } + +func SetBrutalRate(conn stat.Connection, rate uint64, cwnd uint32) error { + sys, ok := conn.(syscall.Conn) + if !ok { + return errors.New("unable to get syscall.Conn") + } + sysConn, err := sys.SyscallConn() + if err != nil { + return errors.New("failed to get sys fd").Base(err) + } + err = sysConn.Control(func(fd uintptr) { + if err := syscall.SetsockoptString(int(fd), syscall.IPPROTO_TCP, syscall.TCP_CONGESTION, "brutal"); err != nil { + errors.LogDebugInner(context.Background(), err, "failed to set CustomSockoptString ", syscall.TCP_CONGESTION, " ", "brutal") + return + } + + if cwnd == 0 { + cwnd = 15 + } + + buf := make([]byte, 16) + binary.LittleEndian.PutUint64(buf[0:], rate) + binary.LittleEndian.PutUint32(buf[8:], cwnd) + if err := syscall.SetsockoptString(int(fd), syscall.IPPROTO_TCP, 23301, string(buf)); err != nil { + errors.LogDebugInner(context.Background(), err, "failed to set CustomSockoptString 23301 ", buf) + return + } + }) + if err != nil { + return errors.New("failed to control connection").Base(err) + } + return nil +} diff --git a/transport/internet/tcp/sockopt_linux_test.go b/transport/internet/tcp/sockopt_linux_test.go index 8210cb4be34c..68d9d39b1987 100644 --- a/transport/internet/tcp/sockopt_linux_test.go +++ b/transport/internet/tcp/sockopt_linux_test.go @@ -4,9 +4,13 @@ package tcp_test import ( + "bytes" "context" + "encoding/binary" + "fmt" "strings" "testing" + unsafe "unsafe" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/testing/servers/tcp" @@ -31,3 +35,31 @@ func TestGetOriginalDestination(t *testing.T) { t.Error("unexpected state") } } + +func TestSockoptParams(t *testing.T) { + type BrutalParams struct { + rate uint64 + cwnd_gain uint32 + } + + params := BrutalParams{ + rate: 15 * 1024 * 1024 / 8, + cwnd_gain: 15, + } + + raw := unsafe.Slice( + (*byte)(unsafe.Pointer(¶ms)), + unsafe.Sizeof(params), + ) + + buf := make([]byte, 16) + binary.LittleEndian.PutUint64(buf, 15*1024*1024/8) + binary.LittleEndian.PutUint32(buf[8:], 15) + + fmt.Println(len(raw), raw) + fmt.Println(len(buf), buf) + + if !bytes.Equal(raw, buf) { + t.Fatal("!bytes.Equal(raw, buf)") + } +} diff --git a/transport/internet/tcp/sockopt_other.go b/transport/internet/tcp/sockopt_other.go index 3f657354a946..cabf0a83f6cf 100644 --- a/transport/internet/tcp/sockopt_other.go +++ b/transport/internet/tcp/sockopt_other.go @@ -11,3 +11,7 @@ import ( func GetOriginalDestination(conn stat.Connection) (net.Destination, error) { return net.Destination{}, nil } + +func SetBrutalRate(conn stat.Connection, rate uint64, cwnd uint32) error { + return nil +}