diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 3b21e383094c..ca7f866f2ece 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -391,11 +391,12 @@ type UdpHop struct { } type HysteriaConfig struct { - Version int32 `json:"version"` - Auth string `json:"auth"` - Up Bandwidth `json:"up"` - Down Bandwidth `json:"down"` - UdpHop UdpHop `json:"udphop"` + Version int32 `json:"version"` + Auth string `json:"auth"` + Congestion string `json:"congestion"` + Up Bandwidth `json:"up"` + Down Bandwidth `json:"down"` + UdpHop UdpHop `json:"udphop"` InitStreamReceiveWindow uint64 `json:"initStreamReceiveWindow"` MaxStreamReceiveWindow uint64 `json:"maxStreamReceiveWindow"` @@ -410,6 +411,7 @@ func (c *HysteriaConfig) Build() (proto.Message, error) { if c.Version != 2 { return nil, errors.New("version != 2") } + up, err := c.Up.Bps() if err != nil { return nil, err @@ -418,6 +420,12 @@ func (c *HysteriaConfig) Build() (proto.Message, error) { if err != nil { return nil, err } + + c.Congestion = strings.ToLower(c.Congestion) + if c.Congestion == "force-brutal" && up == 0 { + return nil, errors.New("force-brutal require up") + } + var hop *PortList if err := json.Unmarshal(c.UdpHop.PortList, &hop); err != nil { hop = &PortList{} @@ -455,6 +463,7 @@ func (c *HysteriaConfig) Build() (proto.Message, error) { config := &hysteria.Config{} config.Version = c.Version config.Auth = c.Auth + config.Congestion = c.Congestion config.Up = up config.Down = down config.Ports = hop.Build().Ports() diff --git a/proxy/hysteria/client.go b/proxy/hysteria/client.go index 4e98544358d9..6325261afef0 100644 --- a/proxy/hysteria/client.go +++ b/proxy/hysteria/client.go @@ -17,6 +17,7 @@ import ( "github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/policy" + hyCtx "github.com/xtls/xray-core/proxy/hysteria/ctx" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/hysteria" @@ -55,6 +56,9 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter ob.CanSpliceCopy = 3 target := ob.Target + if target.Network == net.Network_UDP { + hyCtx.ContextWithRequireDatagram(ctx) + } conn, err := dialer.Dial(ctx, c.server.Destination) if err != nil { return errors.New("failed to find an available destination").AtWarning().Base(err) diff --git a/proxy/hysteria/ctx/ctx.go b/proxy/hysteria/ctx/ctx.go new file mode 100644 index 000000000000..6c03a101f981 --- /dev/null +++ b/proxy/hysteria/ctx/ctx.go @@ -0,0 +1,20 @@ +package ctx + +import ( + "context" +) + +type key int + +const ( + requireDatagram key = iota +) + +func ContextWithRequireDatagram(ctx context.Context) context.Context { + return context.WithValue(ctx, requireDatagram, struct{}{}) +} + +func RequireDatagramFromContext(ctx context.Context) bool { + _, ok := ctx.Value(requireDatagram).(struct{}) + return ok +} diff --git a/transport/internet/hysteria/config.pb.go b/transport/internet/hysteria/config.pb.go index 5e453c3005bb..8ec3a88e9cb6 100644 --- a/transport/internet/hysteria/config.pb.go +++ b/transport/internet/hysteria/config.pb.go @@ -25,17 +25,18 @@ type Config struct { state protoimpl.MessageState `protogen:"open.v1"` Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` Auth string `protobuf:"bytes,2,opt,name=auth,proto3" json:"auth,omitempty"` - Up uint64 `protobuf:"varint,3,opt,name=up,proto3" json:"up,omitempty"` - Down uint64 `protobuf:"varint,4,opt,name=down,proto3" json:"down,omitempty"` - Ports []uint32 `protobuf:"varint,5,rep,packed,name=ports,proto3" json:"ports,omitempty"` - Interval int64 `protobuf:"varint,6,opt,name=interval,proto3" json:"interval,omitempty"` - InitStreamReceiveWindow uint64 `protobuf:"varint,7,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"` - MaxStreamReceiveWindow uint64 `protobuf:"varint,8,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"` - InitConnReceiveWindow uint64 `protobuf:"varint,9,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"` - MaxConnReceiveWindow uint64 `protobuf:"varint,10,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"` - MaxIdleTimeout int64 `protobuf:"varint,11,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"` - KeepAlivePeriod int64 `protobuf:"varint,12,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"` - DisablePathMtuDiscovery bool `protobuf:"varint,13,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"` + Congestion string `protobuf:"bytes,3,opt,name=congestion,proto3" json:"congestion,omitempty"` + Up uint64 `protobuf:"varint,4,opt,name=up,proto3" json:"up,omitempty"` + Down uint64 `protobuf:"varint,5,opt,name=down,proto3" json:"down,omitempty"` + Ports []uint32 `protobuf:"varint,6,rep,packed,name=ports,proto3" json:"ports,omitempty"` + Interval int64 `protobuf:"varint,7,opt,name=interval,proto3" json:"interval,omitempty"` + InitStreamReceiveWindow uint64 `protobuf:"varint,8,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"` + MaxStreamReceiveWindow uint64 `protobuf:"varint,9,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"` + InitConnReceiveWindow uint64 `protobuf:"varint,10,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"` + MaxConnReceiveWindow uint64 `protobuf:"varint,11,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"` + MaxIdleTimeout int64 `protobuf:"varint,12,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"` + KeepAlivePeriod int64 `protobuf:"varint,13,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"` + DisablePathMtuDiscovery bool `protobuf:"varint,14,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -84,6 +85,13 @@ func (x *Config) GetAuth() string { return "" } +func (x *Config) GetCongestion() string { + if x != nil { + return x.Congestion + } + return "" +} + func (x *Config) GetUp() uint64 { if x != nil { return x.Up @@ -165,22 +173,25 @@ var File_transport_internet_hysteria_config_proto protoreflect.FileDescriptor const file_transport_internet_hysteria_config_proto_rawDesc = "" + "\n" + - "(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\x87\x04\n" + + "(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\xa7\x04\n" + "\x06Config\x12\x18\n" + "\aversion\x18\x01 \x01(\x05R\aversion\x12\x12\n" + - "\x04auth\x18\x02 \x01(\tR\x04auth\x12\x0e\n" + - "\x02up\x18\x03 \x01(\x04R\x02up\x12\x12\n" + - "\x04down\x18\x04 \x01(\x04R\x04down\x12\x14\n" + - "\x05ports\x18\x05 \x03(\rR\x05ports\x12\x1a\n" + - "\binterval\x18\x06 \x01(\x03R\binterval\x12;\n" + - "\x1ainit_stream_receive_window\x18\a \x01(\x04R\x17initStreamReceiveWindow\x129\n" + - "\x19max_stream_receive_window\x18\b \x01(\x04R\x16maxStreamReceiveWindow\x127\n" + - "\x18init_conn_receive_window\x18\t \x01(\x04R\x15initConnReceiveWindow\x125\n" + - "\x17max_conn_receive_window\x18\n" + - " \x01(\x04R\x14maxConnReceiveWindow\x12(\n" + - "\x10max_idle_timeout\x18\v \x01(\x03R\x0emaxIdleTimeout\x12*\n" + - "\x11keep_alive_period\x18\f \x01(\x03R\x0fkeepAlivePeriod\x12;\n" + - "\x1adisable_path_mtu_discovery\x18\r \x01(\bR\x17disablePathMtuDiscoveryB\x82\x01\n" + + "\x04auth\x18\x02 \x01(\tR\x04auth\x12\x1e\n" + + "\n" + + "congestion\x18\x03 \x01(\tR\n" + + "congestion\x12\x0e\n" + + "\x02up\x18\x04 \x01(\x04R\x02up\x12\x12\n" + + "\x04down\x18\x05 \x01(\x04R\x04down\x12\x14\n" + + "\x05ports\x18\x06 \x03(\rR\x05ports\x12\x1a\n" + + "\binterval\x18\a \x01(\x03R\binterval\x12;\n" + + "\x1ainit_stream_receive_window\x18\b \x01(\x04R\x17initStreamReceiveWindow\x129\n" + + "\x19max_stream_receive_window\x18\t \x01(\x04R\x16maxStreamReceiveWindow\x127\n" + + "\x18init_conn_receive_window\x18\n" + + " \x01(\x04R\x15initConnReceiveWindow\x125\n" + + "\x17max_conn_receive_window\x18\v \x01(\x04R\x14maxConnReceiveWindow\x12(\n" + + "\x10max_idle_timeout\x18\f \x01(\x03R\x0emaxIdleTimeout\x12*\n" + + "\x11keep_alive_period\x18\r \x01(\x03R\x0fkeepAlivePeriod\x12;\n" + + "\x1adisable_path_mtu_discovery\x18\x0e \x01(\bR\x17disablePathMtuDiscoveryB\x82\x01\n" + "$com.xray.transport.internet.hysteriaP\x01Z5github.com/xtls/xray-core/transport/internet/hysteria\xaa\x02 Xray.Transport.Internet.Hysteriab\x06proto3" var ( diff --git a/transport/internet/hysteria/config.proto b/transport/internet/hysteria/config.proto index 221133380d05..787a2b403697 100644 --- a/transport/internet/hysteria/config.proto +++ b/transport/internet/hysteria/config.proto @@ -9,17 +9,18 @@ option java_multiple_files = true; message Config { int32 version = 1; string auth = 2; - uint64 up = 3; - uint64 down = 4; - repeated uint32 ports = 5; - int64 interval = 6; + string congestion = 3; + uint64 up = 4; + uint64 down = 5; + repeated uint32 ports = 6; + int64 interval = 7; - uint64 init_stream_receive_window = 7; - uint64 max_stream_receive_window = 8; - uint64 init_conn_receive_window = 9; - uint64 max_conn_receive_window = 10; - int64 max_idle_timeout = 11; - int64 keep_alive_period = 12; - bool disable_path_mtu_discovery = 13; + uint64 init_stream_receive_window = 8; + uint64 max_stream_receive_window = 9; + uint64 init_conn_receive_window = 10; + uint64 max_conn_receive_window = 11; + int64 max_idle_timeout = 12; + int64 keep_alive_period = 13; + bool disable_path_mtu_discovery = 14; } diff --git a/transport/internet/hysteria/conn.go b/transport/internet/hysteria/conn.go index fe1259bb8f88..28c20fec80d5 100644 --- a/transport/internet/hysteria/conn.go +++ b/transport/internet/hysteria/conn.go @@ -64,6 +64,9 @@ func (i *InterUdpConn) Read(p []byte) (int, error) { return 0, io.EOF } n := copy(p, b) + if n != len(b) { + return 0, io.ErrShortBuffer + } return n, nil } diff --git a/transport/internet/hysteria/dialer.go b/transport/internet/hysteria/dialer.go index 5a694aa4236b..d4723a1b5d0b 100644 --- a/transport/internet/hysteria/dialer.go +++ b/transport/internet/hysteria/dialer.go @@ -16,8 +16,8 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/task" + hyCtx "github.com/xtls/xray-core/proxy/hysteria/ctx" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/finalmask" "github.com/xtls/xray-core/transport/internet/hysteria/congestion" @@ -115,8 +115,8 @@ type client struct { conn *quic.Conn config *Config tlsConfig *go_tls.Config - udpmaskManager *finalmask.UdpmaskManager socketConfig *internet.SocketConfig + udpmaskManager *finalmask.UdpmaskManager udpSM *udpSessionManager mutex sync.Mutex } @@ -243,10 +243,25 @@ func (c *client) dial() error { serverAuto := resp.Header.Get(CommonHeaderCCRX) serverDown, _ := strconv.ParseUint(serverAuto, 10, 64) - if serverAuto == "auto" || c.config.Up == 0 || serverDown == 0 { + switch c.config.Congestion { + case "reno": + errors.LogDebug(c.ctx, "congestion reno") + case "bbr": + errors.LogDebug(c.ctx, "congestion bbr") congestion.UseBBR(quicConn) - } else { - congestion.UseBrutal(quicConn, min(c.config.Up, serverDown)) + case "brutal", "": + if serverAuto == "auto" || c.config.Up == 0 || serverDown == 0 { + errors.LogDebug(c.ctx, "congestion bbr") + congestion.UseBBR(quicConn) + } else { + errors.LogDebug(c.ctx, "congestion brutal bytes per second ", min(c.config.Up, serverDown)) + congestion.UseBrutal(quicConn, min(c.config.Up, serverDown)) + } + case "force-brutal": + errors.LogDebug(c.ctx, "congestion brutal bytes per second ", c.config.Up) + congestion.UseBrutal(quicConn, c.config.Up) + default: + errors.LogDebug(c.ctx, "congestion reno") } c.pktConn = pktConn @@ -363,6 +378,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me return nil, errors.New("tls config is nil") } + requireDatagram := hyCtx.RequireDatagramFromContext(ctx) addr := dest.NetAddr() config := streamSettings.ProtocolSettings.(*Config) @@ -375,18 +391,15 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me dest: dest, config: config, tlsConfig: tlsConfig.GetTLSConfig(), - udpmaskManager: streamSettings.UdpmaskManager, socketConfig: streamSettings.SocketSettings, + udpmaskManager: streamSettings.UdpmaskManager, } manger.m[addr] = c } c.setCtx(ctx) manger.mutex.Unlock() - outbounds := session.OutboundsFromContext(ctx) - targetUdp := len(outbounds) > 0 && outbounds[len(outbounds)-1].Target.Network == net.Network_UDP - - if targetUdp { + if requireDatagram { return c.udp() } return c.tcp()