Repository: v2fly/v2ray-core
Pull request #3067 (2024-07-01 05:22:42)

Add pprof flag for debugging
This commit adds a pprof flag to the application, which allows enabling or disabling pprof for debugging purposes. This change is necessary to facilitate easier performance profiling during development and debugging sessions.

Related issue: #1806


Patch:
diff --git a/app/dns/nameserver_doh.go b/app/dns/nameserver_doh.go
index 6e1430e6..b1889c16 100644
--- a/app/dns/nameserver_doh.go
+++ b/app/dns/nameserver_doh.go
@@ -11,7 +11,6 @@ import (
 	"net/http"
 	"net/url"
 	"sync"
-	"sync/atomic"
 	"time"
 
 	"golang.org/x/net/dns/dnsmessage"
@@ -35,7 +34,6 @@ type DoHNameServer struct {
 	ips        map[string]record
 	pub        *pubsub.Service
 	cleanup    *task.Periodic
-	reqID      uint32
 	httpClient *http.Client
 	dohURL     string
 	name       string
@@ -204,7 +202,7 @@ func (s *DoHNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
 }
 
 func (s *DoHNameServer) newReqID() uint16 {
-	return uint16(atomic.AddUint32(&s.reqID, 1))
+	return 0
 }
 
 func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
diff --git a/app/dns/nameserver_quic.go b/app/dns/nameserver_quic.go
index 28e69225..7275cb8a 100644
--- a/app/dns/nameserver_quic.go
+++ b/app/dns/nameserver_quic.go
@@ -6,7 +6,6 @@ import (
 	"encoding/binary"
 	"net/url"
 	"sync"
-	"sync/atomic"
 	"time"
 
 	"github.com/quic-go/quic-go"
@@ -35,7 +34,6 @@ type QUICNameServer struct {
 	ips         map[string]record
 	pub         *pubsub.Service
 	cleanup     *task.Periodic
-	reqID       uint32
 	name        string
 	destination net.Destination
 	connection  quic.Connection
@@ -149,7 +147,7 @@ func (s *QUICNameServer) updateIP(req *dnsRequest, ipRec *IPRecord) {
 }
 
 func (s *QUICNameServer) newReqID() uint16 {
-	return uint16(atomic.AddUint32(&s.reqID, 1))
+	return 0
 }
 
 func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
diff --git a/app/router/config.go b/app/router/config.go
index c1ac9745..5af1ea75 100644
--- a/app/router/config.go
+++ b/app/router/config.go
@@ -144,7 +144,7 @@ func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatch
 		return &Balancer{
 			selectors: br.OutboundSelector,
 			strategy:  &LeastPingStrategy{config: s},
-			ohm:       ohm,
+			ohm:       ohm, fallbackTag: br.FallbackTag,
 		}, nil
 	case "leastload":
 		i, err := serial.GetInstanceOf(br.StrategySettings)
diff --git a/common/net/system.go b/common/net/system.go
index cb5657e9..e205e422 100644
--- a/common/net/system.go
+++ b/common/net/system.go
@@ -14,6 +14,7 @@ var (
 	DialUDP         = net.DialUDP
 	DialUnix        = net.DialUnix
 	FileConn        = net.FileConn
+	FileListener    = net.FileListener
 	Listen          = net.Listen
 	ListenTCP       = net.ListenTCP
 	ListenUDP       = net.ListenUDP
diff --git a/core.go b/core.go
index 1b8c4b8c..e810c75d 100644
--- a/core.go
+++ b/core.go
@@ -18,7 +18,7 @@ import (
 )
 
 var (
-	version  = "5.16.1"
+	version  = "5.17.1"
 	build    = "Custom"
 	codename = "V2Fly, a community-driven edition of V2Ray."
 	intro    = "A unified platform for anti-censorship."
diff --git a/go.mod b/go.mod
index 514c5987..57395110 100644
--- a/go.mod
+++ b/go.mod
@@ -5,24 +5,26 @@ go 1.21
 toolchain go1.21.4
 
 require (
-	github.com/adrg/xdg v0.4.0
-	github.com/go-chi/chi/v5 v5.0.12
+	github.com/adrg/xdg v0.5.0
+	github.com/go-chi/chi/v5 v5.1.0
 	github.com/go-chi/render v1.0.3
-	github.com/go-playground/validator/v10 v10.20.0
+	github.com/go-playground/validator/v10 v10.22.0
+	github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259
 	github.com/golang/mock v1.6.0
 	github.com/golang/protobuf v1.5.4
 	github.com/google/go-cmp v0.6.0
 	github.com/google/gopacket v1.1.19
-	github.com/gorilla/websocket v1.5.1
+	github.com/gorilla/websocket v1.5.3
 	github.com/jhump/protoreflect v1.16.0
 	github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
-	github.com/miekg/dns v1.1.59
+	github.com/miekg/dns v1.1.62
 	github.com/mustafaturan/bus v1.0.2
 	github.com/pelletier/go-toml v1.9.5
-	github.com/pion/transport/v2 v2.2.5
+	github.com/pion/dtls/v2 v2.2.12
+	github.com/pion/transport/v2 v2.2.10
 	github.com/pires/go-proxyproto v0.7.0
-	github.com/quic-go/quic-go v0.43.0
-	github.com/refraction-networking/utls v1.6.5
+	github.com/quic-go/quic-go v0.46.0
+	github.com/refraction-networking/utls v1.6.7
 	github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
 	github.com/stretchr/testify v1.9.0
 	github.com/v2fly/BrowserBridge v0.0.0-20210430233438-0570fc1d7d08
@@ -32,12 +34,12 @@ require (
 	github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432
 	go.starlark.net v0.0.0-20230612165344-9532f5667272
 	go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
-	golang.org/x/crypto v0.22.0
-	golang.org/x/net v0.24.0
-	golang.org/x/sync v0.7.0
-	golang.org/x/sys v0.19.0
-	google.golang.org/grpc v1.63.2
-	google.golang.org/protobuf v1.34.0
+	golang.org/x/crypto v0.26.0
+	golang.org/x/net v0.28.0
+	golang.org/x/sync v0.8.0
+	golang.org/x/sys v0.24.0
+	google.golang.org/grpc v1.65.0
+	google.golang.org/protobuf v1.34.2
 	gopkg.in/yaml.v3 v3.0.1
 	gvisor.dev/gvisor v0.0.0-20231020174304-b8a429915ff1
 	h12.io/socks v1.0.3
@@ -67,21 +69,21 @@ require (
 	github.com/mustafaturan/monoton v1.0.0 // indirect
 	github.com/onsi/ginkgo/v2 v2.10.0 // indirect
 	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
-	github.com/pion/dtls/v2 v2.2.7 // indirect
 	github.com/pion/logging v0.2.2 // indirect
 	github.com/pion/randutil v0.1.0 // indirect
 	github.com/pion/sctp v1.8.7 // indirect
+	github.com/pion/transport/v3 v3.0.7 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
 	github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 // indirect
 	github.com/xtaci/smux v1.5.24 // indirect
 	go.uber.org/mock v0.4.0 // indirect
-	golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
-	golang.org/x/mod v0.16.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
+	golang.org/x/mod v0.18.0 // indirect
+	golang.org/x/text v0.17.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
-	golang.org/x/tools v0.19.0 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
+	golang.org/x/tools v0.22.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
 )
 
 replace github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 => github.com/xiaokangwang/struc v0.0.0-20231031203518-0e381172f248
diff --git a/go.sum b/go.sum
index 262e14c6..e559d9ac 100644
--- a/go.sum
+++ b/go.sum
@@ -15,8 +15,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/FlowerWrong/water v0.0.0-20180301012659-01a4eaa1f6f2/go.mod h1:xrG5L7lq7T2DLnPr2frMnL906CNEoKRwLB+VYFhPq2w=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
-github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
+github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
+github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
 github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 h1:+JkXLHME8vLJafGhOH4aoV2Iu8bR55nU6iKMVfYVLjY=
 github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1/go.mod h1:nuudZmJhzWtx2212z+pkuy7B6nkBqa+xwNXZHL1j8cg=
 github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
@@ -69,8 +69,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
 github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
 github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
-github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
+github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
 github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
 github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -85,13 +85,14 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
-github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
+github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
 github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259 h1:ZHJ7+IGpuOXtVf6Zk/a3WuHQgkC+vXwaqfUBDFwahtI=
 github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259/go.mod h1:9Qcha0gTWLw//0VNka1Cbnjvg3pNKGFdAm7E9sBabxE=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -139,8 +140,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
 github.com/gopherjs/gopherjs v0.0.0-20210420193930-a4630ec28c79/go.mod h1:Opf9rtYVq0eTyX+aRVmRO9hE8ERAozcdrBxWG9Q6mkQ=
 github.com/gopherjs/websocket v0.0.0-20191103002815-9a42957e2b3a/go.mod h1:jd+zY81Fx2lC4bfw58+Rflg1srqmedQjbBUejKOjYNY=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
-github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
@@ -199,8 +200,8 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
-github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
+github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
+github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -233,8 +234,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
 github.com/pion/dtls/v2 v2.0.0-rc.7/go.mod h1:U199DvHpRBN0muE9+tVN4TMy1jvEhZIZ63lk4xkvVSk=
-github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
-github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
+github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
+github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
 github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
 github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
 github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
@@ -244,9 +245,11 @@ github.com/pion/sctp v1.8.7 h1:JnABvFakZueGAn4KU/4PSKg+GWbF6QWbKTWZOSGJjXw=
 github.com/pion/sctp v1.8.7/go.mod h1:g1Ul+ARqZq5JEmoFy87Q/4CePtKnTJ1QCL9dBBdN6AU=
 github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
 github.com/pion/transport/v2 v2.1.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ=
-github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
-github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=
-github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
+github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
+github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q=
+github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
+github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
+github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
 github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
 github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -265,10 +268,10 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/quic-go/quic-go v0.43.0 h1:sjtsTKWX0dsHpuMJvLxGqoQdtgJnbAPWY+W+5vjYW/g=
-github.com/quic-go/quic-go v0.43.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
-github.com/refraction-networking/utls v1.6.5 h1:Jlfqgs/t1Uy6FHHQ8Fz9ZTrRmP/zS7d/NZw7BLahaL8=
-github.com/refraction-networking/utls v1.6.5/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
+github.com/quic-go/quic-go v0.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y=
+github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
+github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
+github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
 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=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -306,11 +309,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -327,6 +328,7 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF
 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
 github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
 github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
+github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432 h1:I/ATawgO2RerCq9ACwL0wBB8xNXZdE3J+93MCEHReRs=
 github.com/xiaokangwang/VLite v0.0.0-20220418190619-cff95160a432/go.mod h1:QN7Go2ftTVfx0aCTh9RXHV8pkpi0FtmbwQw40dy61wQ=
@@ -362,17 +364,17 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
-golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
-golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
+golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
-golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -392,8 +394,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
-golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
+golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -415,11 +417,11 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
-golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
-golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
+golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -432,8 +434,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -458,7 +460,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -467,16 +468,17 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
-golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
+golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
+golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -486,8 +488,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
+golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
@@ -516,8 +519,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
-golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
+golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
+golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -541,15 +544,15 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
-google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
+google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
+google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -558,8 +561,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
 google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4=
-google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/infra/conf/cfgcommon/socketcfg/socket.go b/infra/conf/cfgcommon/socketcfg/socket.go
index ab6996f7..940c0d82 100644
--- a/infra/conf/cfgcommon/socketcfg/socket.go
+++ b/infra/conf/cfgcommon/socketcfg/socket.go
@@ -18,6 +18,7 @@ type SocketConfig struct {
 	RxBufSize            uint64 `json:"rxBufSize"`
 	TxBufSize            uint64 `json:"txBufSize"`
 	ForceBufSize         bool   `json:"forceBufSize"`
+	MPTCP                *bool  `json:"mptcp"`
 }
 
 // Build implements Buildable.
@@ -46,6 +47,15 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
 		tproxy = internet.SocketConfig_Off
 	}
 
+	var mptcpSettings internet.MPTCPState
+	if c.MPTCP != nil {
+		if *c.MPTCP {
+			mptcpSettings = internet.MPTCPState_Enable
+		} else {
+			mptcpSettings = internet.MPTCPState_Disable
+		}
+	}
+
 	return &internet.SocketConfig{
 		Mark:                 c.Mark,
 		Tfo:                  tfoSettings,
@@ -58,5 +68,6 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
 		TxBufSize:            int64(c.TxBufSize),
 		ForceBufSize:         c.ForceBufSize,
 		BindToDevice:         c.BindToDevice,
+		Mptcp:                mptcpSettings,
 	}, nil
 }
diff --git a/infra/conf/merge/rules.go b/infra/conf/merge/rules.go
index 5ba3f909..51ccf2e6 100644
--- a/infra/conf/merge/rules.go
+++ b/infra/conf/merge/rules.go
@@ -4,6 +4,8 @@
 
 package merge
 
+import "fmt"
+
 const (
 	priorityKey string = "_priority"
 	tagKey      string = "_tag"
@@ -11,7 +13,7 @@ const (
 
 // ApplyRules applies merge rules according to _tag, _priority fields, and remove them
 func ApplyRules(m map[string]interface{}) error {
-	err := sortMergeSlices(m)
+	err := sortMergeSlices(m, "")
 	if err != nil {
 		return err
 	}
@@ -20,22 +22,22 @@ func ApplyRules(m map[string]interface{}) error {
 }
 
 // sortMergeSlices enumerates all slices in a map, to sort by priority and merge by tag
-func sortMergeSlices(target map[string]interface{}) error {
+func sortMergeSlices(target map[string]interface{}, path string) error {
 	for key, value := range target {
 		if slice, ok := value.([]interface{}); ok {
 			sortByPriority(slice)
-			s, err := mergeSameTag(slice)
+			s, err := mergeSameTag(slice, fmt.Sprintf("%s.%s", path, key))
 			if err != nil {
 				return err
 			}
 			target[key] = s
 			for _, item := range s {
 				if m, ok := item.(map[string]interface{}); ok {
-					sortMergeSlices(m)
+					sortMergeSlices(m, fmt.Sprintf("%s.%s[]", path, key))
 				}
 			}
 		} else if field, ok := value.(map[string]interface{}); ok {
-			sortMergeSlices(field)
+			sortMergeSlices(field, fmt.Sprintf("%s.%s", path, key))
 		}
 	}
 	return nil
diff --git a/infra/conf/merge/tag.go b/infra/conf/merge/tag.go
index 6c2632e7..3850d551 100644
--- a/infra/conf/merge/tag.go
+++ b/infra/conf/merge/tag.go
@@ -4,10 +4,16 @@
 
 package merge
 
-func getTag(v map[string]interface{}) string {
-	if field, ok := v["tag"]; ok {
-		if t, ok := field.(string); ok {
-			return t
+import (
+	"strings"
+)
+
+func getTag(v map[string]interface{}, tagKeyOnly bool) string {
+	if !tagKeyOnly {
+		if field, ok := v["tag"]; ok {
+			if t, ok := field.(string); ok {
+				return t
+			}
 		}
 	}
 	if field, ok := v[tagKey]; ok {
@@ -18,16 +24,21 @@ func getTag(v map[string]interface{}) string {
 	return ""
 }
 
-func mergeSameTag(s []interface{}) ([]interface{}, error) {
+func mergeSameTag(s []interface{}, path string) ([]interface{}, error) {
 	// from: [a,"",b,"",a,"",b,""]
 	// to: [a,"",b,"",merged,"",merged,""]
 	merged := &struct{}{}
+	tagKeyOnly := false
+	// See https://github.com/v2fly/v2ray-core/issues/2981
+	if strings.HasPrefix(path, ".dns.servers") {
+		tagKeyOnly = true
+	}
 	for i, item1 := range s {
 		map1, ok := item1.(map[string]interface{})
 		if !ok {
 			continue
 		}
-		tag1 := getTag(map1)
+		tag1 := getTag(map1, tagKeyOnly)
 		if tag1 == "" {
 			continue
 		}
@@ -36,7 +47,7 @@ func mergeSameTag(s []interface{}) ([]interface{}, error) {
 			if !ok {
 				continue
 			}
-			tag2 := getTag(map2)
+			tag2 := getTag(map2, tagKeyOnly)
 			if tag1 == tag2 {
 				s[j] = merged
 				err := mergeMaps(map1, map2)
diff --git a/infra/conf/v4/shadowsocks.go b/infra/conf/v4/shadowsocks.go
index ecfc506e..e58458f6 100644
--- a/infra/conf/v4/shadowsocks.go
+++ b/infra/conf/v4/shadowsocks.go
@@ -3,6 +3,7 @@ package v4
 import (
 	"github.com/golang/protobuf/proto"
 
+	"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
 	"github.com/v2fly/v2ray-core/v5/common/protocol"
 	"github.com/v2fly/v2ray-core/v5/common/serial"
 	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
@@ -10,13 +11,14 @@ import (
 )
 
 type ShadowsocksServerConfig struct {
-	Cipher      string                 `json:"method"`
-	Password    string                 `json:"password"`
-	UDP         bool                   `json:"udp"`
-	Level       byte                   `json:"level"`
-	Email       string                 `json:"email"`
-	NetworkList *cfgcommon.NetworkList `json:"network"`
-	IVCheck     bool                   `json:"ivCheck"`
+	Cipher         string                 `json:"method"`
+	Password       string                 `json:"password"`
+	UDP            bool                   `json:"udp"`
+	Level          byte                   `json:"level"`
+	Email          string                 `json:"email"`
+	NetworkList    *cfgcommon.NetworkList `json:"network"`
+	IVCheck        bool                   `json:"ivCheck"`
+	PacketEncoding string                 `json:"packetEncoding"`
 }
 
 func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
@@ -42,6 +44,13 @@ func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
 		Account: serial.ToTypedMessage(account),
 	}
 
+	switch v.PacketEncoding {
+	case "Packet":
+		config.PacketEncoding = packetaddr.PacketAddrType_Packet
+	case "", "None":
+		config.PacketEncoding = packetaddr.PacketAddrType_None
+	}
+
 	return config, nil
 }
 
diff --git a/infra/conf/v4/socks.go b/infra/conf/v4/socks.go
index 2a875419..367eb361 100644
--- a/infra/conf/v4/socks.go
+++ b/infra/conf/v4/socks.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/golang/protobuf/proto"
 
+	"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
 	"github.com/v2fly/v2ray-core/v5/common/protocol"
 	"github.com/v2fly/v2ray-core/v5/common/serial"
 	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
@@ -30,12 +31,13 @@ const (
 )
 
 type SocksServerConfig struct {
-	AuthMethod string             `json:"auth"`
-	Accounts   []*SocksAccount    `json:"accounts"`
-	UDP        bool               `json:"udp"`
-	Host       *cfgcommon.Address `json:"ip"`
-	Timeout    uint32             `json:"timeout"`
-	UserLevel  uint32             `json:"userLevel"`
+	AuthMethod     string             `json:"auth"`
+	Accounts       []*SocksAccount    `json:"accounts"`
+	UDP            bool               `json:"udp"`
+	Host           *cfgcommon.Address `json:"ip"`
+	Timeout        uint32             `json:"timeout"`
+	UserLevel      uint32             `json:"userLevel"`
+	PacketEncoding string             `json:"packetEncoding"`
 }
 
 func (v *SocksServerConfig) Build() (proto.Message, error) {
@@ -64,6 +66,14 @@ func (v *SocksServerConfig) Build() (proto.Message, error) {
 
 	config.Timeout = v.Timeout
 	config.UserLevel = v.UserLevel
+
+	switch v.PacketEncoding {
+	case "Packet":
+		config.PacketEncoding = packetaddr.PacketAddrType_Packet
+	case "", "None":
+		config.PacketEncoding = packetaddr.PacketAddrType_None
+	}
+
 	return config, nil
 }
 
diff --git a/infra/conf/v4/trojan.go b/infra/conf/v4/trojan.go
index 56551da2..ac339e80 100644
--- a/infra/conf/v4/trojan.go
+++ b/infra/conf/v4/trojan.go
@@ -11,6 +11,7 @@ import (
 	"github.com/golang/protobuf/proto"
 
 	"github.com/v2fly/v2ray-core/v5/common/net"
+	"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
 	"github.com/v2fly/v2ray-core/v5/common/protocol"
 	"github.com/v2fly/v2ray-core/v5/common/serial"
 	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
@@ -91,9 +92,10 @@ type TrojanUserConfig struct {
 
 // TrojanServerConfig is Inbound configuration
 type TrojanServerConfig struct {
-	Clients   []*TrojanUserConfig      `json:"clients"`
-	Fallback  json.RawMessage          `json:"fallback"`
-	Fallbacks []*TrojanInboundFallback `json:"fallbacks"`
+	Clients        []*TrojanUserConfig      `json:"clients"`
+	Fallback       json.RawMessage          `json:"fallback"`
+	Fallbacks      []*TrojanInboundFallback `json:"fallbacks"`
+	PacketEncoding string                   `json:"packetEncoding"`
 }
 
 // Build implements Buildable
@@ -167,5 +169,12 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
 		}
 	}
 
+	switch c.PacketEncoding {
+	case "Packet":
+		config.PacketEncoding = packetaddr.PacketAddrType_Packet
+	case "", "None":
+		config.PacketEncoding = packetaddr.PacketAddrType_None
+	}
+
 	return config, nil
 }
diff --git a/infra/conf/v4/v2ray.go b/infra/conf/v4/v2ray.go
index 399fdc22..9ca662d1 100644
--- a/infra/conf/v4/v2ray.go
+++ b/infra/conf/v4/v2ray.go
@@ -1,7 +1,9 @@
 package v4
 
 import (
+	"context"
 	"encoding/json"
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -21,6 +23,7 @@ import (
 	"github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/dns"
 	"github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/log"
 	"github.com/v2fly/v2ray-core/v5/infra/conf/synthetic/router"
+	"github.com/v2fly/v2ray-core/v5/infra/conf/v5cfg"
 )
 
 var (
@@ -481,15 +484,12 @@ func (c *Config) Build() (*core.Config, error) {
 
 	// Load Additional Services that do not have a json translator
 
-	if msg, err := c.BuildServices(c.Services); err != nil {
-		developererr := newError("Loading a V2Ray Features as a service is intended for developers only. " +
-			"This is used for developers to prototype new features or for an advanced client to use special features in V2Ray," +
-			" instead of allowing end user to enable it without special tool and knowledge.")
-		sb := strings.Builder{}
-		return nil, newError("Cannot load service").Base(developererr).Base(err).Base(newError(sb.String()))
-	} else { // nolint: revive
-		// Using a else here is required to keep msg in scope
-		config.App = append(config.App, msg...)
+	for serviceName, service := range c.Services {
+		servicePackedConfig, err := v5cfg.LoadHeterogeneousConfigFromRawJSON(context.Background(), "service", serviceName, *service)
+		if err != nil {
+			return nil, newError(fmt.Sprintf("failed to parse %v config in Services", serviceName)).Base(err)
+		}
+		config.App = append(config.App, serial.ToTypedMessage(servicePackedConfig))
 	}
 
 	var inbounds []InboundDetourConfig
diff --git a/main/commands/run.go b/main/commands/run.go
index 066bfae1..2fbb7a2b 100644
--- a/main/commands/run.go
+++ b/main/commands/run.go
@@ -14,6 +14,7 @@ import (
 	"github.com/v2fly/v2ray-core/v5/common/cmdarg"
 	"github.com/v2fly/v2ray-core/v5/common/platform"
 	"github.com/v2fly/v2ray-core/v5/main/commands/base"
+	"github.com/v2fly/v2ray-core/v5/main/plugins"
 )
 
 // CmdRun runs V2Ray with config
@@ -75,6 +76,12 @@ func setConfigFlags(cmd *base.Command) {
 
 func executeRun(cmd *base.Command, args []string) {
 	setConfigFlags(cmd)
+	var pluginFuncs []func() error
+	for _, plugin := range plugins.Plugins {
+		if f := plugin(cmd); f != nil {
+			pluginFuncs = append(pluginFuncs, f)
+		}
+	}
 	cmd.Flag.Parse(args)
 	printVersion()
 	configFiles = getConfigFilePath()
@@ -83,6 +90,14 @@ func executeRun(cmd *base.Command, args []string) {
 		base.Fatalf("Failed to start: %s", err)
 	}
 
+	for _, f := range pluginFuncs {
+		go func(f func() error) {
+			if err := f(); err != nil {
+				log.Print(err)
+			}
+		}(f)
+	}
+
 	if err := server.Start(); err != nil {
 		base.Fatalf("Failed to start: %s", err)
 	}
diff --git a/main/distro/all/all.go b/main/distro/all/all.go
index 200f1228..668f80c9 100644
--- a/main/distro/all/all.go
+++ b/main/distro/all/all.go
@@ -33,7 +33,6 @@ import (
 	// Developer preview features
 	_ "github.com/v2fly/v2ray-core/v5/app/instman"
 	_ "github.com/v2fly/v2ray-core/v5/app/observatory"
-	_ "github.com/v2fly/v2ray-core/v5/app/restfulapi"
 	_ "github.com/v2fly/v2ray-core/v5/app/tun"
 
 	// Inbound and outbound proxies.
@@ -74,8 +73,12 @@ import (
 	_ "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/simple"
 	_ "github.com/v2fly/v2ray-core/v5/transport/internet/request/roundtripper/httprt"
 
+	_ "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/packetconn"
+
 	_ "github.com/v2fly/v2ray-core/v5/transport/internet/request/stereotype/meek"
 
+	_ "github.com/v2fly/v2ray-core/v5/transport/internet/dtls"
+
 	_ "github.com/v2fly/v2ray-core/v5/transport/internet/httpupgrade"
 
 	// Transport headers
diff --git a/main/plugins/plugin.go b/main/plugins/plugin.go
new file mode 100644
index 00000000..58786b00
--- /dev/null
+++ b/main/plugins/plugin.go
@@ -0,0 +1,11 @@
+package plugins
+
+import "github.com/v2fly/v2ray-core/v5/main/commands/base"
+
+var Plugins []Plugin
+
+type Plugin func(*base.Command) func() error
+
+func RegisterPlugin(plugin Plugin) {
+	Plugins = append(Plugins, plugin)
+}
diff --git a/main/plugins/plugin_pprof/plugin_pprof.go b/main/plugins/plugin_pprof/plugin_pprof.go
new file mode 100644
index 00000000..5da1a7b7
--- /dev/null
+++ b/main/plugins/plugin_pprof/plugin_pprof.go
@@ -0,0 +1,29 @@
+package plugin_pprof
+
+import (
+	"github.com/v2fly/v2ray-core/v5/main/plugins"
+	"net/http"
+	"net/http/pprof"
+
+	"github.com/v2fly/v2ray-core/v5/main/commands/base"
+)
+
+var pprofPlugin plugins.Plugin = func(cmd *base.Command) func() error {
+	addr := cmd.Flag.String("pprof", "", "")
+	return func() error {
+		if *addr != "" {
+			h := http.NewServeMux()
+			h.HandleFunc("/debug/pprof/", pprof.Index)
+			h.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
+			h.HandleFunc("/debug/pprof/profile", pprof.Profile)
+			h.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
+			h.HandleFunc("/debug/pprof/trace", pprof.Trace)
+			return (&http.Server{Addr: *addr, Handler: h}).ListenAndServe()
+		}
+		return nil
+	}
+}
+
+func init() {
+	plugins.RegisterPlugin(pprofPlugin)
+}
diff --git a/proxy/freedom/config.pb.go b/proxy/freedom/config.pb.go
index 0669c70f..0574b69e 100644
--- a/proxy/freedom/config.pb.go
+++ b/proxy/freedom/config.pb.go
@@ -16,6 +16,55 @@ const (
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
+type ProtocolReplacement int32
+
+const (
+	ProtocolReplacement_IDENTITY  ProtocolReplacement = 0
+	ProtocolReplacement_FORCE_TCP ProtocolReplacement = 1
+	ProtocolReplacement_FORCE_UDP ProtocolReplacement = 2
+)
+
+// Enum value maps for ProtocolReplacement.
+var (
+	ProtocolReplacement_name = map[int32]string{
+		0: "IDENTITY",
+		1: "FORCE_TCP",
+		2: "FORCE_UDP",
+	}
+	ProtocolReplacement_value = map[string]int32{
+		"IDENTITY":  0,
+		"FORCE_TCP": 1,
+		"FORCE_UDP": 2,
+	}
+)
+
+func (x ProtocolReplacement) Enum() *ProtocolReplacement {
+	p := new(ProtocolReplacement)
+	*p = x
+	return p
+}
+
+func (x ProtocolReplacement) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ProtocolReplacement) Descriptor() protoreflect.EnumDescriptor {
+	return file_proxy_freedom_config_proto_enumTypes[0].Descriptor()
+}
+
+func (ProtocolReplacement) Type() protoreflect.EnumType {
+	return &file_proxy_freedom_config_proto_enumTypes[0]
+}
+
+func (x ProtocolReplacement) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use ProtocolReplacement.Descriptor instead.
+func (ProtocolReplacement) EnumDescriptor() ([]byte, []int) {
+	return file_proxy_freedom_config_proto_rawDescGZIP(), []int{0}
+}
+
 type Config_DomainStrategy int32
 
 const (
@@ -52,11 +101,11 @@ func (x Config_DomainStrategy) String() string {
 }
 
 func (Config_DomainStrategy) Descriptor() protoreflect.EnumDescriptor {
-	return file_proxy_freedom_config_proto_enumTypes[0].Descriptor()
+	return file_proxy_freedom_config_proto_enumTypes[1].Descriptor()
 }
 
 func (Config_DomainStrategy) Type() protoreflect.EnumType {
-	return &file_proxy_freedom_config_proto_enumTypes[0]
+	return &file_proxy_freedom_config_proto_enumTypes[1]
 }
 
 func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
@@ -125,6 +174,7 @@ type Config struct {
 	Timeout             uint32               `protobuf:"varint,2,opt,name=timeout,proto3" json:"timeout,omitempty"`
 	DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
 	UserLevel           uint32               `protobuf:"varint,4,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
+	ProtocolReplacement ProtocolReplacement  `protobuf:"varint,5,opt,name=protocol_replacement,json=protocolReplacement,proto3,enum=v2ray.core.proxy.freedom.ProtocolReplacement" json:"protocol_replacement,omitempty"`
 }
 
 func (x *Config) Reset() {
@@ -188,10 +238,20 @@ func (x *Config) GetUserLevel() uint32 {
 	return 0
 }
 
+func (x *Config) GetProtocolReplacement() ProtocolReplacement {
+	if x != nil {
+		return x.ProtocolReplacement
+	}
+	return ProtocolReplacement_IDENTITY
+}
+
 type SimplifiedConfig struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
+
+	DestinationOverride *DestinationOverride `protobuf:"bytes,3,opt,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
+	ProtocolReplacement ProtocolReplacement  `protobuf:"varint,5,opt,name=protocol_replacement,json=protocolReplacement,proto3,enum=v2ray.core.proxy.freedom.ProtocolReplacement" json:"protocol_replacement,omitempty"`
 }
 
 func (x *SimplifiedConfig) Reset() {
@@ -226,6 +286,20 @@ func (*SimplifiedConfig) Descriptor() ([]byte, []int) {
 	return file_proxy_freedom_config_proto_rawDescGZIP(), []int{2}
 }
 
+func (x *SimplifiedConfig) GetDestinationOverride() *DestinationOverride {
+	if x != nil {
+		return x.DestinationOverride
+	}
+	return nil
+}
+
+func (x *SimplifiedConfig) GetProtocolReplacement() ProtocolReplacement {
+	if x != nil {
+		return x.ProtocolReplacement
+	}
+	return ProtocolReplacement_IDENTITY
+}
+
 var File_proxy_freedom_config_proto protoreflect.FileDescriptor
 
 var file_proxy_freedom_config_proto_rawDesc = []byte{
@@ -242,7 +316,7 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{
 	0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
 	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,
 	0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x06,
-	0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0xc4, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0xa6, 0x03, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
 	0x67, 0x12, 0x58, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61,
 	0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x76, 0x32, 0x72,
 	0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72,
@@ -258,21 +332,44 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{
 	0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
 	0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75,
 	0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52,
-	0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x41, 0x0a, 0x0e, 0x44, 0x6f,
-	0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05,
-	0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49,
-	0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02,
-	0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x22, 0x2b, 0x0a,
-	0x10, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x3a, 0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
-	0x64, 0x12, 0x07, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x42, 0x69, 0x0a, 0x1c, 0x63, 0x6f,
-	0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f,
-	0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69,
-	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76,
-	0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f,
-	0x78, 0x79, 0x2f, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x18, 0x56, 0x32, 0x52,
-	0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72,
-	0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x60, 0x0a, 0x14, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65,
+	0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79,
+	0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65,
+	0x64, 0x6f, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x65, 0x70, 0x6c,
+	0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
+	0x6c, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x41, 0x0a, 0x0e,
+	0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09,
+	0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45,
+	0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34,
+	0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x22,
+	0xef, 0x01, 0x0a, 0x10, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x60, 0x0a, 0x14, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
+	0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x44, 0x65,
+	0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
+	0x65, 0x52, 0x13, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x76,
+	0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x60, 0x0a, 0x14, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+	0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05,
+	0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72,
+	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e,
+	0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d,
+	0x65, 0x6e, 0x74, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x65, 0x70,
+	0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3a, 0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a, 0x08,
+	0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x07, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f,
+	0x6d, 0x2a, 0x41, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x65, 0x70,
+	0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x44, 0x45, 0x4e,
+	0x54, 0x49, 0x54, 0x59, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f,
+	0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x55,
+	0x44, 0x50, 0x10, 0x02, 0x42, 0x69, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61,
+	0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65,
+	0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63,
+	0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, 0x65,
+	0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x18, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72,
+	0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -287,24 +384,28 @@ func file_proxy_freedom_config_proto_rawDescGZIP() []byte {
 	return file_proxy_freedom_config_proto_rawDescData
 }
 
-var file_proxy_freedom_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_proxy_freedom_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
 var file_proxy_freedom_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
-var file_proxy_freedom_config_proto_goTypes = []interface{}{
-	(Config_DomainStrategy)(0),      // 0: v2ray.core.proxy.freedom.Config.DomainStrategy
-	(*DestinationOverride)(nil),     // 1: v2ray.core.proxy.freedom.DestinationOverride
-	(*Config)(nil),                  // 2: v2ray.core.proxy.freedom.Config
-	(*SimplifiedConfig)(nil),        // 3: v2ray.core.proxy.freedom.SimplifiedConfig
-	(*protocol.ServerEndpoint)(nil), // 4: v2ray.core.common.protocol.ServerEndpoint
+var file_proxy_freedom_config_proto_goTypes = []any{
+	(ProtocolReplacement)(0),        // 0: v2ray.core.proxy.freedom.ProtocolReplacement
+	(Config_DomainStrategy)(0),      // 1: v2ray.core.proxy.freedom.Config.DomainStrategy
+	(*DestinationOverride)(nil),     // 2: v2ray.core.proxy.freedom.DestinationOverride
+	(*Config)(nil),                  // 3: v2ray.core.proxy.freedom.Config
+	(*SimplifiedConfig)(nil),        // 4: v2ray.core.proxy.freedom.SimplifiedConfig
+	(*protocol.ServerEndpoint)(nil), // 5: v2ray.core.common.protocol.ServerEndpoint
 }
 var file_proxy_freedom_config_proto_depIdxs = []int32{
-	4, // 0: v2ray.core.proxy.freedom.DestinationOverride.server:type_name -> v2ray.core.common.protocol.ServerEndpoint
-	0, // 1: v2ray.core.proxy.freedom.Config.domain_strategy:type_name -> v2ray.core.proxy.freedom.Config.DomainStrategy
-	1, // 2: v2ray.core.proxy.freedom.Config.destination_override:type_name -> v2ray.core.proxy.freedom.DestinationOverride
-	3, // [3:3] is the sub-list for method output_type
-	3, // [3:3] is the sub-list for method input_type
-	3, // [3:3] is the sub-list for extension type_name
-	3, // [3:3] is the sub-list for extension extendee
-	0, // [0:3] is the sub-list for field type_name
+	5, // 0: v2ray.core.proxy.freedom.DestinationOverride.server:type_name -> v2ray.core.common.protocol.ServerEndpoint
+	1, // 1: v2ray.core.proxy.freedom.Config.domain_strategy:type_name -> v2ray.core.proxy.freedom.Config.DomainStrategy
+	2, // 2: v2ray.core.proxy.freedom.Config.destination_override:type_name -> v2ray.core.proxy.freedom.DestinationOverride
+	0, // 3: v2ray.core.proxy.freedom.Config.protocol_replacement:type_name -> v2ray.core.proxy.freedom.ProtocolReplacement
+	2, // 4: v2ray.core.proxy.freedom.SimplifiedConfig.destination_override:type_name -> v2ray.core.proxy.freedom.DestinationOverride
+	0, // 5: v2ray.core.proxy.freedom.SimplifiedConfig.protocol_replacement:type_name -> v2ray.core.proxy.freedom.ProtocolReplacement
+	6, // [6:6] is the sub-list for method output_type
+	6, // [6:6] is the sub-list for method input_type
+	6, // [6:6] is the sub-list for extension type_name
+	6, // [6:6] is the sub-list for extension extendee
+	0, // [0:6] is the sub-list for field type_name
 }
 
 func init() { file_proxy_freedom_config_proto_init() }
@@ -313,7 +414,7 @@ func file_proxy_freedom_config_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_proxy_freedom_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_proxy_freedom_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*DestinationOverride); i {
 			case 0:
 				return &v.state
@@ -325,7 +426,7 @@ func file_proxy_freedom_config_proto_init() {
 				return nil
 			}
 		}
-		file_proxy_freedom_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_proxy_freedom_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*Config); i {
 			case 0:
 				return &v.state
@@ -337,7 +438,7 @@ func file_proxy_freedom_config_proto_init() {
 				return nil
 			}
 		}
-		file_proxy_freedom_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_proxy_freedom_config_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*SimplifiedConfig); i {
 			case 0:
 				return &v.state
@@ -355,7 +456,7 @@ func file_proxy_freedom_config_proto_init() {
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_proxy_freedom_config_proto_rawDesc,
-			NumEnums:      1,
+			NumEnums:      2,
 			NumMessages:   3,
 			NumExtensions: 0,
 			NumServices:   0,
diff --git a/proxy/freedom/config.proto b/proxy/freedom/config.proto
index e095dc8e..78c8631d 100644
--- a/proxy/freedom/config.proto
+++ b/proxy/freedom/config.proto
@@ -13,6 +13,12 @@ message DestinationOverride {
   v2ray.core.common.protocol.ServerEndpoint server = 1;
 }
 
+enum ProtocolReplacement {
+  IDENTITY = 0;
+  FORCE_TCP = 1;
+  FORCE_UDP = 2;
+}
+
 message Config {
   enum DomainStrategy {
     AS_IS = 0;
@@ -24,9 +30,13 @@ message Config {
   uint32 timeout = 2 [deprecated = true];
   DestinationOverride destination_override = 3;
   uint32 user_level = 4;
+  ProtocolReplacement protocol_replacement = 5;
 }
 
 message SimplifiedConfig {
   option (v2ray.core.common.protoext.message_opt).type = "outbound";
   option (v2ray.core.common.protoext.message_opt).short_name = "freedom";
+
+  DestinationOverride destination_override = 3;
+  ProtocolReplacement protocol_replacement = 5;
 }
\ No newline at end of file
diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go
index e16e72b8..2f70b733 100644
--- a/proxy/freedom/freedom.go
+++ b/proxy/freedom/freedom.go
@@ -35,7 +35,10 @@ func init() {
 	common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
 		simplifiedServer := config.(*SimplifiedConfig)
 		_ = simplifiedServer
-		fullConfig := &Config{}
+		fullConfig := &Config{
+			DestinationOverride: simplifiedServer.DestinationOverride,
+			ProtocolReplacement: simplifiedServer.ProtocolReplacement,
+		}
 		return common.CreateObject(ctx, fullConfig)
 	}))
 }
@@ -104,6 +107,14 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
 			destination.Port = net.Port(server.Port)
 		}
 	}
+	if h.config.ProtocolReplacement != ProtocolReplacement_IDENTITY {
+		if h.config.ProtocolReplacement == ProtocolReplacement_FORCE_TCP {
+			destination.Network = net.Network_TCP
+		}
+		if h.config.ProtocolReplacement == ProtocolReplacement_FORCE_UDP {
+			destination.Network = net.Network_UDP
+		}
+	}
 	if h.config.useIP() {
 		outbound.Resolver = func(ctx context.Context, domain string) net.Address {
 			return h.resolveIP(ctx, domain, dialer.Address())
@@ -153,7 +164,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
 		defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
 
 		var reader buf.Reader
-		if destination.Network == net.Network_TCP {
+		if destination.Network == net.Network_TCP && h.config.ProtocolReplacement == ProtocolReplacement_IDENTITY {
 			reader = buf.NewReader(conn)
 		} else {
 			reader = buf.NewPacketReader(conn)
diff --git a/proxy/http/client.go b/proxy/http/client.go
index 316ed74a..36aa0cfa 100644
--- a/proxy/http/client.go
+++ b/proxy/http/client.go
@@ -27,7 +27,7 @@ import (
 	"github.com/v2fly/v2ray-core/v5/proxy"
 	"github.com/v2fly/v2ray-core/v5/transport"
 	"github.com/v2fly/v2ray-core/v5/transport/internet"
-	"github.com/v2fly/v2ray-core/v5/transport/internet/tls"
+	"github.com/v2fly/v2ray-core/v5/transport/internet/security"
 )
 
 type Client struct {
@@ -288,12 +288,12 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u
 	}
 
 	nextProto := ""
-	if tlsConn, ok := iConn.(*tls.Conn); ok {
-		if err := tlsConn.Handshake(); err != nil {
+	if connALPNGetter, ok := iConn.(security.ConnectionApplicationProtocol); ok {
+		nextProto, err = connALPNGetter.GetConnectionApplicationProtocol()
+		if err != nil {
 			rawConn.Close()
 			return nil, nil, err
 		}
-		nextProto = tlsConn.ConnectionState().NegotiatedProtocol
 	}
 
 	switch nextProto {
diff --git a/release/config/config.json b/release/config/config.json
index d6dd70ab..0550f6d9 100644
--- a/release/config/config.json
+++ b/release/config/config.json
@@ -126,7 +126,7 @@
   },
 
   // Stats enables internal stats counter.
-  // This setting can be used together with Policy and Api. 
+  // This setting can be used together with Policy and Api.
   //"stats":{},
 
   // Api enables gRPC APIs for external programs to communicate with V2Ray instance.
diff --git a/release/debian/changelog b/release/debian/changelog
index 94f1cf1b..c16aef6a 100644
--- a/release/debian/changelog
+++ b/release/debian/changelog
@@ -82,7 +82,7 @@ v2ray-core (4.34.0-1) unstable; urgency=medium
 
   * TLS Session Resumption is now disabled by default (#569).
     See #557 for more information.
-  * Support for the legacy Shadowsocks protocol with stream ciphers has been removed (#566). 
+  * Support for the legacy Shadowsocks protocol with stream ciphers has been removed (#566).
     If you are still using the unsecure stream ciphers, migrate to Shadowsocks AEAD (ChaCha20Poly1305 and AES-GCM) immediately.
   * We have added preliminary support for DNS over QUIC (#534).
     Currently only non-proxied lookup is supported.
diff --git a/transport/internet/config.pb.go b/transport/internet/config.pb.go
index 1ded5291..31b62337 100644
--- a/transport/internet/config.pb.go
+++ b/transport/internet/config.pb.go
@@ -1,3 +1,9 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.34.2
+// 	protoc        v4.25.3
+// source: transport/internet/config.proto
+
 package internet
 
 import (
@@ -73,6 +79,60 @@ func (TransportProtocol) EnumDescriptor() ([]byte, []int) {
 	return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
 }
 
+// MPTCP is the state of MPTCP settings.
+// Define it here to avoid conflict with TCPFastOpenState.
+type MPTCPState int32
+
+const (
+	// AsIs is to leave the current MPTCP state as is, unmodified.
+	MPTCPState_AsIs MPTCPState = 0
+	// Enable is for enabling MPTCP explictly.
+	MPTCPState_Enable MPTCPState = 1
+	// Disable is for disabling MPTCP explictly.
+	MPTCPState_Disable MPTCPState = 2
+)
+
+// Enum value maps for MPTCPState.
+var (
+	MPTCPState_name = map[int32]string{
+		0: "AsIs",
+		1: "Enable",
+		2: "Disable",
+	}
+	MPTCPState_value = map[string]int32{
+		"AsIs":    0,
+		"Enable":  1,
+		"Disable": 2,
+	}
+)
+
+func (x MPTCPState) Enum() *MPTCPState {
+	p := new(MPTCPState)
+	*p = x
+	return p
+}
+
+func (x MPTCPState) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (MPTCPState) Descriptor() protoreflect.EnumDescriptor {
+	return file_transport_internet_config_proto_enumTypes[1].Descriptor()
+}
+
+func (MPTCPState) Type() protoreflect.EnumType {
+	return &file_transport_internet_config_proto_enumTypes[1]
+}
+
+func (x MPTCPState) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use MPTCPState.Descriptor instead.
+func (MPTCPState) EnumDescriptor() ([]byte, []int) {
+	return file_transport_internet_config_proto_rawDescGZIP(), []int{1}
+}
+
 type SocketConfig_TCPFastOpenState int32
 
 const (
@@ -109,11 +169,11 @@ func (x SocketConfig_TCPFastOpenState) String() string {
 }
 
 func (SocketConfig_TCPFastOpenState) Descriptor() protoreflect.EnumDescriptor {
-	return file_transport_internet_config_proto_enumTypes[1].Descriptor()
+	return file_transport_internet_config_proto_enumTypes[2].Descriptor()
 }
 
 func (SocketConfig_TCPFastOpenState) Type() protoreflect.EnumType {
-	return &file_transport_internet_config_proto_enumTypes[1]
+	return &file_transport_internet_config_proto_enumTypes[2]
 }
 
 func (x SocketConfig_TCPFastOpenState) Number() protoreflect.EnumNumber {
@@ -161,11 +221,11 @@ func (x SocketConfig_TProxyMode) String() string {
 }
 
 func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor {
-	return file_transport_internet_config_proto_enumTypes[2].Descriptor()
+	return file_transport_internet_config_proto_enumTypes[3].Descriptor()
 }
 
 func (SocketConfig_TProxyMode) Type() protoreflect.EnumType {
-	return &file_transport_internet_config_proto_enumTypes[2]
+	return &file_transport_internet_config_proto_enumTypes[3]
 }
 
 func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
@@ -410,17 +470,18 @@ type SocketConfig struct {
 	Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=v2ray.core.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"`
 	// ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket
 	// option. This option is for UDP only.
-	ReceiveOriginalDestAddress bool   `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
-	BindAddress                []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
-	BindPort                   uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
-	AcceptProxyProtocol        bool   `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
-	TcpKeepAliveInterval       int32  `protobuf:"varint,8,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"`
-	TfoQueueLength             uint32 `protobuf:"varint,9,opt,name=tfo_queue_length,json=tfoQueueLength,proto3" json:"tfo_queue_length,omitempty"`
-	TcpKeepAliveIdle           int32  `protobuf:"varint,10,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"`
-	BindToDevice               string `protobuf:"bytes,11,opt,name=bind_to_device,json=bindToDevice,proto3" json:"bind_to_device,omitempty"`
-	RxBufSize                  int64  `protobuf:"varint,12,opt,name=rx_buf_size,json=rxBufSize,proto3" json:"rx_buf_size,omitempty"`
-	TxBufSize                  int64  `protobuf:"varint,13,opt,name=tx_buf_size,json=txBufSize,proto3" json:"tx_buf_size,omitempty"`
-	ForceBufSize               bool   `protobuf:"varint,14,opt,name=force_buf_size,json=forceBufSize,proto3" json:"force_buf_size,omitempty"`
+	ReceiveOriginalDestAddress bool       `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
+	BindAddress                []byte     `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
+	BindPort                   uint32     `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
+	AcceptProxyProtocol        bool       `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
+	TcpKeepAliveInterval       int32      `protobuf:"varint,8,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"`
+	TfoQueueLength             uint32     `protobuf:"varint,9,opt,name=tfo_queue_length,json=tfoQueueLength,proto3" json:"tfo_queue_length,omitempty"`
+	TcpKeepAliveIdle           int32      `protobuf:"varint,10,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"`
+	BindToDevice               string     `protobuf:"bytes,11,opt,name=bind_to_device,json=bindToDevice,proto3" json:"bind_to_device,omitempty"`
+	RxBufSize                  int64      `protobuf:"varint,12,opt,name=rx_buf_size,json=rxBufSize,proto3" json:"rx_buf_size,omitempty"`
+	TxBufSize                  int64      `protobuf:"varint,13,opt,name=tx_buf_size,json=txBufSize,proto3" json:"tx_buf_size,omitempty"`
+	ForceBufSize               bool       `protobuf:"varint,14,opt,name=force_buf_size,json=forceBufSize,proto3" json:"force_buf_size,omitempty"`
+	Mptcp                      MPTCPState `protobuf:"varint,15,opt,name=mptcp,proto3,enum=v2ray.core.transport.internet.MPTCPState" json:"mptcp,omitempty"`
 }
 
 func (x *SocketConfig) Reset() {
@@ -553,6 +614,13 @@ func (x *SocketConfig) GetForceBufSize() bool {
 	return false
 }
 
+func (x *SocketConfig) GetMptcp() MPTCPState {
+	if x != nil {
+		return x.Mptcp
+	}
+	return MPTCPState_AsIs
+}
+
 var File_transport_internet_config_proto protoreflect.FileDescriptor
 
 var file_transport_internet_config_proto_rawDesc = []byte{
@@ -605,7 +673,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{
 	0x0a, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72,
 	0x50, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x74, 0x72, 0x61,
 	0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79,
-	0x22, 0xfd, 0x05, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x22, 0xbe, 0x06, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
 	0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
 	0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x4e, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01,
 	0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
@@ -646,27 +714,34 @@ var file_transport_internet_config_proto_rawDesc = []byte{
 	0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x78, 0x42, 0x75, 0x66, 0x53, 0x69,
 	0x7a, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x5f,
 	0x73, 0x69, 0x7a, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72, 0x63,
-	0x65, 0x42, 0x75, 0x66, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x35, 0x0a, 0x10, 0x54, 0x43, 0x50, 0x46,
-	0x61, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04,
-	0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
-	0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x02, 0x22,
-	0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a,
-	0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79,
-	0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02,
-	0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f,
-	0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x07,
-	0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10,
-	0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03,
-	0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f,
-	0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x05, 0x42, 0x78, 0x0a, 0x21,
-	0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74,
-	0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
-	0x74, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
-	0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
-	0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
-	0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x1d, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43,
-	0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
-	0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x42, 0x75, 0x66, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x3f, 0x0a, 0x05, 0x6d, 0x70, 0x74, 0x63,
+	0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
+	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
+	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x4d, 0x50, 0x54, 0x43, 0x50, 0x53, 0x74, 0x61,
+	0x74, 0x65, 0x52, 0x05, 0x6d, 0x70, 0x74, 0x63, 0x70, 0x22, 0x35, 0x0a, 0x10, 0x54, 0x43, 0x50,
+	0x46, 0x61, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a,
+	0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x6e, 0x61, 0x62, 0x6c,
+	0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x02,
+	0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07,
+	0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78,
+	0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10,
+	0x02, 0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72,
+	0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12,
+	0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50,
+	0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10,
+	0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44,
+	0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x05, 0x2a, 0x2f, 0x0a,
+	0x0a, 0x4d, 0x50, 0x54, 0x43, 0x50, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x41,
+	0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x10,
+	0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x02, 0x42, 0x78,
+	0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65,
+	0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
+	0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
+	0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
+	0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
+	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x1d, 0x56, 0x32, 0x52, 0x61, 0x79,
+	0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
+	0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -681,32 +756,34 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
 	return file_transport_internet_config_proto_rawDescData
 }
 
-var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
+var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
 var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
-var file_transport_internet_config_proto_goTypes = []interface{}{
+var file_transport_internet_config_proto_goTypes = []any{
 	(TransportProtocol)(0),             // 0: v2ray.core.transport.internet.TransportProtocol
-	(SocketConfig_TCPFastOpenState)(0), // 1: v2ray.core.transport.internet.SocketConfig.TCPFastOpenState
-	(SocketConfig_TProxyMode)(0),       // 2: v2ray.core.transport.internet.SocketConfig.TProxyMode
-	(*TransportConfig)(nil),            // 3: v2ray.core.transport.internet.TransportConfig
-	(*StreamConfig)(nil),               // 4: v2ray.core.transport.internet.StreamConfig
-	(*ProxyConfig)(nil),                // 5: v2ray.core.transport.internet.ProxyConfig
-	(*SocketConfig)(nil),               // 6: v2ray.core.transport.internet.SocketConfig
-	(*anypb.Any)(nil),                  // 7: google.protobuf.Any
+	(MPTCPState)(0),                    // 1: v2ray.core.transport.internet.MPTCPState
+	(SocketConfig_TCPFastOpenState)(0), // 2: v2ray.core.transport.internet.SocketConfig.TCPFastOpenState
+	(SocketConfig_TProxyMode)(0),       // 3: v2ray.core.transport.internet.SocketConfig.TProxyMode
+	(*TransportConfig)(nil),            // 4: v2ray.core.transport.internet.TransportConfig
+	(*StreamConfig)(nil),               // 5: v2ray.core.transport.internet.StreamConfig
+	(*ProxyConfig)(nil),                // 6: v2ray.core.transport.internet.ProxyConfig
+	(*SocketConfig)(nil),               // 7: v2ray.core.transport.internet.SocketConfig
+	(*anypb.Any)(nil),                  // 8: google.protobuf.Any
 }
 var file_transport_internet_config_proto_depIdxs = []int32{
 	0, // 0: v2ray.core.transport.internet.TransportConfig.protocol:type_name -> v2ray.core.transport.internet.TransportProtocol
-	7, // 1: v2ray.core.transport.internet.TransportConfig.settings:type_name -> google.protobuf.Any
+	8, // 1: v2ray.core.transport.internet.TransportConfig.settings:type_name -> google.protobuf.Any
 	0, // 2: v2ray.core.transport.internet.StreamConfig.protocol:type_name -> v2ray.core.transport.internet.TransportProtocol
-	3, // 3: v2ray.core.transport.internet.StreamConfig.transport_settings:type_name -> v2ray.core.transport.internet.TransportConfig
-	7, // 4: v2ray.core.transport.internet.StreamConfig.security_settings:type_name -> google.protobuf.Any
-	6, // 5: v2ray.core.transport.internet.StreamConfig.socket_settings:type_name -> v2ray.core.transport.internet.SocketConfig
-	1, // 6: v2ray.core.transport.internet.SocketConfig.tfo:type_name -> v2ray.core.transport.internet.SocketConfig.TCPFastOpenState
-	2, // 7: v2ray.core.transport.internet.SocketConfig.tproxy:type_name -> v2ray.core.transport.internet.SocketConfig.TProxyMode
-	8, // [8:8] is the sub-list for method output_type
-	8, // [8:8] is the sub-list for method input_type
-	8, // [8:8] is the sub-list for extension type_name
-	8, // [8:8] is the sub-list for extension extendee
-	0, // [0:8] is the sub-list for field type_name
+	4, // 3: v2ray.core.transport.internet.StreamConfig.transport_settings:type_name -> v2ray.core.transport.internet.TransportConfig
+	8, // 4: v2ray.core.transport.internet.StreamConfig.security_settings:type_name -> google.protobuf.Any
+	7, // 5: v2ray.core.transport.internet.StreamConfig.socket_settings:type_name -> v2ray.core.transport.internet.SocketConfig
+	2, // 6: v2ray.core.transport.internet.SocketConfig.tfo:type_name -> v2ray.core.transport.internet.SocketConfig.TCPFastOpenState
+	3, // 7: v2ray.core.transport.internet.SocketConfig.tproxy:type_name -> v2ray.core.transport.internet.SocketConfig.TProxyMode
+	1, // 8: v2ray.core.transport.internet.SocketConfig.mptcp:type_name -> v2ray.core.transport.internet.MPTCPState
+	9, // [9:9] is the sub-list for method output_type
+	9, // [9:9] is the sub-list for method input_type
+	9, // [9:9] is the sub-list for extension type_name
+	9, // [9:9] is the sub-list for extension extendee
+	0, // [0:9] is the sub-list for field type_name
 }
 
 func init() { file_transport_internet_config_proto_init() }
@@ -715,7 +792,7 @@ func file_transport_internet_config_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_transport_internet_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_transport_internet_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*TransportConfig); i {
 			case 0:
 				return &v.state
@@ -727,7 +804,7 @@ func file_transport_internet_config_proto_init() {
 				return nil
 			}
 		}
-		file_transport_internet_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_transport_internet_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*StreamConfig); i {
 			case 0:
 				return &v.state
@@ -739,7 +816,7 @@ func file_transport_internet_config_proto_init() {
 				return nil
 			}
 		}
-		file_transport_internet_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_transport_internet_config_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*ProxyConfig); i {
 			case 0:
 				return &v.state
@@ -751,7 +828,7 @@ func file_transport_internet_config_proto_init() {
 				return nil
 			}
 		}
-		file_transport_internet_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_transport_internet_config_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*SocketConfig); i {
 			case 0:
 				return &v.state
@@ -769,7 +846,7 @@ func file_transport_internet_config_proto_init() {
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_transport_internet_config_proto_rawDesc,
-			NumEnums:      3,
+			NumEnums:      4,
 			NumMessages:   4,
 			NumExtensions: 0,
 			NumServices:   0,
diff --git a/transport/internet/config.proto b/transport/internet/config.proto
index 2478c6f2..0a6a057d 100644
--- a/transport/internet/config.proto
+++ b/transport/internet/config.proto
@@ -53,6 +53,17 @@ message ProxyConfig {
   bool transportLayerProxy = 2;
 }
 
+// MPTCP is the state of MPTCP settings.
+// Define it here to avoid conflict with TCPFastOpenState.
+enum MPTCPState {
+  // AsIs is to leave the current MPTCP state as is, unmodified.
+  AsIs = 0;
+  // Enable is for enabling MPTCP explictly.
+  Enable = 1;
+  // Disable is for disabling MPTCP explictly.
+  Disable = 2;
+}
+
 // SocketConfig is options to be applied on network sockets.
 message SocketConfig {
   // Mark of the connection. If non-zero, the value will be set to SO_MARK.
@@ -103,4 +114,7 @@ message SocketConfig {
   int64 rx_buf_size = 12;
   int64 tx_buf_size = 13;
   bool force_buf_size = 14;
+
+
+  MPTCPState mptcp = 15;
 }
diff --git a/transport/internet/dtls/config.pb.go b/transport/internet/dtls/config.pb.go
new file mode 100644
index 00000000..658b20fe
--- /dev/null
+++ b/transport/internet/dtls/config.pb.go
@@ -0,0 +1,236 @@
+package dtls
+
+import (
+	_ "github.com/v2fly/v2ray-core/v5/common/protoext"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type DTLSMode int32
+
+const (
+	DTLSMode_INVALID DTLSMode = 0
+	DTLSMode_PSK     DTLSMode = 1
+)
+
+// Enum value maps for DTLSMode.
+var (
+	DTLSMode_name = map[int32]string{
+		0: "INVALID",
+		1: "PSK",
+	}
+	DTLSMode_value = map[string]int32{
+		"INVALID": 0,
+		"PSK":     1,
+	}
+)
+
+func (x DTLSMode) Enum() *DTLSMode {
+	p := new(DTLSMode)
+	*p = x
+	return p
+}
+
+func (x DTLSMode) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (DTLSMode) Descriptor() protoreflect.EnumDescriptor {
+	return file_transport_internet_dtls_config_proto_enumTypes[0].Descriptor()
+}
+
+func (DTLSMode) Type() protoreflect.EnumType {
+	return &file_transport_internet_dtls_config_proto_enumTypes[0]
+}
+
+func (x DTLSMode) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use DTLSMode.Descriptor instead.
+func (DTLSMode) EnumDescriptor() ([]byte, []int) {
+	return file_transport_internet_dtls_config_proto_rawDescGZIP(), []int{0}
+}
+
+type Config struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Mode                   DTLSMode `protobuf:"varint,1,opt,name=mode,proto3,enum=v2ray.core.transport.internet.dtls.DTLSMode" json:"mode,omitempty"`
+	Psk                    []byte   `protobuf:"bytes,2,opt,name=psk,proto3" json:"psk,omitempty"`
+	Mtu                    uint32   `protobuf:"varint,3,opt,name=mtu,proto3" json:"mtu,omitempty"`
+	ReplayProtectionWindow uint32   `protobuf:"varint,4,opt,name=replay_protection_window,json=replayProtectionWindow,proto3" json:"replay_protection_window,omitempty"`
+}
+
+func (x *Config) Reset() {
+	*x = Config{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_transport_internet_dtls_config_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Config) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Config) ProtoMessage() {}
+
+func (x *Config) ProtoReflect() protoreflect.Message {
+	mi := &file_transport_internet_dtls_config_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Config.ProtoReflect.Descriptor instead.
+func (*Config) Descriptor() ([]byte, []int) {
+	return file_transport_internet_dtls_config_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Config) GetMode() DTLSMode {
+	if x != nil {
+		return x.Mode
+	}
+	return DTLSMode_INVALID
+}
+
+func (x *Config) GetPsk() []byte {
+	if x != nil {
+		return x.Psk
+	}
+	return nil
+}
+
+func (x *Config) GetMtu() uint32 {
+	if x != nil {
+		return x.Mtu
+	}
+	return 0
+}
+
+func (x *Config) GetReplayProtectionWindow() uint32 {
+	if x != nil {
+		return x.ReplayProtectionWindow
+	}
+	return 0
+}
+
+var File_transport_internet_dtls_config_proto protoreflect.FileDescriptor
+
+var file_transport_internet_dtls_config_proto_rawDesc = []byte{
+	0x0a, 0x24, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
+	0x72, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x22, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
+	0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
+	0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x74, 0x6c, 0x73, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
+	0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x78, 0x74, 0x2f, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x01, 0x0a,
+	0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
+	0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
+	0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x74, 0x6c, 0x73, 0x2e, 0x44, 0x54, 0x4c, 0x53, 0x4d,
+	0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x73, 0x6b,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x70, 0x73, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x6d,
+	0x74, 0x75, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x12, 0x38, 0x0a,
+	0x18, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x16, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x69, 0x6f,
+	0x6e, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x3a, 0x15, 0x82, 0xb5, 0x18, 0x11, 0x0a, 0x09, 0x74,
+	0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x04, 0x64, 0x74, 0x6c, 0x73, 0x2a, 0x20,
+	0x0a, 0x08, 0x44, 0x54, 0x4c, 0x53, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e,
+	0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x50, 0x53, 0x4b, 0x10, 0x01,
+	0x42, 0x87, 0x01, 0x0a, 0x26, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63,
+	0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x36, 0x67,
+	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f,
+	0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+	0x2f, 0x64, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x22, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f,
+	0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74,
+	0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x74, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
+}
+
+var (
+	file_transport_internet_dtls_config_proto_rawDescOnce sync.Once
+	file_transport_internet_dtls_config_proto_rawDescData = file_transport_internet_dtls_config_proto_rawDesc
+)
+
+func file_transport_internet_dtls_config_proto_rawDescGZIP() []byte {
+	file_transport_internet_dtls_config_proto_rawDescOnce.Do(func() {
+		file_transport_internet_dtls_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_dtls_config_proto_rawDescData)
+	})
+	return file_transport_internet_dtls_config_proto_rawDescData
+}
+
+var file_transport_internet_dtls_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_transport_internet_dtls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_transport_internet_dtls_config_proto_goTypes = []any{
+	(DTLSMode)(0),  // 0: v2ray.core.transport.internet.dtls.DTLSMode
+	(*Config)(nil), // 1: v2ray.core.transport.internet.dtls.Config
+}
+var file_transport_internet_dtls_config_proto_depIdxs = []int32{
+	0, // 0: v2ray.core.transport.internet.dtls.Config.mode:type_name -> v2ray.core.transport.internet.dtls.DTLSMode
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_transport_internet_dtls_config_proto_init() }
+func file_transport_internet_dtls_config_proto_init() {
+	if File_transport_internet_dtls_config_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_transport_internet_dtls_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
+			switch v := v.(*Config); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_transport_internet_dtls_config_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_transport_internet_dtls_config_proto_goTypes,
+		DependencyIndexes: file_transport_internet_dtls_config_proto_depIdxs,
+		EnumInfos:         file_transport_internet_dtls_config_proto_enumTypes,
+		MessageInfos:      file_transport_internet_dtls_config_proto_msgTypes,
+	}.Build()
+	File_transport_internet_dtls_config_proto = out.File
+	file_transport_internet_dtls_config_proto_rawDesc = nil
+	file_transport_internet_dtls_config_proto_goTypes = nil
+	file_transport_internet_dtls_config_proto_depIdxs = nil
+}
diff --git a/transport/internet/dtls/config.proto b/transport/internet/dtls/config.proto
new file mode 100644
index 00000000..79992880
--- /dev/null
+++ b/transport/internet/dtls/config.proto
@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+package v2ray.core.transport.internet.dtls;
+option csharp_namespace = "V2Ray.Core.Transport.Internet.Dtls";
+option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/dtls";
+option java_package = "com.v2ray.core.transport.internet.dtls";
+option java_multiple_files = true;
+
+import "common/protoext/extensions.proto";
+
+enum DTLSMode {
+  INVALID = 0;
+  PSK = 1;
+}
+
+message Config {
+  option (v2ray.core.common.protoext.message_opt).type = "transport";
+  option (v2ray.core.common.protoext.message_opt).short_name = "dtls";
+
+  DTLSMode mode = 1;
+  bytes psk = 2;
+
+  uint32 mtu = 3;
+  uint32 replay_protection_window = 4;
+}
\ No newline at end of file
diff --git a/transport/internet/dtls/dialer.go b/transport/internet/dtls/dialer.go
new file mode 100644
index 00000000..882fbe5a
--- /dev/null
+++ b/transport/internet/dtls/dialer.go
@@ -0,0 +1,57 @@
+package dtls
+
+import (
+	"context"
+
+	"github.com/pion/dtls/v2"
+
+	"github.com/v2fly/v2ray-core/v5/common"
+	"github.com/v2fly/v2ray-core/v5/common/environment"
+	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
+	"github.com/v2fly/v2ray-core/v5/common/net"
+	"github.com/v2fly/v2ray-core/v5/common/session"
+	"github.com/v2fly/v2ray-core/v5/transport/internet"
+)
+
+func dialDTLS(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (net.Conn, error) {
+	transportConfiguration := streamSettings.ProtocolSettings.(*Config)
+
+	newError("dialing DTLS to ", dest).WriteToLog()
+
+	transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
+	dialer := transportEnvironment.Dialer()
+
+	rawConn, err := dialer.Dial(ctx, nil, dest, streamSettings.SocketSettings)
+	if err != nil {
+		return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err)
+	}
+	config := &dtls.Config{}
+	config.MTU = int(transportConfiguration.Mtu)
+	config.ReplayProtectionWindow = int(transportConfiguration.ReplayProtectionWindow)
+
+	switch transportConfiguration.Mode {
+	case DTLSMode_PSK:
+		config.PSK = func(bytes []byte) ([]byte, error) {
+			return transportConfiguration.Psk, nil
+		}
+		config.PSKIdentityHint = []byte("")
+		config.CipherSuites = []dtls.CipherSuiteID{dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256}
+	default:
+		return nil, newError("unknow dtls mode")
+	}
+	return dtls.Client(rawConn, config)
+}
+
+func dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (internet.Connection, error) {
+	newError("creating connection to ", dest).WriteToLog(session.ExportIDToError(ctx))
+
+	conn, err := dialDTLS(ctx, dest, streamSettings)
+	if err != nil {
+		return nil, newError("failed to dial request to ", dest).Base(err)
+	}
+	return internet.Connection(conn), nil
+}
+
+func init() {
+	common.Must(internet.RegisterTransportDialer(protocolName, dial))
+}
diff --git a/transport/internet/dtls/dtls.go b/transport/internet/dtls/dtls.go
new file mode 100644
index 00000000..a547a0bd
--- /dev/null
+++ b/transport/internet/dtls/dtls.go
@@ -0,0 +1,16 @@
+package dtls
+
+import (
+	"github.com/v2fly/v2ray-core/v5/common"
+	"github.com/v2fly/v2ray-core/v5/transport/internet"
+)
+
+//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
+
+const protocolName = "dtls"
+
+func init() {
+	common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
+		return new(Config)
+	}))
+}
diff --git a/transport/internet/dtls/errors.generated.go b/transport/internet/dtls/errors.generated.go
new file mode 100644
index 00000000..2cf2f117
--- /dev/null
+++ b/transport/internet/dtls/errors.generated.go
@@ -0,0 +1,9 @@
+package dtls
+
+import "github.com/v2fly/v2ray-core/v5/common/errors"
+
+type errPathObjHolder struct{}
+
+func newError(values ...interface{}) *errors.Error {
+	return errors.New(values...).WithPathObj(errPathObjHolder{})
+}
diff --git a/transport/internet/dtls/listener.go b/transport/internet/dtls/listener.go
new file mode 100644
index 00000000..36514a9a
--- /dev/null
+++ b/transport/internet/dtls/listener.go
@@ -0,0 +1,212 @@
+package dtls
+
+import (
+	"context"
+	"io"
+	gonet "net"
+	"sync"
+	"time"
+
+	"github.com/pion/dtls/v2"
+
+	"github.com/v2fly/v2ray-core/v5/common"
+	"github.com/v2fly/v2ray-core/v5/common/buf"
+	"github.com/v2fly/v2ray-core/v5/common/net"
+	"github.com/v2fly/v2ray-core/v5/transport/internet"
+	"github.com/v2fly/v2ray-core/v5/transport/internet/udp"
+)
+
+type Listener struct {
+	config *Config
+
+	sync.Mutex
+	addConn internet.ConnHandler
+	hub     *udp.Hub
+
+	sessions map[ConnectionID]*dTLSConnWrapped
+}
+
+func (l *Listener) Close() error {
+	return l.hub.Close()
+}
+
+func (l *Listener) Addr() net.Addr {
+	return l.hub.Addr()
+}
+
+type ConnectionID struct {
+	Remote net.Address
+	Port   net.Port
+}
+
+func newDTLSServerConn(src net.Destination, parent *Listener) *dTLSConn {
+	ctx := context.Background()
+	ctx, finish := context.WithCancel(ctx)
+	return &dTLSConn{
+		src:      src,
+		parent:   parent,
+		readChan: make(chan *buf.Buffer, 256),
+		ctx:      ctx,
+		finish:   finish,
+	}
+}
+
+type dTLSConnWrapped struct {
+	unencryptedConn *dTLSConn
+	dTLSConn        *dtls.Conn
+}
+
+type dTLSConn struct {
+	src    net.Destination
+	parent *Listener
+
+	readChan chan *buf.Buffer
+	ctx      context.Context
+	finish   func()
+}
+
+func (l *dTLSConn) Read(b []byte) (n int, err error) {
+	select {
+	case pack := <-l.readChan:
+		n := copy(b, pack.Bytes())
+		defer pack.Release()
+		if n < int(pack.Len()) {
+			return n, io.ErrShortBuffer
+		}
+		return n, nil
+	case <-l.ctx.Done():
+		return 0, l.ctx.Err()
+	}
+}
+
+func (l *dTLSConn) Write(b []byte) (n int, err error) {
+	return l.parent.hub.WriteTo(b, l.src)
+}
+
+func (l *dTLSConn) Close() error {
+	l.finish()
+	l.parent.Remove(l.src)
+	return nil
+}
+
+func (l *dTLSConn) LocalAddr() gonet.Addr {
+	return nil
+}
+
+func (l *dTLSConn) RemoteAddr() gonet.Addr {
+	return &net.UDPAddr{
+		IP:   l.src.Address.IP(),
+		Port: int(l.src.Port.Value()),
+	}
+}
+
+func (l *dTLSConn) SetDeadline(t time.Time) error {
+	return nil
+}
+
+func (l *dTLSConn) SetReadDeadline(t time.Time) error {
+	return nil
+}
+
+func (l *dTLSConn) SetWriteDeadline(t time.Time) error {
+	return nil
+}
+
+func (l *dTLSConn) OnReceive(payload *buf.Buffer) {
+	select {
+	case l.readChan <- payload:
+	default:
+	}
+}
+
+func NewListener(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (*Listener, error) {
+	transportConfiguration := streamSettings.ProtocolSettings.(*Config)
+	hub, err := udp.ListenUDP(ctx, address, port, streamSettings, udp.HubCapacity(1024))
+	if err != nil {
+		return nil, err
+	}
+	l := &Listener{
+		addConn:  addConn,
+		config:   transportConfiguration,
+		sessions: make(map[ConnectionID]*dTLSConnWrapped),
+	}
+	l.Lock()
+	l.hub = hub
+	l.Unlock()
+	newError("listening on ", address, ":", port).WriteToLog()
+
+	go l.handlePackets()
+	return l, err
+}
+
+func (l *Listener) handlePackets() {
+	receive := l.hub.Receive()
+	for payload := range receive {
+		l.OnReceive(payload.Payload, payload.Source)
+	}
+}
+
+func newDTLSConnWrapped(unencryptedConnection *dTLSConn, transportConfiguration *Config) (*dtls.Conn, error) {
+	config := &dtls.Config{}
+	config.MTU = int(transportConfiguration.Mtu)
+	config.ReplayProtectionWindow = int(transportConfiguration.ReplayProtectionWindow)
+
+	switch transportConfiguration.Mode {
+	case DTLSMode_PSK:
+		config.PSK = func(bytes []byte) ([]byte, error) {
+			return transportConfiguration.Psk, nil
+		}
+		config.PSKIdentityHint = []byte("")
+		config.CipherSuites = []dtls.CipherSuiteID{dtls.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256}
+	default:
+		newError("unknown dtls mode").WriteToLog()
+	}
+	dtlsConn, err := dtls.Server(unencryptedConnection, config)
+	if err != nil {
+		return nil, newError("unable to create dtls server conn").Base(err)
+	}
+	return dtlsConn, err
+}
+
+func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination) {
+	id := ConnectionID{
+		Remote: src.Address,
+		Port:   src.Port,
+	}
+	l.Lock()
+	defer l.Unlock()
+	conn, found := l.sessions[id]
+	if !found {
+		var err error
+		unEncryptedConn := newDTLSServerConn(src, l)
+		conn = &dTLSConnWrapped{unencryptedConn: unEncryptedConn}
+		l.sessions[id] = conn
+		go func() {
+			conn.dTLSConn, err = newDTLSConnWrapped(unEncryptedConn, l.config)
+			if err != nil {
+				newError("unable to accept new dtls connection").Base(err).WriteToLog()
+				return
+			}
+			l.addConn(internet.Connection(conn.dTLSConn))
+		}()
+	}
+	conn.unencryptedConn.OnReceive(payload)
+}
+
+func (l *Listener) Remove(src net.Destination) {
+	l.Lock()
+	defer l.Unlock()
+	id := ConnectionID{
+		Remote: src.Address,
+		Port:   src.Port,
+	}
+	delete(l.sessions, id)
+}
+
+func ListenDTLS(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
+	return NewListener(ctx, address, port, streamSettings, addConn)
+}
+
+func init() {
+	common.Must(internet.RegisterTransportListener(protocolName, ListenDTLS))
+}
diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go
index 68b56c19..3084e19d 100644
--- a/transport/internet/kcp/dialer.go
+++ b/transport/internet/kcp/dialer.go
@@ -8,6 +8,8 @@ import (
 	"github.com/v2fly/v2ray-core/v5/common"
 	"github.com/v2fly/v2ray-core/v5/common/buf"
 	"github.com/v2fly/v2ray-core/v5/common/dice"
+	"github.com/v2fly/v2ray-core/v5/common/environment"
+	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
 	"github.com/v2fly/v2ray-core/v5/common/net"
 	"github.com/v2fly/v2ray-core/v5/transport/internet"
 	"github.com/v2fly/v2ray-core/v5/transport/internet/tls"
@@ -47,7 +49,10 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet
 	dest.Network = net.Network_UDP
 	newError("dialing mKCP to ", dest).WriteToLog()
 
-	rawConn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
+	transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
+	dialer := transportEnvironment.Dialer()
+
+	rawConn, err := dialer.Dial(ctx, nil, dest, streamSettings.SocketSettings)
 	if err != nil {
 		return nil, newError("failed to dial to dest: ", err).AtWarning().Base(err)
 	}
diff --git a/transport/internet/request/assembler/packetconn/errors.generated.go b/transport/internet/request/assembler/packetconn/errors.generated.go
new file mode 100644
index 00000000..e7fcdf4d
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/errors.generated.go
@@ -0,0 +1,9 @@
+package packetconn
+
+import "github.com/v2fly/v2ray-core/v5/common/errors"
+
+type errPathObjHolder struct{}
+
+func newError(values ...interface{}) *errors.Error {
+	return errors.New(values...).WithPathObj(errPathObjHolder{})
+}
diff --git a/transport/internet/request/assembler/packetconn/packetConn.pb.go b/transport/internet/request/assembler/packetconn/packetConn.pb.go
new file mode 100644
index 00000000..7fec3244
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/packetConn.pb.go
@@ -0,0 +1,342 @@
+package packetconn
+
+import (
+	_ "github.com/v2fly/v2ray-core/v5/common/protoext"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	anypb "google.golang.org/protobuf/types/known/anypb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type ClientConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	UnderlyingTransportSetting *anypb.Any `protobuf:"bytes,1,opt,name=underlying_transport_setting,json=underlyingTransportSetting,proto3" json:"underlying_transport_setting,omitempty"`
+	UnderlyingTransportName    string     `protobuf:"bytes,2,opt,name=underlying_transport_name,json=underlyingTransportName,proto3" json:"underlying_transport_name,omitempty"`
+	MaxWriteDelay              int32      `protobuf:"varint,3,opt,name=max_write_delay,json=maxWriteDelay,proto3" json:"max_write_delay,omitempty"`
+	MaxRequestSize             int32      `protobuf:"varint,4,opt,name=max_request_size,json=maxRequestSize,proto3" json:"max_request_size,omitempty"`
+	PollingIntervalInitial     int32      `protobuf:"varint,5,opt,name=polling_interval_initial,json=pollingIntervalInitial,proto3" json:"polling_interval_initial,omitempty"`
+}
+
+func (x *ClientConfig) Reset() {
+	*x = ClientConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ClientConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ClientConfig) ProtoMessage() {}
+
+func (x *ClientConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ClientConfig.ProtoReflect.Descriptor instead.
+func (*ClientConfig) Descriptor() ([]byte, []int) {
+	return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *ClientConfig) GetUnderlyingTransportSetting() *anypb.Any {
+	if x != nil {
+		return x.UnderlyingTransportSetting
+	}
+	return nil
+}
+
+func (x *ClientConfig) GetUnderlyingTransportName() string {
+	if x != nil {
+		return x.UnderlyingTransportName
+	}
+	return ""
+}
+
+func (x *ClientConfig) GetMaxWriteDelay() int32 {
+	if x != nil {
+		return x.MaxWriteDelay
+	}
+	return 0
+}
+
+func (x *ClientConfig) GetMaxRequestSize() int32 {
+	if x != nil {
+		return x.MaxRequestSize
+	}
+	return 0
+}
+
+func (x *ClientConfig) GetPollingIntervalInitial() int32 {
+	if x != nil {
+		return x.PollingIntervalInitial
+	}
+	return 0
+}
+
+type ServerConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	UnderlyingTransportSetting     *anypb.Any `protobuf:"bytes,1,opt,name=underlying_transport_setting,json=underlyingTransportSetting,proto3" json:"underlying_transport_setting,omitempty"`
+	UnderlyingTransportName        string     `protobuf:"bytes,2,opt,name=underlying_transport_name,json=underlyingTransportName,proto3" json:"underlying_transport_name,omitempty"`
+	MaxWriteSize                   int32      `protobuf:"varint,3,opt,name=max_write_size,json=maxWriteSize,proto3" json:"max_write_size,omitempty"`
+	MaxWriteDurationMs             int32      `protobuf:"varint,4,opt,name=max_write_duration_ms,json=maxWriteDurationMs,proto3" json:"max_write_duration_ms,omitempty"`
+	MaxSimultaneousWriteConnection int32      `protobuf:"varint,5,opt,name=max_simultaneous_write_connection,json=maxSimultaneousWriteConnection,proto3" json:"max_simultaneous_write_connection,omitempty"`
+	PacketWritingBuffer            int32      `protobuf:"varint,6,opt,name=packet_writing_buffer,json=packetWritingBuffer,proto3" json:"packet_writing_buffer,omitempty"`
+}
+
+func (x *ServerConfig) Reset() {
+	*x = ServerConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ServerConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ServerConfig) ProtoMessage() {}
+
+func (x *ServerConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ServerConfig.ProtoReflect.Descriptor instead.
+func (*ServerConfig) Descriptor() ([]byte, []int) {
+	return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *ServerConfig) GetUnderlyingTransportSetting() *anypb.Any {
+	if x != nil {
+		return x.UnderlyingTransportSetting
+	}
+	return nil
+}
+
+func (x *ServerConfig) GetUnderlyingTransportName() string {
+	if x != nil {
+		return x.UnderlyingTransportName
+	}
+	return ""
+}
+
+func (x *ServerConfig) GetMaxWriteSize() int32 {
+	if x != nil {
+		return x.MaxWriteSize
+	}
+	return 0
+}
+
+func (x *ServerConfig) GetMaxWriteDurationMs() int32 {
+	if x != nil {
+		return x.MaxWriteDurationMs
+	}
+	return 0
+}
+
+func (x *ServerConfig) GetMaxSimultaneousWriteConnection() int32 {
+	if x != nil {
+		return x.MaxSimultaneousWriteConnection
+	}
+	return 0
+}
+
+func (x *ServerConfig) GetPacketWritingBuffer() int32 {
+	if x != nil {
+		return x.PacketWritingBuffer
+	}
+	return 0
+}
+
+var File_transport_internet_request_assembler_packetconn_packetConn_proto protoreflect.FileDescriptor
+
+var file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc = []byte{
+	0x0a, 0x40, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
+	0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2f, 0x61, 0x73, 0x73,
+	0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e,
+	0x6e, 0x2f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x12, 0x3a, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74,
+	0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+	0x74, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62,
+	0x6c, 0x65, 0x72, 0x2e, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x1a, 0x20,
+	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x78, 0x74, 0x2f,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe4, 0x02, 0x0a, 0x0c,
+	0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x56, 0x0a, 0x1c,
+	0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73,
+	0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x1a, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c,
+	0x79, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74,
+	0x74, 0x69, 0x6e, 0x67, 0x12, 0x3a, 0x0a, 0x19, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69,
+	0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79,
+	0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65,
+	0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x65,
+	0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x57, 0x72,
+	0x69, 0x74, 0x65, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f,
+	0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01,
+	0x28, 0x05, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69,
+	0x7a, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x05,
+	0x20, 0x01, 0x28, 0x05, 0x52, 0x16, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74,
+	0x65, 0x72, 0x76, 0x61, 0x6c, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x3a, 0x34, 0x82, 0xb5,
+	0x18, 0x30, 0x0a, 0x22, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x72, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2e,
+	0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f,
+	0x6e, 0x6e, 0x22, 0xb0, 0x03, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x12, 0x56, 0x0a, 0x1c, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e,
+	0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74,
+	0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
+	0x1a, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73,
+	0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3a, 0x0a, 0x19, 0x75,
+	0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
+	0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17,
+	0x75, 0x6e, 0x64, 0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
+	0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x77,
+	0x72, 0x69, 0x74, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
+	0x0c, 0x6d, 0x61, 0x78, 0x57, 0x72, 0x69, 0x74, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x31, 0x0a,
+	0x15, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x6d, 0x61,
+	0x78, 0x57, 0x72, 0x69, 0x74, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73,
+	0x12, 0x49, 0x0a, 0x21, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x74, 0x61, 0x6e,
+	0x65, 0x6f, 0x75, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1e, 0x6d, 0x61, 0x78,
+	0x53, 0x69, 0x6d, 0x75, 0x6c, 0x74, 0x61, 0x6e, 0x65, 0x6f, 0x75, 0x73, 0x57, 0x72, 0x69, 0x74,
+	0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x70,
+	0x61, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x66, 0x66, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x13, 0x70, 0x61, 0x63, 0x6b,
+	0x65, 0x74, 0x57, 0x72, 0x69, 0x74, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x3a,
+	0x34, 0x82, 0xb5, 0x18, 0x30, 0x0a, 0x22, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
+	0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c,
+	0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x0a, 0x70, 0x61, 0x63, 0x6b, 0x65,
+	0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x42, 0xcf, 0x01, 0x0a, 0x3e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32,
+	0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
+	0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2e, 0x70, 0x61,
+	0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68,
+	0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72,
+	0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73,
+	0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2f,
+	0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0xaa, 0x02, 0x3a, 0x56, 0x32, 0x52,
+	0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
+	0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x63,
+	0x6b, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescOnce sync.Once
+	file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData = file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc
+)
+
+func file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescGZIP() []byte {
+	file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescOnce.Do(func() {
+		file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData)
+	})
+	return file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDescData
+}
+
+var file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes = []any{
+	(*ClientConfig)(nil), // 0: v2ray.core.transport.internet.request.assembler.packetconn.ClientConfig
+	(*ServerConfig)(nil), // 1: v2ray.core.transport.internet.request.assembler.packetconn.ServerConfig
+	(*anypb.Any)(nil),    // 2: google.protobuf.Any
+}
+var file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs = []int32{
+	2, // 0: v2ray.core.transport.internet.request.assembler.packetconn.ClientConfig.underlying_transport_setting:type_name -> google.protobuf.Any
+	2, // 1: v2ray.core.transport.internet.request.assembler.packetconn.ServerConfig.underlying_transport_setting:type_name -> google.protobuf.Any
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_transport_internet_request_assembler_packetconn_packetConn_proto_init() }
+func file_transport_internet_request_assembler_packetconn_packetConn_proto_init() {
+	if File_transport_internet_request_assembler_packetconn_packetConn_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[0].Exporter = func(v any, i int) any {
+			switch v := v.(*ClientConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes[1].Exporter = func(v any, i int) any {
+			switch v := v.(*ServerConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes,
+		DependencyIndexes: file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs,
+		MessageInfos:      file_transport_internet_request_assembler_packetconn_packetConn_proto_msgTypes,
+	}.Build()
+	File_transport_internet_request_assembler_packetconn_packetConn_proto = out.File
+	file_transport_internet_request_assembler_packetconn_packetConn_proto_rawDesc = nil
+	file_transport_internet_request_assembler_packetconn_packetConn_proto_goTypes = nil
+	file_transport_internet_request_assembler_packetconn_packetConn_proto_depIdxs = nil
+}
diff --git a/transport/internet/request/assembler/packetconn/packetConn.proto b/transport/internet/request/assembler/packetconn/packetConn.proto
new file mode 100644
index 00000000..fd4d6cc6
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/packetConn.proto
@@ -0,0 +1,35 @@
+syntax = "proto3";
+
+package v2ray.core.transport.internet.request.assembler.packetconn;
+option csharp_namespace = "V2Ray.Core.Transport.Internet.Request.Assembler.Packetconn";
+option go_package = "github.com/v2fly/v2ray-core/v5/transport/internet/request/assembler/packetconn";
+option java_package = "com.v2ray.core.transport.internet.request.assembler.packetconn";
+option java_multiple_files = true;
+
+import "common/protoext/extensions.proto";
+import "google/protobuf/any.proto";
+
+message ClientConfig {
+  option (v2ray.core.common.protoext.message_opt).type = "transport.request.assembler.client";
+  option (v2ray.core.common.protoext.message_opt).short_name = "packetconn";
+
+  google.protobuf.Any underlying_transport_setting = 1;
+  string underlying_transport_name = 2;
+
+  int32 max_write_delay = 3;
+  int32 max_request_size = 4;
+  int32 polling_interval_initial = 5;
+}
+
+message ServerConfig {
+  option (v2ray.core.common.protoext.message_opt).type = "transport.request.assembler.server";
+  option (v2ray.core.common.protoext.message_opt).short_name = "packetconn";
+
+  google.protobuf.Any underlying_transport_setting = 1;
+  string underlying_transport_name = 2;
+
+  int32 max_write_size = 3;
+  int32 max_write_duration_ms = 4;
+  int32 max_simultaneous_write_connection = 5;
+  int32 packet_writing_buffer = 6;
+}
\ No newline at end of file
diff --git a/transport/internet/request/assembler/packetconn/packetbundle.go b/transport/internet/request/assembler/packetconn/packetbundle.go
new file mode 100644
index 00000000..976d3036
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/packetbundle.go
@@ -0,0 +1,48 @@
+package packetconn
+
+import (
+	"encoding/binary"
+	"io"
+)
+
+func NewPacketBundle() PacketBundle {
+	return &packetBundle{}
+}
+
+type packetBundle struct{}
+
+func (p *packetBundle) Overhead() int {
+	return 2
+}
+
+func (p *packetBundle) WriteToBundle(b []byte, writer io.Writer) (err error) {
+	err = binary.Write(writer, binary.BigEndian, uint16(len(b)))
+	if err != nil {
+		return
+	}
+	_, err = writer.Write(b)
+	return
+}
+
+func (p *packetBundle) ReadFromBundle(writer io.Reader) (b []byte, err error) {
+	var length uint16
+	err = binary.Read(writer, binary.BigEndian, &length)
+	if err != nil {
+		return
+	}
+	b = make([]byte, length)
+	n, err := io.ReadFull(writer, b)
+	if err != nil {
+		return
+	}
+	if n != int(length) {
+		return nil, io.ErrUnexpectedEOF
+	}
+	return
+}
+
+type PacketBundle interface {
+	Overhead() int
+	WriteToBundle(b []byte, writer io.Writer) (err error)
+	ReadFromBundle(writer io.Reader) (b []byte, err error)
+}
diff --git a/transport/internet/request/assembler/packetconn/packetconn.go b/transport/internet/request/assembler/packetconn/packetconn.go
new file mode 100644
index 00000000..01b6df44
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/packetconn.go
@@ -0,0 +1,3 @@
+package packetconn
+
+//go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
diff --git a/transport/internet/request/assembler/packetconn/req2packet.go b/transport/internet/request/assembler/packetconn/req2packet.go
new file mode 100644
index 00000000..c8e7e881
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/req2packet.go
@@ -0,0 +1,441 @@
+package packetconn
+
+import (
+	"bytes"
+	"context"
+	"crypto/rand"
+	"io"
+	"sync"
+	"time"
+
+	"github.com/golang-collections/go-datastructures/queue"
+
+	"github.com/v2fly/v2ray-core/v5/transport/internet/request"
+)
+
+func newRequestToPacketConnClient(ctx context.Context, config *ClientConfig) (*requestToPacketConnClient, error) { //nolint: unparam
+	return &requestToPacketConnClient{ctx: ctx, config: config}, nil
+}
+
+type requestToPacketConnClient struct {
+	assembly request.TransportClientAssembly
+	ctx      context.Context
+	config   *ClientConfig
+}
+
+func (r *requestToPacketConnClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) {
+	r.assembly = assembly
+}
+
+func (r *requestToPacketConnClient) Dial() (io.ReadWriteCloser, error) {
+	sessionID := make([]byte, 16)
+	_, err := rand.Read(sessionID)
+	if err != nil {
+		return nil, err
+	}
+	ctxWithCancel, cancel := context.WithCancel(r.ctx)
+
+	clientSess := &requestToPacketConnClientSession{
+		sessionID:              sessionID,
+		currentPollingInterval: int(r.config.PollingIntervalInitial),
+		maxRequestSize:         int(r.config.MaxRequestSize),
+		maxWriteDelay:          int(r.config.MaxWriteDelay),
+		assembly:               r.assembly,
+		writerChan:             make(chan []byte, 256),
+		readerChan:             make(chan []byte, 256),
+		ctx:                    ctxWithCancel,
+		finish:                 cancel,
+	}
+	go clientSess.keepRunning()
+	return clientSess, nil
+}
+
+type requestToPacketConnClientSession struct {
+	sessionID              []byte
+	currentPollingInterval int
+
+	maxRequestSize int
+	maxWriteDelay  int
+
+	assembly   request.TransportClientAssembly
+	writerChan chan []byte
+	readerChan chan []byte
+	ctx        context.Context
+	finish     func()
+	nextWrite  []byte
+}
+
+func (r *requestToPacketConnClientSession) keepRunning() {
+	for r.ctx.Err() == nil {
+		r.runOnce()
+	}
+}
+
+func (r *requestToPacketConnClientSession) runOnce() {
+	requestBody := bytes.NewBuffer(nil)
+	waitTimer := time.NewTimer(time.Duration(r.currentPollingInterval) * time.Millisecond)
+	var seenPacket bool
+	packetBundler := NewPacketBundle()
+copyFromChan:
+	for {
+		select {
+		case <-r.ctx.Done():
+			return
+		case <-waitTimer.C:
+			break copyFromChan
+		case packet := <-r.writerChan:
+			if !seenPacket {
+				seenPacket = true
+				waitTimer.Stop()
+				waitTimer.Reset(time.Duration(r.maxWriteDelay) * time.Millisecond)
+			}
+			sizeOffset := packetBundler.Overhead() + len(packet)
+			if requestBody.Len()+sizeOffset > r.maxRequestSize {
+				r.nextWrite = packet
+				break copyFromChan
+			}
+			err := packetBundler.WriteToBundle(packet, requestBody)
+			if err != nil {
+				newError("failed to write to bundle").Base(err).WriteToLog()
+			}
+		}
+	}
+	waitTimer.Stop()
+	go func() {
+		reader, writer := io.Pipe()
+		defer writer.Close()
+		streamingRespOpt := &pipedStreamingRespOption{writer}
+		go func() {
+			for {
+				if packet, err := packetBundler.ReadFromBundle(reader); err == nil {
+					r.readerChan <- packet
+				} else {
+					return
+				}
+			}
+		}()
+		resp, err := r.assembly.Tripper().RoundTrip(r.ctx, request.Request{Data: requestBody.Bytes(), ConnectionTag: r.sessionID},
+			streamingRespOpt)
+		if err != nil {
+			newError("failed to roundtrip").Base(err).WriteToLog()
+			if r.ctx.Err() != nil {
+				return
+			}
+		}
+		if resp.Data != nil && len(resp.Data) != 0 {
+			respReader := bytes.NewReader(resp.Data)
+			for respReader.Len() != 0 {
+				packet, err := packetBundler.ReadFromBundle(respReader)
+				if err != nil {
+					newError("failed to read from bundle").Base(err).WriteToLog()
+					if r.ctx.Err() != nil {
+						return
+					}
+				}
+				r.readerChan <- packet
+			}
+		}
+	}()
+}
+
+type pipedStreamingRespOption struct {
+	writer *io.PipeWriter
+}
+
+func (p *pipedStreamingRespOption) RoundTripperOption() {
+}
+
+func (p *pipedStreamingRespOption) GetResponseWriter() io.Writer {
+	return p.writer
+}
+
+func (r *requestToPacketConnClientSession) Write(p []byte) (n int, err error) {
+	buf := make([]byte, len(p))
+	copy(buf, p)
+	select {
+	case <-r.ctx.Done():
+		return 0, r.ctx.Err()
+	case r.writerChan <- buf:
+		return len(p), nil
+	}
+}
+
+func (r *requestToPacketConnClientSession) Read(p []byte) (n int, err error) {
+	select {
+	case <-r.ctx.Done():
+		return 0, r.ctx.Err()
+	case buf := <-r.readerChan:
+		copy(p, buf)
+		return len(buf), nil
+	}
+}
+
+func (r *requestToPacketConnClientSession) Close() error {
+	r.finish()
+	return nil
+}
+
+func newRequestToPacketConnServer(ctx context.Context, config *ServerConfig) *requestToPacketConnServer {
+	return &requestToPacketConnServer{
+		sessionMap: sync.Map{},
+		ctx:        ctx,
+		config:     config,
+	}
+}
+
+type requestToPacketConnServer struct {
+	packetSessionReceiver request.SessionReceiver
+
+	sessionMap sync.Map
+
+	ctx    context.Context
+	config *ServerConfig
+}
+
+func (r *requestToPacketConnServer) onSessionReceiverReady(sessrecv request.SessionReceiver) {
+	r.packetSessionReceiver = sessrecv
+}
+
+func (r *requestToPacketConnServer) OnRoundTrip(ctx context.Context, req request.Request,
+	opts ...request.RoundTripperOption,
+) (resp request.Response, err error) {
+	SessionID := req.ConnectionTag
+	if SessionID == nil {
+		return request.Response{}, newError("nil session id")
+	}
+	sessionID := string(SessionID)
+	var session *requestToPacketConnServerSession
+	sessionAny, found := r.sessionMap.Load(sessionID)
+	if found {
+		var ok bool
+		session, ok = sessionAny.(*requestToPacketConnServerSession)
+		if !ok {
+			return request.Response{}, newError("failed to cast session")
+		}
+	}
+	if !found {
+		ctxWithFinish, finish := context.WithCancel(ctx)
+		session = &requestToPacketConnServerSession{
+			SessionID:                      SessionID,
+			writingConnectionQueue:         queue.New(64),
+			writerChan:                     make(chan []byte, int(r.config.PacketWritingBuffer)),
+			readerChan:                     make(chan []byte, 256),
+			ctx:                            ctxWithFinish,
+			finish:                         finish,
+			server:                         r,
+			maxWriteSize:                   int(r.config.MaxWriteSize),
+			maxWriteDuration:               int(r.config.MaxWriteDurationMs),
+			maxSimultaneousWriteConnection: int(r.config.MaxSimultaneousWriteConnection),
+		}
+		_, loaded := r.sessionMap.LoadOrStore(sessionID, session)
+		if !loaded {
+			err = r.packetSessionReceiver.OnNewSession(ctx, session)
+		}
+	}
+	if err != nil {
+		return request.Response{}, err
+	}
+	return session.OnRoundTrip(ctx, req, opts...)
+}
+
+func (r *requestToPacketConnServer) removeSessionID(sessionID []byte) {
+	r.sessionMap.Delete(string(sessionID))
+}
+
+type requestToPacketConnServerSession struct {
+	SessionID []byte
+
+	writingConnectionQueue *queue.Queue
+
+	writerChan chan []byte
+	readerChan chan []byte
+	ctx        context.Context
+	finish     func()
+	server     *requestToPacketConnServer
+
+	maxWriteSize                   int
+	maxWriteDuration               int
+	maxSimultaneousWriteConnection int
+}
+
+func (r *requestToPacketConnServerSession) Read(p []byte) (n int, err error) {
+	select {
+	case <-r.ctx.Done():
+		return 0, r.ctx.Err()
+	case buf := <-r.readerChan:
+		copy(p, buf)
+		return len(buf), nil
+	}
+}
+
+var debugStats struct {
+	packetWritten int
+	packetDropped int
+}
+
+/*
+var _ = func() bool {
+	go func() {
+		for {
+			time.Sleep(time.Second)
+			newError("packet written: ", debugStats.packetWritten, " packet dropped: ", debugStats.packetDropped).WriteToLog()
+		}
+	}()
+	return true
+}()*/
+
+func (r *requestToPacketConnServerSession) Write(p []byte) (n int, err error) {
+	buf := make([]byte, len(p))
+	copy(buf, p)
+	select {
+	case <-r.ctx.Done():
+		return 0, r.ctx.Err()
+	case r.writerChan <- buf:
+		debugStats.packetWritten++
+		return len(p), nil
+	default: // This write will be called from global listener's routine, it must not block
+		debugStats.packetDropped++
+		return len(p), nil
+	}
+}
+
+func (r *requestToPacketConnServerSession) Close() error {
+	r.server.removeSessionID(r.SessionID)
+	r.finish()
+	return nil
+}
+
+type writingConnection struct {
+	focus     func()
+	finish    func()
+	finishCtx context.Context
+}
+
+func (r *requestToPacketConnServerSession) OnRoundTrip(ctx context.Context, req request.Request,
+	opts ...request.RoundTripperOption,
+) (resp request.Response, err error) {
+	// TODO: fix connection graceful close
+	var streamingRespWriter io.Writer
+	var streamingRespWriterFlusher request.OptionSupportsStreamingResponseExtensionFlusher
+	for _, opt := range opts {
+		if streamingRespOpt, ok := opt.(request.OptionSupportsStreamingResponse); ok {
+			streamingRespWriter = streamingRespOpt.GetResponseWriter()
+			if streamingRespWriterFlusherOpt, ok := opt.(request.OptionSupportsStreamingResponseExtensionFlusher); ok {
+				streamingRespWriterFlusher = streamingRespWriterFlusherOpt
+			}
+		}
+	}
+	packetBundler := NewPacketBundle()
+	reqReader := bytes.NewReader(req.Data)
+	for reqReader.Len() != 0 {
+		packet, err := packetBundler.ReadFromBundle(reqReader)
+		if err != nil {
+			err = newError("failed to read from bundle").Base(err)
+			return request.Response{}, err
+		}
+		r.readerChan <- packet
+	}
+	onFocusCtx, focus := context.WithCancel(ctx)
+	onFinishCtx, finish := context.WithCancel(ctx)
+	r.writingConnectionQueue.Put(&writingConnection{
+		focus:     focus,
+		finish:    finish,
+		finishCtx: onFinishCtx,
+	})
+
+	amountToEnd := r.writingConnectionQueue.Len() - int64(r.maxSimultaneousWriteConnection)
+	for amountToEnd > 0 {
+		{
+			_, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool {
+				i.(*writingConnection).finish()
+				amountToEnd--
+				return amountToEnd > 0
+			})
+		}
+	}
+
+	{
+		_, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool {
+			i.(*writingConnection).focus()
+			return false
+		})
+	}
+
+	bufferedRespWriter := bytes.NewBuffer(nil)
+	finishWrite := func() {
+		resp.Data = bufferedRespWriter.Bytes()
+		{
+			_, _ = r.writingConnectionQueue.TakeUntil(func(i interface{}) bool {
+				i.(*writingConnection).focus()
+				if i.(*writingConnection).finishCtx.Err() != nil { //nolint: gosimple
+					return true
+				}
+				return false
+			})
+		}
+	}
+
+	progressiveSend := streamingRespWriter != nil
+	var respWriter io.Writer
+	if progressiveSend {
+		respWriter = streamingRespWriter
+	} else {
+		respWriter = bufferedRespWriter
+	}
+
+	var bytesSent int
+	onReceivePacket := func(packet []byte) bool {
+		bytesSent += len(packet) + packetBundler.Overhead()
+		err := packetBundler.WriteToBundle(packet, respWriter)
+		if err != nil {
+			newError("failed to write to bundle").Base(err).WriteToLog()
+		}
+		if streamingRespWriterFlusher != nil {
+			streamingRespWriterFlusher.Flush()
+		}
+		if bytesSent >= r.maxWriteSize {
+			return false
+		}
+		return true
+	}
+
+	finishWriteTimer := time.NewTimer(time.Millisecond * time.Duration(r.maxWriteDuration))
+
+	if !progressiveSend {
+		select {
+		case <-onFocusCtx.Done():
+		case <-onFinishCtx.Done():
+			finishWrite()
+			return resp, nil
+		}
+	} else {
+		select {
+		case <-onFinishCtx.Done():
+			finishWrite()
+			return resp, nil
+		default:
+		}
+	}
+	firstRead := true
+	for {
+		select {
+		case <-onFinishCtx.Done():
+			finishWrite()
+			finishWriteTimer.Stop()
+			return resp, nil
+		case packet := <-r.writerChan:
+			keepSending := onReceivePacket(packet)
+			if firstRead {
+				firstRead = false
+			}
+			if !keepSending {
+				finishWrite()
+				finishWriteTimer.Stop()
+				return resp, nil
+			}
+		case <-finishWriteTimer.C:
+			finishWrite()
+			return resp, nil
+		}
+	}
+}
diff --git a/transport/internet/request/assembler/packetconn/udpassembler.go b/transport/internet/request/assembler/packetconn/udpassembler.go
new file mode 100644
index 00000000..19ac0910
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/udpassembler.go
@@ -0,0 +1,78 @@
+package packetconn
+
+import (
+	"golang.org/x/net/context"
+
+	"github.com/v2fly/v2ray-core/v5/common"
+	"github.com/v2fly/v2ray-core/v5/common/environment"
+	"github.com/v2fly/v2ray-core/v5/common/net"
+	"github.com/v2fly/v2ray-core/v5/common/serial"
+	"github.com/v2fly/v2ray-core/v5/transport/internet"
+)
+
+type wrappedTransportEnvironment struct {
+	environment.TransportEnvironment
+	client *requestToPacketConnClient
+	server *requestToPacketConnServer
+}
+
+func (w *wrappedTransportEnvironment) Listen(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.Listener, error) {
+	return nil, newError("not implemented")
+}
+
+func (w *wrappedTransportEnvironment) ListenPacket(ctx context.Context, addr net.Addr, sockopt *internet.SocketConfig) (net.PacketConn, error) {
+	packetConn := newWrappedPacketConn(ctx)
+	w.server.onSessionReceiverReady(packetConn)
+	return packetConn, nil
+}
+
+func (w *wrappedTransportEnvironment) Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *internet.SocketConfig) (net.Conn, error) {
+	session, err := w.client.Dial()
+	if err != nil {
+		return nil, err
+	}
+	return newWrappedConn(session), nil
+}
+
+func (w *wrappedTransportEnvironment) Dialer() internet.SystemDialer {
+	return w
+}
+
+func (w *wrappedTransportEnvironment) Listener() internet.SystemListener {
+	return w
+}
+
+func newUDPAssemblerServerFromConfig(ctx context.Context, config *ServerConfig) (*udpAssemblerServer, error) {
+	instance, err := serial.GetInstanceOf(config.UnderlyingTransportSetting)
+	if err != nil {
+		return nil, newError("failed to get instance of underlying transport").Base(err).AtError()
+	}
+	memcfg := &internet.MemoryStreamConfig{ProtocolName: config.UnderlyingTransportName, ProtocolSettings: instance}
+	return newUDPAssemblerServer(ctx, config, memcfg), nil
+}
+
+func newUDPAssemblerClientFromConfig(ctx context.Context, config *ClientConfig) (*udpAssemblerClient, error) {
+	instance, err := serial.GetInstanceOf(config.UnderlyingTransportSetting)
+	if err != nil {
+		return nil, newError("failed to get instance of underlying transport").Base(err).AtError()
+	}
+	memcfg := &internet.MemoryStreamConfig{ProtocolName: config.UnderlyingTransportName, ProtocolSettings: instance}
+	return newUDPAssemblerClient(ctx, config, memcfg), nil
+}
+
+func init() {
+	common.Must(common.RegisterConfig((*ServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
+		serverConfig, ok := config.(*ServerConfig)
+		if !ok {
+			return nil, newError("not a ServerConfig")
+		}
+		return newUDPAssemblerServerFromConfig(ctx, serverConfig)
+	}))
+	common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
+		clientConfig, ok := config.(*ClientConfig)
+		if !ok {
+			return nil, newError("not a ClientConfig")
+		}
+		return newUDPAssemblerClientFromConfig(ctx, clientConfig)
+	}))
+}
diff --git a/transport/internet/request/assembler/packetconn/udpassemblerClient.go b/transport/internet/request/assembler/packetconn/udpassemblerClient.go
new file mode 100644
index 00000000..c85802ab
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/udpassemblerClient.go
@@ -0,0 +1,81 @@
+package packetconn
+
+import (
+	"io"
+	gonet "net"
+	"sync"
+	"time"
+
+	"golang.org/x/net/context"
+
+	"github.com/v2fly/v2ray-core/v5/common/environment"
+	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
+	"github.com/v2fly/v2ray-core/v5/common/net"
+	"github.com/v2fly/v2ray-core/v5/transport/internet"
+	"github.com/v2fly/v2ray-core/v5/transport/internet/request"
+)
+
+type udpAssemblerClient struct {
+	ctx            context.Context
+	streamSettings *internet.MemoryStreamConfig
+	assembly       request.TransportClientAssembly
+	req2connc      *requestToPacketConnClient
+}
+
+func (u *udpAssemblerClient) NewSession(ctx context.Context, opts ...request.SessionOption) (request.Session, error) {
+	return u.dial(net.Destination{})
+}
+
+func (u *udpAssemblerClient) OnTransportClientAssemblyReady(assembly request.TransportClientAssembly) {
+	u.assembly = assembly
+	u.req2connc.OnTransportClientAssemblyReady(assembly)
+}
+
+func newWrappedConn(in io.ReadWriteCloser) net.Conn {
+	return wrappedConn{in}
+}
+
+type wrappedConn struct {
+	io.ReadWriteCloser
+}
+
+func (w wrappedConn) LocalAddr() gonet.Addr {
+	return nil
+}
+
+func (w wrappedConn) RemoteAddr() gonet.Addr {
+	return nil
+}
+
+func (w wrappedConn) SetDeadline(t time.Time) error {
+	return nil
+}
+
+func (w wrappedConn) SetReadDeadline(t time.Time) error {
+	return nil
+}
+
+func (w wrappedConn) SetWriteDeadline(t time.Time) error {
+	return nil
+}
+
+func newWrappedPacketConn(ctx context.Context) *wrappedPacketConn {
+	ctxWithCancel, cancel := context.WithCancel(ctx)
+	return &wrappedPacketConn{
+		conn:     make(map[string]*serverSession),
+		readChan: make(chan packet, 16), ctx: ctxWithCancel, finish: cancel, connLock: &sync.Mutex{},
+	}
+}
+
+func newUDPAssemblerClient(ctx context.Context, config *ClientConfig, streamSettings *internet.MemoryStreamConfig) *udpAssemblerClient {
+	transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
+	transportEnvironmentWrapped := &wrappedTransportEnvironment{TransportEnvironment: transportEnvironment}
+	transportEnvironmentWrapped.client, _ = newRequestToPacketConnClient(ctx, config)
+	wrappedContext := envctx.ContextWithEnvironment(ctx, transportEnvironmentWrapped)
+	return &udpAssemblerClient{ctx: wrappedContext, streamSettings: streamSettings, req2connc: transportEnvironmentWrapped.client}
+}
+
+func (u *udpAssemblerClient) dial(dest net.Destination) (internet.Connection, error) {
+	_ = dest
+	return internet.Dial(u.ctx, net.TCPDestination(net.LocalHostIP, 0), u.streamSettings)
+}
diff --git a/transport/internet/request/assembler/packetconn/udpassemblerServer.go b/transport/internet/request/assembler/packetconn/udpassemblerServer.go
new file mode 100644
index 00000000..a8135f3d
--- /dev/null
+++ b/transport/internet/request/assembler/packetconn/udpassemblerServer.go
@@ -0,0 +1,166 @@
+package packetconn
+
+import (
+	"crypto/rand"
+	"io"
+	"net"
+	"sync"
+	"time"
+
+	"golang.org/x/net/context"
+
+	"github.com/v2fly/v2ray-core/v5/common/environment"
+	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
+	net2 "github.com/v2fly/v2ray-core/v5/common/net"
+	"github.com/v2fly/v2ray-core/v5/transport/internet"
+	"github.com/v2fly/v2ray-core/v5/transport/internet/request"
+)
+
+type packet struct {
+	addr string
+	data []byte
+}
+
+type wrappedPacketConn struct {
+	connLock *sync.Mutex
+	conn     map[string]*serverSession
+
+	readChan chan packet
+
+	ctx    context.Context
+	finish func()
+}
+
+func (w *wrappedPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
+	select {
+	case pack := <-w.readChan:
+		n := copy(p, pack.data)
+		if n < len(pack.data) {
+			return n, nil, io.ErrShortBuffer
+		}
+		return n, &net.UDPAddr{IP: net2.IP(pack.addr)}, nil
+	case <-w.ctx.Done():
+		return 0, nil, w.ctx.Err()
+	}
+}
+
+func (w *wrappedPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
+	w.connLock.Lock()
+	conn := w.conn[string(addr.(*net.UDPAddr).IP)]
+	w.connLock.Unlock()
+	return conn.Write(p)
+}
+
+func (w *wrappedPacketConn) Close() error {
+	w.finish()
+	return nil
+}
+
+func (w *wrappedPacketConn) LocalAddr() net.Addr {
+	return nil
+}
+
+func (w *wrappedPacketConn) SetDeadline(t time.Time) error {
+	return nil
+}
+
+func (w *wrappedPacketConn) SetReadDeadline(t time.Time) error {
+	return nil
+}
+
+func (w *wrappedPacketConn) SetWriteDeadline(t time.Time) error {
+	return nil
+}
+
+func (w wrappedPacketConn) OnNewSession(ctx context.Context, sess request.Session, opts ...request.SessionOption) error {
+	imaginaryAddr := net2.UDPAddr{
+		IP:   net2.AnyIPv6.IP(),
+		Port: 0,
+	}
+	rand.Read([]byte(imaginaryAddr.IP))
+	session := newServerSession(ctx, sess, string(imaginaryAddr.IP), &w)
+	w.connLock.Lock()
+	w.conn[string(imaginaryAddr.IP)] = session
+	w.connLock.Unlock()
+	session.start()
+	return nil
+}
+
+func newServerSession(ctx context.Context, sess request.Session, name string, listener *wrappedPacketConn) *serverSession {
+	_ = ctx
+	return &serverSession{session: sess, name: name, listener: listener}
+}
+
+type serverSession struct {
+	name     string
+	session  request.Session
+	listener *wrappedPacketConn
+}
+
+func (s *serverSession) start() {
+	go func() {
+		for {
+			select {
+			case <-s.listener.ctx.Done():
+				return
+			default:
+				buf := make([]byte, 2000)
+				n, err := s.session.Read(buf)
+				if err != nil || n > 2000 {
+					return
+				}
+				s.listener.readChan <- packet{s.name, buf[:n]}
+			}
+		}
+	}()
+}
+
+func (s *serverSession) Write(p []byte) (int, error) {
+	return s.session.Write(p)
+}
+
+type udpAssemblerServer struct {
+	ctx            context.Context
+	streamSettings *internet.MemoryStreamConfig
+	assembly       request.TransportServerAssembly
+	req2packs      *requestToPacketConnServer
+	listener       internet.Listener
+}
+
+func (u *udpAssemblerServer) Start() error {
+	listener, err := u.listen(net2.LocalHostIP, 0)
+	if err != nil {
+		return newError("failed to listen").Base(err).AtError()
+	}
+	u.listener = listener
+	return nil
+}
+
+func (u *udpAssemblerServer) Close() error {
+	return u.listener.Close()
+}
+
+func (u *udpAssemblerServer) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) {
+	return u.req2packs.OnRoundTrip(ctx, req, opts...)
+}
+
+func (u *udpAssemblerServer) OnTransportServerAssemblyReady(assembly request.TransportServerAssembly) {
+	u.assembly = assembly
+}
+
+func newUDPAssemblerServer(ctx context.Context, config *ServerConfig, streamSettings *internet.MemoryStreamConfig) *udpAssemblerServer {
+	transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
+	transportEnvironmentWrapped := &wrappedTransportEnvironment{TransportEnvironment: transportEnvironment}
+	transportEnvironmentWrapped.server = newRequestToPacketConnServer(ctx, config)
+	wrappedContext := envctx.ContextWithEnvironment(ctx, transportEnvironmentWrapped)
+	return &udpAssemblerServer{ctx: wrappedContext, streamSettings: streamSettings, req2packs: transportEnvironmentWrapped.server}
+}
+
+func (u *udpAssemblerServer) listen(address net2.Address, port net2.Port) (internet.Listener, error) {
+	return internet.ListenTCP(u.ctx, address, port, u.streamSettings, func(connection internet.Connection) {
+		err := u.assembly.SessionReceiver().OnNewSession(u.ctx, connection)
+		if err != nil {
+			newError("failed to handle new session").Base(err).WriteToLog()
+		}
+	})
+}
diff --git a/transport/internet/request/assembly/hub.go b/transport/internet/request/assembly/hub.go
index 7a6fc404..8e9e21ef 100644
--- a/transport/internet/request/assembly/hub.go
+++ b/transport/internet/request/assembly/hub.go
@@ -36,6 +36,11 @@ func (s server) Close() error {
 	if err := s.tripper.Close(); err != nil {
 		return newError("failed to close tripper").Base(err)
 	}
+	if runnableAssembler, ok := s.assembler.(common.Runnable); ok {
+		if err := runnableAssembler.Close(); err != nil {
+			return newError("failed to close assembler").Base(err)
+		}
+	}
 	return nil
 }
 
@@ -127,6 +132,12 @@ func listenRequest(ctx context.Context, address net.Address, port net.Port, stre
 		return nil, newError("failed to start tripper").Base(err)
 	}
 
+	if runnableAssembler, ok := serverAssembly.assembler.(common.Runnable); ok {
+		if err := runnableAssembler.Start(); err != nil {
+			return nil, newError("failed to start assembler").Base(err)
+		}
+	}
+
 	return serverAssembly, nil
 }
 
diff --git a/transport/internet/request/roundtripper.go b/transport/internet/request/roundtripper.go
index abb8f2f8..c921d1be 100644
--- a/transport/internet/request/roundtripper.go
+++ b/transport/internet/request/roundtripper.go
@@ -2,6 +2,7 @@ package request
 
 import (
 	"context"
+	"io"
 
 	"github.com/v2fly/v2ray-core/v5/common"
 )
@@ -36,3 +37,12 @@ type Request struct {
 type Response struct {
 	Data []byte
 }
+
+type OptionSupportsStreamingResponse interface {
+	RoundTripperOption
+	GetResponseWriter() io.Writer
+}
+
+type OptionSupportsStreamingResponseExtensionFlusher interface {
+	Flush()
+}
diff --git a/transport/internet/request/roundtripper/httprt/config.pb.go b/transport/internet/request/roundtripper/httprt/config.pb.go
index a93feb0a..f2950eec 100644
--- a/transport/internet/request/roundtripper/httprt/config.pb.go
+++ b/transport/internet/request/roundtripper/httprt/config.pb.go
@@ -20,7 +20,9 @@ type ClientConfig struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Http *HTTPConfig `protobuf:"bytes,1,opt,name=http,proto3" json:"http,omitempty"`
+	Http       *HTTPConfig `protobuf:"bytes,1,opt,name=http,proto3" json:"http,omitempty"`
+	AllowHttp  bool        `protobuf:"varint,2,opt,name=allow_http,json=allowHttp,proto3" json:"allow_http,omitempty"`
+	H2PoolSize int32       `protobuf:"varint,3,opt,name=h2_pool_size,json=h2PoolSize,proto3" json:"h2_pool_size,omitempty"`
 }
 
 func (x *ClientConfig) Reset() {
@@ -62,6 +64,20 @@ func (x *ClientConfig) GetHttp() *HTTPConfig {
 	return nil
 }
 
+func (x *ClientConfig) GetAllowHttp() bool {
+	if x != nil {
+		return x.AllowHttp
+	}
+	return false
+}
+
+func (x *ClientConfig) GetH2PoolSize() int32 {
+	if x != nil {
+		return x.H2PoolSize
+	}
+	return 0
+}
+
 type ServerConfig struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -184,48 +200,52 @@ var file_transport_internet_request_roundtripper_httprt_config_proto_rawDesc = [
 	0x75, 0x65, 0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65,
 	0x72, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x1a, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
 	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x78, 0x74, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
-	0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9e, 0x01, 0x0a, 0x0c, 0x43,
+	0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdf, 0x01, 0x0a, 0x0c, 0x43,
 	0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x59, 0x0a, 0x04, 0x68,
 	0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x76, 0x32, 0x72, 0x61,
 	0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
 	0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
 	0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x68,
 	0x74, 0x74, 0x70, 0x72, 0x74, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-	0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x33, 0x82, 0xb5, 0x18, 0x2f, 0x0a, 0x25, 0x74, 0x72,
-	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e,
-	0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x63, 0x6c, 0x69,
-	0x65, 0x6e, 0x74, 0x12, 0x06, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x22, 0xd5, 0x01, 0x0a, 0x0c,
-	0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x59, 0x0a, 0x04,
-	0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x76, 0x32, 0x72,
+	0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f,
+	0x68, 0x74, 0x74, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x6c, 0x6c, 0x6f,
+	0x77, 0x48, 0x74, 0x74, 0x70, 0x12, 0x20, 0x0a, 0x0c, 0x68, 0x32, 0x5f, 0x70, 0x6f, 0x6f, 0x6c,
+	0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x68, 0x32, 0x50,
+	0x6f, 0x6f, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x3a, 0x33, 0x82, 0xb5, 0x18, 0x2f, 0x0a, 0x25, 0x74,
+	0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x63, 0x6c,
+	0x69, 0x65, 0x6e, 0x74, 0x12, 0x06, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x22, 0xd5, 0x01, 0x0a,
+	0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x59, 0x0a,
+	0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x76, 0x32,
+	0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
+	0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72,
+	0x2e, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x35, 0x0a, 0x17, 0x6e, 0x6f, 0x5f, 0x64,
+	0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
+	0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6e, 0x6f, 0x44, 0x65, 0x63,
+	0x6f, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x3a,
+	0x33, 0x82, 0xb5, 0x18, 0x2f, 0x0a, 0x25, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
+	0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72,
+	0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x06, 0x68, 0x74,
+	0x74, 0x70, 0x72, 0x74, 0x22, 0x3e, 0x0a, 0x0a, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x72, 0x6c, 0x50, 0x72, 0x65,
+	0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x72, 0x6c, 0x50, 0x72,
+	0x65, 0x66, 0x69, 0x78, 0x42, 0xcc, 0x01, 0x0a, 0x3d, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72,
 	0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
 	0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x65,
 	0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e,
-	0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x35, 0x0a, 0x17, 0x6e, 0x6f, 0x5f, 0x64, 0x65,
-	0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
-	0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6e, 0x6f, 0x44, 0x65, 0x63, 0x6f,
-	0x64, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x3a, 0x33,
-	0x82, 0xb5, 0x18, 0x2f, 0x0a, 0x25, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
-	0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69,
-	0x70, 0x70, 0x65, 0x72, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x06, 0x68, 0x74, 0x74,
-	0x70, 0x72, 0x74, 0x22, 0x3e, 0x0a, 0x0a, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x72, 0x6c, 0x50, 0x72, 0x65, 0x66,
-	0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x72, 0x6c, 0x50, 0x72, 0x65,
-	0x66, 0x69, 0x78, 0x42, 0xcc, 0x01, 0x0a, 0x3d, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61,
-	0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
-	0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x2e, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x68,
-	0x74, 0x74, 0x70, 0x72, 0x74, 0x50, 0x01, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
-	0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d,
-	0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
-	0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2f,
-	0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0xaa, 0x02, 0x39, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43,
-	0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
-	0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52,
-	0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x68, 0x74, 0x74, 0x70,
-	0x72, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0x50, 0x01, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
+	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79,
+	0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
+	0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72,
+	0x2f, 0x68, 0x74, 0x74, 0x70, 0x72, 0x74, 0xaa, 0x02, 0x39, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e,
+	0x43, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49,
+	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e,
+	0x52, 0x6f, 0x75, 0x6e, 0x64, 0x74, 0x72, 0x69, 0x70, 0x70, 0x65, 0x72, 0x2e, 0x68, 0x74, 0x74,
+	0x70, 0x72, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/transport/internet/request/roundtripper/httprt/config.proto b/transport/internet/request/roundtripper/httprt/config.proto
index ac2df25d..1b750eff 100644
--- a/transport/internet/request/roundtripper/httprt/config.proto
+++ b/transport/internet/request/roundtripper/httprt/config.proto
@@ -13,6 +13,8 @@ message ClientConfig {
   option (v2ray.core.common.protoext.message_opt).short_name = "httprt";
 
   HTTPConfig http = 1;
+  bool allow_http = 2;
+  int32 h2_pool_size = 3;
 }
 
 message ServerConfig {
diff --git a/transport/internet/request/roundtripper/httprt/httprt.go b/transport/internet/request/roundtripper/httprt/httprt.go
index 16035a46..e98d80da 100644
--- a/transport/internet/request/roundtripper/httprt/httprt.go
+++ b/transport/internet/request/roundtripper/httprt/httprt.go
@@ -40,31 +40,55 @@ func (h *httpTripperClient) OnTransportClientAssemblyReady(assembly request.Tran
 }
 
 func (h *httpTripperClient) RoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) {
+	var streamingWriter io.Writer
+	for _, v := range opts {
+		if streamingResp, ok := v.(request.OptionSupportsStreamingResponse); ok {
+			streamingWriter = streamingResp.GetResponseWriter()
+		}
+	}
 	if h.httpRTT == nil {
-		h.httpRTT = transportcommon.NewALPNAwareHTTPRoundTripper(ctx, func(ctx context.Context, addr string) (gonet.Conn, error) {
+		var backDrop http.RoundTripper = unimplementedBackDrop{}
+		if h.config.AllowHttp {
+			backDrop = &http.Transport{
+				DialContext: func(_ context.Context, network, addr string) (gonet.Conn, error) {
+					return h.assembly.AutoImplDialer().Dial(ctx)
+				},
+				DialTLSContext: func(_ context.Context, network, addr string) (gonet.Conn, error) {
+					return nil, newError("unexpected dial of TLS")
+				},
+			}
+		}
+		h.httpRTT = transportcommon.NewALPNAwareHTTPRoundTripperWithH2Pool(ctx, func(ctx context.Context, addr string) (gonet.Conn, error) {
 			return h.assembly.AutoImplDialer().Dial(ctx)
-		}, unimplementedBackDrop{})
+		}, backDrop, int(h.config.H2PoolSize))
 	}
 
 	connectionTagStr := base64.RawURLEncoding.EncodeToString(req.ConnectionTag)
 
 	httpRequest, err := http.NewRequest("POST", h.config.Http.UrlPrefix+h.config.Http.Path, bytes.NewReader(req.Data))
 	if err != nil {
-		return
+		return resp, err
 	}
 
 	httpRequest.Header.Set("X-Session-ID", connectionTagStr)
 
 	httpResp, err := h.httpRTT.RoundTrip(httpRequest)
 	if err != nil {
-		return
+		return resp, err
 	}
 	defer httpResp.Body.Close()
-	result, err := io.ReadAll(httpResp.Body)
+	if streamingWriter == nil {
+		result, err := io.ReadAll(httpResp.Body)
+		if err != nil {
+			return request.Response{}, err
+		}
+		return request.Response{Data: result}, err
+	}
+	_, err = io.Copy(streamingWriter, httpResp.Body)
 	if err != nil {
-		return
+		return request.Response{}, newError("unable to copy response").Base(err)
 	}
-	return request.Response{Data: result}, err
+	return request.Response{}, nil
 }
 
 func newHTTPRoundTripperServer(ctx context.Context, config *ServerConfig) request.RoundTripperServer {
@@ -87,6 +111,25 @@ func (h *httpTripperServer) ServeHTTP(writer http.ResponseWriter, r *http.Reques
 	h.onRequest(writer, r)
 }
 
+type httpRespStreamWriting struct {
+	resp http.ResponseWriter
+	used bool
+}
+
+func (h *httpRespStreamWriting) RoundTripperOption() {
+}
+
+func (h *httpRespStreamWriting) GetResponseWriter() io.Writer {
+	h.used = true
+	return h.resp
+}
+
+func (h *httpRespStreamWriting) Flush() {
+	if f, ok := h.resp.(http.Flusher); ok {
+		f.Flush()
+	}
+}
+
 func (h *httpTripperServer) onRequest(resp http.ResponseWriter, req *http.Request) {
 	tail := req.Header.Get("X-Session-ID")
 	data := []byte(tail)
@@ -103,10 +146,16 @@ func (h *httpTripperServer) onRequest(resp http.ResponseWriter, req *http.Reques
 	if err != nil {
 		newError("unable to read body").Base(err).AtInfo().WriteToLog()
 	}
-	recvResp, err := h.assembly.TripperReceiver().OnRoundTrip(h.ctx, request.Request{Data: body, ConnectionTag: data})
+
+	streamingRespOption := &httpRespStreamWriting{resp: resp}
+	recvResp, err := h.assembly.TripperReceiver().OnRoundTrip(h.ctx, request.Request{Data: body, ConnectionTag: data},
+		streamingRespOption)
 	if err != nil {
 		newError("unable to process roundtrip").Base(err).AtInfo().WriteToLog()
 	}
+	if streamingRespOption.used {
+		return
+	}
 	_, err = io.Copy(resp, bytes.NewReader(recvResp.Data))
 	if err != nil {
 		newError("unable to send response").Base(err).AtInfo().WriteToLog()
diff --git a/transport/internet/socket_activation_other.go b/transport/internet/socket_activation_other.go
new file mode 100644
index 00000000..5bb93136
--- /dev/null
+++ b/transport/internet/socket_activation_other.go
@@ -0,0 +1,13 @@
+//go:build !unix
+// +build !unix
+
+package internet
+
+import (
+	"fmt"
+	"github.com/v2fly/v2ray-core/v5/common/net"
+)
+
+func activateSocket(address string, f func(network, address string, fd uintptr)) (net.Listener, error) {
+	return nil, fmt.Errorf("socket activation is not supported on this platform")
+}
diff --git a/transport/internet/socket_activation_unix.go b/transport/internet/socket_activation_unix.go
new file mode 100644
index 00000000..6dd92ab2
--- /dev/null
+++ b/transport/internet/socket_activation_unix.go
@@ -0,0 +1,63 @@
+//go:build unix
+// +build unix
+
+package internet
+
+import (
+	"fmt"
+	"os"
+	"path"
+	"strconv"
+	"syscall"
+
+	"github.com/v2fly/v2ray-core/v5/common/net"
+)
+
+func activateSocket(address string, f func(network, address string, fd uintptr)) (net.Listener, error) {
+	fd, err := strconv.Atoi(path.Base(address))
+	if err != nil {
+		return nil, err
+	}
+
+	err = syscall.SetNonblock(fd, true)
+	if err != nil {
+		return nil, err
+	}
+
+	acceptConn, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ACCEPTCONN)
+	if err != nil {
+		return nil, err
+	}
+	if acceptConn == 0 {
+		return nil, fmt.Errorf("socket '%s' has not been marked to accept connections", address)
+	}
+
+	sockType, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
+	if err != nil {
+		return nil, err
+	}
+	if sockType != syscall.SOCK_STREAM {
+		// XXX: currently only stream socks are supported
+		return nil, fmt.Errorf("socket '%s' is not a stream socket", address)
+	}
+
+	ufd := uintptr(fd)
+
+	sa, err := syscall.Getsockname(fd)
+	if err != nil {
+		return nil, err
+	}
+	switch sa := sa.(type) {
+	case *syscall.SockaddrInet4:
+		addr := net.TCPAddr{IP: sa.Addr[:], Port: sa.Port, Zone: ""}
+		f("tcp4", addr.String(), ufd)
+	case *syscall.SockaddrInet6:
+		addr := net.TCPAddr{IP: sa.Addr[:], Port: sa.Port, Zone: strconv.Itoa(int(sa.ZoneId))}
+		f("tcp6", addr.String(), ufd)
+	}
+
+	file := os.NewFile(ufd, address)
+	defer file.Close()
+
+	return net.FileListener(file)
+}
diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go
index 314664a7..09ad1539 100644
--- a/transport/internet/system_dialer.go
+++ b/transport/internet/system_dialer.go
@@ -73,6 +73,15 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
 		KeepAlive: goStdKeepAlive,
 	}
 
+	if dest.Network == net.Network_TCP && sockopt != nil {
+		switch sockopt.Mptcp {
+		case MPTCPState_Enable:
+			dialer.SetMultipathTCP(true)
+		case MPTCPState_Disable:
+			dialer.SetMultipathTCP(false)
+		}
+	}
+
 	if sockopt != nil || len(d.controllers) > 0 {
 		dialer.Control = func(network, address string, c syscall.RawConn) error {
 			return c.Control(func(fd uintptr) {
diff --git a/transport/internet/system_listener.go b/transport/internet/system_listener.go
index ef54228d..e9ca184c 100644
--- a/transport/internet/system_listener.go
+++ b/transport/internet/system_listener.go
@@ -36,29 +36,35 @@ func (l *combinedListener) Close() error {
 	return l.Listener.Close()
 }
 
-func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []controller) func(network, address string, c syscall.RawConn) error {
-	return func(network, address string, c syscall.RawConn) error {
-		return c.Control(func(fd uintptr) {
-			if sockopt != nil {
-				if err := applyInboundSocketOptions(network, fd, sockopt); err != nil {
-					newError("failed to apply socket options to incoming connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
-				}
+func getRawControlFunc(network, address string, ctx context.Context, sockopt *SocketConfig, controllers []controller) func(fd uintptr) {
+	return func(fd uintptr) {
+		if sockopt != nil {
+			if err := applyInboundSocketOptions(network, fd, sockopt); err != nil {
+				newError("failed to apply socket options to incoming connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
 			}
+		}
 
-			setReusePort(fd) // nolint: staticcheck
+		setReusePort(fd) // nolint: staticcheck
 
-			for _, controller := range controllers {
-				if err := controller(network, address, fd); err != nil {
-					newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx))
-				}
+		for _, controller := range controllers {
+			if err := controller(network, address, fd); err != nil {
+				newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx))
 			}
-		})
+		}
+	}
+}
+
+func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []controller) func(network, address string, c syscall.RawConn) error {
+	return func(network, address string, c syscall.RawConn) error {
+		return c.Control(getRawControlFunc(network, address, ctx, sockopt, controllers))
 	}
 }
 
 func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) {
 	var lc net.ListenConfig
 	var network, address string
+	var l net.Listener
+	var err error
 	// callback is called after the Listen function returns
 	// this is used to wrap the listener and do some post processing
 	callback := func(l net.Listener, err error) (net.Listener, error) {
@@ -69,8 +75,17 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
 		network = addr.Network()
 		address = addr.String()
 		lc.Control = getControlFunc(ctx, sockopt, dl.controllers)
-		if sockopt != nil && (sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0) {
-			lc.KeepAlive = time.Duration(-1)
+		if sockopt != nil {
+			switch sockopt.Mptcp {
+			case MPTCPState_Enable:
+				lc.SetMultipathTCP(true)
+			case MPTCPState_Disable:
+				lc.SetMultipathTCP(false)
+			}
+
+			if sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0 {
+				lc.KeepAlive = time.Duration(-1)
+			}
 		}
 	case *net.UnixAddr:
 		lc.Control = nil
@@ -84,6 +99,14 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
 				copy(fullAddr, address[1:])
 				address = string(fullAddr)
 			}
+		} else if strings.HasPrefix(address, "/dev/fd/") {
+			// socket activation
+			l, err = activateSocket(address, func(network, address string, fd uintptr) {
+				getRawControlFunc(network, address, ctx, sockopt, dl.controllers)(fd)
+			})
+			if err != nil {
+				return nil, err
+			}
 		} else {
 			// normal unix domain socket
 			var fileMode *os.FileMode
@@ -124,13 +147,18 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
 		}
 	}
 
-	l, err := lc.Listen(ctx, network, address)
-	l, err = callback(l, err)
-	if err == nil && sockopt != nil && sockopt.AcceptProxyProtocol {
+	if l == nil {
+		l, err = lc.Listen(ctx, network, address)
+		l, err = callback(l, err)
+		if err != nil {
+			return nil, err
+		}
+	}
+	if sockopt != nil && sockopt.AcceptProxyProtocol {
 		policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil }
 		l = &proxyproto.Listener{Listener: l, Policy: policyFunc}
 	}
-	return l, err
+	return l, nil
 }
 
 func (dl *DefaultListener) ListenPacket(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.PacketConn, error) {
diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go
index 5d359424..8de83b93 100644
--- a/transport/internet/tls/config.go
+++ b/transport/internet/tls/config.go
@@ -5,6 +5,7 @@ import (
 	"crypto/tls"
 	"crypto/x509"
 	"encoding/base64"
+	"os"
 	"strings"
 	"sync"
 	"time"
@@ -194,6 +195,16 @@ func (c *Config) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Cert
 	return nil
 }
 
+type alwaysFlushWriter struct {
+	file *os.File
+}
+
+func (a *alwaysFlushWriter) Write(p []byte) (n int, err error) {
+	n, err = a.file.Write(p)
+	a.file.Sync()
+	return n, err
+}
+
 // GetTLSConfig converts this Config into tls.Config.
 func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
 	root, err := c.getCertPool()
@@ -226,6 +237,10 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
 		ClientCAs:              clientRoot,
 	}
 
+	if c.AllowInsecureIfPinnedPeerCertificate && c.PinnedPeerCertificateChainSha256 != nil {
+		config.InsecureSkipVerify = true
+	}
+
 	for _, opt := range opts {
 		opt(config)
 	}
diff --git a/transport/internet/tls/config.pb.go b/transport/internet/tls/config.pb.go
index 9bb8894d..6559d82e 100644
--- a/transport/internet/tls/config.pb.go
+++ b/transport/internet/tls/config.pb.go
@@ -232,6 +232,8 @@ type Config struct {
 	MinVersion Config_TLSVersion `protobuf:"varint,9,opt,name=min_version,json=minVersion,proto3,enum=v2ray.core.transport.internet.tls.Config_TLSVersion" json:"min_version,omitempty"`
 	// Maximum TLS version to support.
 	MaxVersion Config_TLSVersion `protobuf:"varint,10,opt,name=max_version,json=maxVersion,proto3,enum=v2ray.core.transport.internet.tls.Config_TLSVersion" json:"max_version,omitempty"`
+	// Whether or not to allow self-signed certificates when pinned_peer_certificate_chain_sha256 is present.
+	AllowInsecureIfPinnedPeerCertificate bool `protobuf:"varint,11,opt,name=allow_insecure_if_pinned_peer_certificate,json=allowInsecureIfPinnedPeerCertificate,proto3" json:"allow_insecure_if_pinned_peer_certificate,omitempty"`
 }
 
 func (x *Config) Reset() {
@@ -336,6 +338,13 @@ func (x *Config) GetMaxVersion() Config_TLSVersion {
 	return Config_Default
 }
 
+func (x *Config) GetAllowInsecureIfPinnedPeerCertificate() bool {
+	if x != nil {
+		return x.AllowInsecureIfPinnedPeerCertificate
+	}
+	return false
+}
+
 var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
 
 var file_transport_internet_tls_config_proto_rawDesc = []byte{
@@ -367,7 +376,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
 	0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
 	0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x41, 0x55, 0x54, 0x48,
 	0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, 0x5f, 0x43, 0x4c, 0x49,
-	0x45, 0x4e, 0x54, 0x10, 0x03, 0x22, 0xd9, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x45, 0x4e, 0x54, 0x10, 0x03, 0x22, 0xb2, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
 	0x12, 0x2d, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75,
 	0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x06, 0x82, 0xb5, 0x18, 0x02, 0x28, 0x01,
 	0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12,
@@ -406,22 +415,28 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
 	0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
 	0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e,
 	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x4c, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
-	0x6e, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x49, 0x0a,
-	0x0a, 0x54, 0x4c, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x44,
-	0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x4c, 0x53, 0x31,
-	0x5f, 0x30, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x4c, 0x53, 0x31, 0x5f, 0x31, 0x10, 0x02,
-	0x12, 0x0a, 0x0a, 0x06, 0x54, 0x4c, 0x53, 0x31, 0x5f, 0x32, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06,
-	0x54, 0x4c, 0x53, 0x31, 0x5f, 0x33, 0x10, 0x04, 0x3a, 0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a, 0x08,
-	0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x03, 0x74, 0x6c, 0x73, 0x90, 0xff, 0x29,
-	0x01, 0x42, 0x84, 0x01, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
-	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
-	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x35, 0x67,
-	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f,
-	0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72,
-	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
-	0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x21, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72,
-	0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
-	0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x6e, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a,
+	0x29, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f,
+	0x69, 0x66, 0x5f, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63,
+	0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x24, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x49,
+	0x66, 0x50, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69,
+	0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x22, 0x49, 0x0a, 0x0a, 0x54, 0x4c, 0x53, 0x56, 0x65, 0x72,
+	0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x10,
+	0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x4c, 0x53, 0x31, 0x5f, 0x30, 0x10, 0x01, 0x12, 0x0a, 0x0a,
+	0x06, 0x54, 0x4c, 0x53, 0x31, 0x5f, 0x31, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x4c, 0x53,
+	0x31, 0x5f, 0x32, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x4c, 0x53, 0x31, 0x5f, 0x33, 0x10,
+	0x04, 0x3a, 0x17, 0x82, 0xb5, 0x18, 0x13, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+	0x79, 0x12, 0x03, 0x74, 0x6c, 0x73, 0x90, 0xff, 0x29, 0x01, 0x42, 0x84, 0x01, 0x0a, 0x25, 0x63,
+	0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+	0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63,
+	0x6f, 0x72, 0x65, 0x2f, 0x76, 0x35, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
+	0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x21,
+	0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73,
+	0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c,
+	0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -438,7 +453,7 @@ func file_transport_internet_tls_config_proto_rawDescGZIP() []byte {
 
 var file_transport_internet_tls_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
 var file_transport_internet_tls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_transport_internet_tls_config_proto_goTypes = []interface{}{
+var file_transport_internet_tls_config_proto_goTypes = []any{
 	(Certificate_Usage)(0), // 0: v2ray.core.transport.internet.tls.Certificate.Usage
 	(Config_TLSVersion)(0), // 1: v2ray.core.transport.internet.tls.Config.TLSVersion
 	(*Certificate)(nil),    // 2: v2ray.core.transport.internet.tls.Certificate
@@ -462,7 +477,7 @@ func file_transport_internet_tls_config_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_transport_internet_tls_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_transport_internet_tls_config_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Certificate); i {
 			case 0:
 				return &v.state
@@ -474,7 +489,7 @@ func file_transport_internet_tls_config_proto_init() {
 				return nil
 			}
 		}
-		file_transport_internet_tls_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_transport_internet_tls_config_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*Config); i {
 			case 0:
 				return &v.state
diff --git a/transport/internet/tls/config.proto b/transport/internet/tls/config.proto
index 1573f07a..8c9f92e5 100644
--- a/transport/internet/tls/config.proto
+++ b/transport/internet/tls/config.proto
@@ -76,4 +76,6 @@ message Config {
   // Maximum TLS version to support.
   TLSVersion max_version = 10;
 
+  // Whether or not to allow self-signed certificates when pinned_peer_certificate_chain_sha256 is present.
+  bool allow_insecure_if_pinned_peer_certificate = 11;
 }
diff --git a/transport/internet/transportcommon/httpDialer.go b/transport/internet/transportcommon/httpDialer.go
index aafebc93..4db68f74 100644
--- a/transport/internet/transportcommon/httpDialer.go
+++ b/transport/internet/transportcommon/httpDialer.go
@@ -17,10 +17,17 @@ import (
 
 type DialerFunc func(ctx context.Context, addr string) (net.Conn, error)
 
-// NewALPNAwareHTTPRoundTripper creates an instance of RoundTripper that dial to remote HTTPS endpoint with
-// an alternative version of TLS implementation.
 func NewALPNAwareHTTPRoundTripper(ctx context.Context, dialer DialerFunc,
 	backdropTransport http.RoundTripper,
+) http.RoundTripper {
+	return NewALPNAwareHTTPRoundTripperWithH2Pool(ctx, dialer, backdropTransport, 1)
+}
+
+// NewALPNAwareHTTPRoundTripperWithH2Pool creates an instance of RoundTripper that dial to remote HTTPS endpoint with
+// an alternative version of TLS implementation.
+func NewALPNAwareHTTPRoundTripperWithH2Pool(ctx context.Context, dialer DialerFunc,
+	backdropTransport http.RoundTripper,
+	h2PoolSize int,
 ) http.RoundTripper {
 	rtImpl := &alpnAwareHTTPRoundTripperImpl{
 		connectWithH1:     map[string]bool{},
@@ -46,6 +53,8 @@ type alpnAwareHTTPRoundTripperImpl struct {
 
 	ctx    context.Context
 	dialer DialerFunc
+
+	h2PoolSize int
 }
 
 type pendingConnKey struct {
@@ -175,10 +184,20 @@ func (r *alpnAwareHTTPRoundTripperImpl) dialTLS(ctx context.Context, addr string
 }
 
 func (r *alpnAwareHTTPRoundTripperImpl) init() {
-	r.httpsH2Transport = &http2.Transport{
-		DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
-			return r.dialOrGetTLSWithExpectedALPN(context.Background(), addr, true)
-		},
+	if r.h2PoolSize >= 2 {
+		r.httpsH2Transport = newH2TransportPool(int64(r.h2PoolSize), func() *http2.Transport {
+			return &http2.Transport{
+				DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
+					return r.dialOrGetTLSWithExpectedALPN(context.Background(), addr, true)
+				},
+			}
+		})
+	} else {
+		r.httpsH2Transport = &http2.Transport{
+			DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
+				return r.dialOrGetTLSWithExpectedALPN(context.Background(), addr, true)
+			},
+		}
 	}
 	r.httpsH1Transport = &http.Transport{
 		DialTLSContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
@@ -220,3 +239,29 @@ func (c *unclaimedConnection) tick() {
 		c.Conn = nil
 	}
 }
+
+type h2TransportFactory func() *http2.Transport
+
+func newH2TransportPool(size int64, h2factory h2TransportFactory) *h2TransportPool {
+	return &h2TransportPool{
+		pool:      make([]*http2.Transport, size),
+		size:      size,
+		h2factory: h2factory,
+	}
+}
+
+type h2TransportPool struct {
+	pool       []*http2.Transport
+	h2factory  h2TransportFactory
+	usageCount int64
+	size       int64
+}
+
+func (h *h2TransportPool) RoundTrip(request *http.Request) (*http.Response, error) {
+	currentSlot := h.usageCount % h.size
+	h.usageCount++
+	if h.pool[currentSlot] == nil {
+		h.pool[currentSlot] = h.h2factory()
+	}
+	return h.pool[currentSlot].RoundTrip(request)
+}
diff --git a/transport/internet/transportcommon/listener.go b/transport/internet/transportcommon/listener.go
index 265ba3f0..66b1e366 100644
--- a/transport/internet/transportcommon/listener.go
+++ b/transport/internet/transportcommon/listener.go
@@ -2,12 +2,14 @@ package transportcommon
 
 import (
 	"context"
+	"crypto/tls"
 
 	"github.com/v2fly/v2ray-core/v5/common/environment"
 	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
 	"github.com/v2fly/v2ray-core/v5/common/net"
 	"github.com/v2fly/v2ray-core/v5/common/session"
 	"github.com/v2fly/v2ray-core/v5/transport/internet"
+	v2tls "github.com/v2fly/v2ray-core/v5/transport/internet/tls"
 )
 
 func ListenWithSecuritySettings(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig) (
@@ -43,5 +45,11 @@ func ListenWithSecuritySettings(ctx context.Context, address net.Address, port n
 	if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol {
 		newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
 	}
+
+	if config := v2tls.ConfigFromStreamSettings(streamSettings); config != nil {
+		if tlsConfig := config.GetTLSConfig(); tlsConfig != nil {
+			l = tls.NewListener(l, tlsConfig)
+		}
+	}
 	return l, nil
 }
diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go
index a544c26d..6af9caa9 100644
--- a/transport/internet/udp/hub.go
+++ b/transport/internet/udp/hub.go
@@ -3,6 +3,9 @@ package udp
 import (
 	"context"
 
+	"github.com/v2fly/v2ray-core/v5/common/environment"
+	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
+
 	"github.com/v2fly/v2ray-core/v5/common/buf"
 	"github.com/v2fly/v2ray-core/v5/common/net"
 	"github.com/v2fly/v2ray-core/v5/common/protocol/udp"
@@ -25,6 +28,7 @@ func HubReceiveOriginalDestination(r bool) HubOption {
 
 type Hub struct {
 	conn         *net.UDPConn
+	connPacket   net.PacketConn
 	cache        chan *udp.Packet
 	capacity     int
 	recvOrigDest bool
@@ -46,8 +50,9 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
 	if sockopt != nil && sockopt.ReceiveOriginalDestAddress {
 		hub.recvOrigDest = true
 	}
-
-	udpConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{
+	transportEnvironment := envctx.EnvironmentFromContext(ctx).(environment.TransportEnvironment)
+	listener := transportEnvironment.Listener()
+	udpConn, err := listener.ListenPacket(ctx, &net.UDPAddr{
 		IP:   address.IP(),
 		Port: int(port),
 	}, sockopt)
@@ -55,7 +60,12 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
 		return nil, err
 	}
 	newError("listening UDP on ", address, ":", port).WriteToLog()
-	hub.conn = udpConn.(*net.UDPConn)
+	if udpConnDirect, ok := udpConn.(*net.UDPConn); ok {
+		hub.conn = udpConnDirect
+	} else {
+		hub.connPacket = udpConn
+	}
+
 	hub.cache = make(chan *udp.Packet, hub.capacity)
 
 	go hub.start()
@@ -64,11 +74,21 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
 
 // Close implements net.Listener.
 func (h *Hub) Close() error {
+	if h.connPacket != nil {
+		h.connPacket.Close()
+		return nil
+	}
 	h.conn.Close()
 	return nil
 }
 
 func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) {
+	if h.connPacket != nil {
+		return h.connPacket.WriteTo(payload, &net.UDPAddr{
+			IP:   dest.Address.IP(),
+			Port: int(dest.Port),
+		})
+	}
 	return h.conn.WriteToUDP(payload, &net.UDPAddr{
 		IP:   dest.Address.IP(),
 		Port: int(dest.Port),
@@ -83,47 +103,76 @@ func (h *Hub) start() {
 
 	for {
 		buffer := buf.New()
-		var noob int
-		var addr *net.UDPAddr
-		rawBytes := buffer.Extend(buf.Size)
-
-		n, noob, _, addr, err := ReadUDPMsg(h.conn, rawBytes, oobBytes)
-		if err != nil {
-			newError("failed to read UDP msg").Base(err).WriteToLog()
-			buffer.Release()
-			break
-		}
-		buffer.Resize(0, int32(n))
+		if h.conn != nil {
+			var noob int
+			var addr *net.UDPAddr
+			rawBytes := buffer.Extend(buf.Size)
+			n, noob, _, addr, err := ReadUDPMsg(h.conn, rawBytes, oobBytes)
+			if err != nil {
+				newError("failed to read UDP msg").Base(err).WriteToLog()
+				buffer.Release()
+				break
+			}
+			buffer.Resize(0, int32(n))
 
-		if buffer.IsEmpty() {
-			buffer.Release()
-			continue
-		}
+			if buffer.IsEmpty() {
+				buffer.Release()
+				continue
+			}
 
-		payload := &udp.Packet{
-			Payload: buffer,
-			Source:  net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)),
-		}
-		if h.recvOrigDest && noob > 0 {
-			payload.Target = RetrieveOriginalDest(oobBytes[:noob])
-			if payload.Target.IsValid() {
-				newError("UDP original destination: ", payload.Target).AtDebug().WriteToLog()
-			} else {
-				newError("failed to read UDP original destination").WriteToLog()
+			payload := &udp.Packet{
+				Payload: buffer,
+				Source:  net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)),
 			}
-		}
+			if h.recvOrigDest && noob > 0 {
+				payload.Target = RetrieveOriginalDest(oobBytes[:noob])
+				if payload.Target.IsValid() {
+					newError("UDP original destination: ", payload.Target).AtDebug().WriteToLog()
+				} else {
+					newError("failed to read UDP original destination").WriteToLog()
+				}
+			}
+
+			select {
+			case c <- payload:
+			default:
+				buffer.Release()
+				payload.Payload = nil
+			}
+		} else {
+			rawBytes := buffer.Extend(buf.Size)
+			n, addr, err := h.connPacket.ReadFrom(rawBytes)
+			if err != nil {
+				newError("failed to read UDP msg").Base(err).WriteToLog()
+				buffer.Release()
+				break
+			}
+			buffer.Resize(0, int32(n))
 
-		select {
-		case c <- payload:
-		default:
-			buffer.Release()
-			payload.Payload = nil
+			if buffer.IsEmpty() {
+				buffer.Release()
+				continue
+			}
+
+			payload := &udp.Packet{
+				Payload: buffer,
+				Source:  net.DestinationFromAddr(addr),
+			}
+			select {
+			case c <- payload:
+			default:
+				buffer.Release()
+				payload.Payload = nil
+			}
 		}
 	}
 }
 
 // Addr implements net.Listener.
 func (h *Hub) Addr() net.Addr {
+	if h.conn == nil {
+		return h.connPacket.LocalAddr()
+	}
 	return h.conn.LocalAddr()
 }
 


Test patch:
diff --git a/infra/conf/merge/merge_test.go b/infra/conf/merge/merge_test.go
index 6f393a9e..331d03b7 100644
--- a/infra/conf/merge/merge_test.go
+++ b/infra/conf/merge/merge_test.go
@@ -78,7 +78,7 @@ func TestMergeTag(t *testing.T) {
 				"outboundTag": "out-2"
 			}]
 		}
-	}	
+	}
 `
 	expected := `
 	{
@@ -194,6 +194,108 @@ func TestMergeTagDeep(t *testing.T) {
 	assertResult(t, m, expected)
 }
 
+func TestNoMergeDnsServers(t *testing.T) {
+	json1 := `
+	{
+		"dns": {
+			"queryStrategy": "UseIPv4",
+			"fallbackStrategy": "disabled-if-any-match",
+			"domainMatcher": "mph",
+			"servers": [
+				{
+					"address": "aaa.bbb.ccc.ddd",
+					"port": 53,
+					"domains": [
+						"geosite:cn"
+					],
+					"tag": "dns-domestic"
+				},
+				{
+					"address": "114.114.114.114",
+					"port": 53,
+					"domains": [
+						"geosite:cn"
+					],
+					"tag": "dns-domestic"
+				},
+				{
+					"address": "https://1.1.1.1/dns-query",
+					"tag": "dns-international"
+				}
+			]
+		},
+		"routing": {
+			"domainStrategy": "IPIfNonMatch",
+			"domainMatcher": "mph",
+			"rules": [
+				{
+					"type": "field",
+					"inboundTag": "dns-domestic",
+					"outboundTag": "direct"
+				},
+				{
+					"type": "field",
+					"inboundTag": "dns-international",
+					"outboundTag": "proxy"
+				}
+			]
+		}
+	}
+`
+	expected := `
+	{
+		"dns": {
+			"queryStrategy": "UseIPv4",
+			"fallbackStrategy": "disabled-if-any-match",
+			"domainMatcher": "mph",
+			"servers": [
+				{
+					"address": "aaa.bbb.ccc.ddd",
+					"port": 53,
+					"domains": [
+						"geosite:cn"
+					],
+					"tag": "dns-domestic"
+				},
+				{
+					"address": "114.114.114.114",
+					"port": 53,
+					"domains": [
+						"geosite:cn"
+					],
+					"tag": "dns-domestic"
+				},
+				{
+					"address": "https://1.1.1.1/dns-query",
+					"tag": "dns-international"
+				}
+			]
+		},
+		"routing": {
+			"domainStrategy": "IPIfNonMatch",
+			"domainMatcher": "mph",
+			"rules": [
+				{
+					"type": "field",
+					"inboundTag": "dns-domestic",
+					"outboundTag": "direct"
+				},
+				{
+					"type": "field",
+					"inboundTag": "dns-international",
+					"outboundTag": "proxy"
+				}
+			]
+		}
+	}
+`
+	m, err := merge.JSONs([][]byte{[]byte(json1)})
+	if err != nil {
+		t.Error(err)
+	}
+	assertResult(t, m, expected)
+}
+
 func assertResult(t *testing.T, value []byte, expected string) {
 	v := make(map[string]interface{})
 	err := serial.DecodeJSON(bytes.NewReader(value), &v)
diff --git a/infra/conf/v4/socks_test.go b/infra/conf/v4/socks_test.go
index 1d4a881a..3f6fc429 100644
--- a/infra/conf/v4/socks_test.go
+++ b/infra/conf/v4/socks_test.go
@@ -4,6 +4,7 @@ import (
 	"testing"
 
 	"github.com/v2fly/v2ray-core/v5/common/net"
+	"github.com/v2fly/v2ray-core/v5/common/net/packetaddr"
 	"github.com/v2fly/v2ray-core/v5/common/protocol"
 	"github.com/v2fly/v2ray-core/v5/common/serial"
 	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
@@ -30,7 +31,8 @@ func TestSocksInboundConfig(t *testing.T) {
 				"udp": false,
 				"ip": "127.0.0.1",
 				"timeout": 5,
-				"userLevel": 1
+				"userLevel": 1,
+				"packetEncoding": "Packet"
 			}`,
 			Parser: testassist.LoadJSON(creator),
 			Output: &socks.ServerConfig{
@@ -44,8 +46,9 @@ func TestSocksInboundConfig(t *testing.T) {
 						Ip: []byte{127, 0, 0, 1},
 					},
 				},
-				Timeout:   5,
-				UserLevel: 1,
+				Timeout:        5,
+				UserLevel:      1,
+				PacketEncoding: packetaddr.PacketAddrType_Packet,
 			},
 		},
 	})
diff --git a/infra/conf/v4/transport_test.go b/infra/conf/v4/transport_test.go
index f342a125..1eddc3fc 100644
--- a/infra/conf/v4/transport_test.go
+++ b/infra/conf/v4/transport_test.go
@@ -38,13 +38,15 @@ func TestSocketConfig(t *testing.T) {
 			Input: `{
 				"mark": 1,
 				"tcpFastOpen": true,
-				"tcpFastOpenQueueLength": 1024
+				"tcpFastOpenQueueLength": 1024,
+				"mptcp": true
 			}`,
 			Parser: createParser(),
 			Output: &internet.SocketConfig{
 				Mark:           1,
 				Tfo:            internet.SocketConfig_Enable,
 				TfoQueueLength: 1024,
+				Mptcp:          internet.MPTCPState_Enable,
 			},
 		},
 	})
diff --git a/transport/internet/kcp/kcp_test.go b/transport/internet/kcp/kcp_test.go
index d10534a2..5e37b35e 100644
--- a/transport/internet/kcp/kcp_test.go
+++ b/transport/internet/kcp/kcp_test.go
@@ -7,6 +7,11 @@ import (
 	"testing"
 	"time"
 
+	"github.com/v2fly/v2ray-core/v5/common/environment"
+	"github.com/v2fly/v2ray-core/v5/common/environment/envctx"
+	"github.com/v2fly/v2ray-core/v5/common/environment/systemnetworkimpl"
+	"github.com/v2fly/v2ray-core/v5/common/environment/transientstorageimpl"
+
 	"github.com/google/go-cmp/cmp"
 	"golang.org/x/sync/errgroup"
 
@@ -18,7 +23,17 @@ import (
 )
 
 func TestDialAndListen(t *testing.T) {
-	listener, err := NewListener(context.Background(), net.LocalHostIP, net.Port(0), &internet.MemoryStreamConfig{
+	ctx := context.Background()
+	defaultNetworkImpl := systemnetworkimpl.NewSystemNetworkDefault()
+	rootEnv := environment.NewRootEnvImpl(ctx, transientstorageimpl.NewScopedTransientStorageImpl(), defaultNetworkImpl.Dialer(), defaultNetworkImpl.Listener())
+	proxyEnvironment := rootEnv.ProxyEnvironment("o")
+	transportEnvironment, err := proxyEnvironment.NarrowScopeToTransport("kcp")
+	if err != nil {
+		t.Fatal(err)
+	}
+	ctx = envctx.ContextWithEnvironment(ctx, transportEnvironment)
+
+	listener, err := NewListener(ctx, net.LocalHostIP, net.Port(0), &internet.MemoryStreamConfig{
 		ProtocolName:     "mkcp",
 		ProtocolSettings: &Config{},
 	}, func(conn internet.Connection) {
@@ -45,7 +60,7 @@ func TestDialAndListen(t *testing.T) {
 	var errg errgroup.Group
 	for i := 0; i < 10; i++ {
 		errg.Go(func() error {
-			clientConn, err := DialKCP(context.Background(), net.UDPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
+			clientConn, err := DialKCP(ctx, net.UDPDestination(net.LocalHostIP, port), &internet.MemoryStreamConfig{
 				ProtocolName:     "mkcp",
 				ProtocolSettings: &Config{},
 			})
diff --git a/transport/internet/sockopt_test.go b/transport/internet/sockopt_test.go
index 56e978e0..d712e278 100644
--- a/transport/internet/sockopt_test.go
+++ b/transport/internet/sockopt_test.go
@@ -39,3 +39,33 @@ func TestTCPFastOpen(t *testing.T) {
 		t.Fatal(r)
 	}
 }
+
+// Currently, Multipath TCP is only supported on Linux.
+// We test the Multipath TCP Settings on other platforms for ensure code will not have any negative impact on other platforms.
+func TestMultipathTCP(t *testing.T) {
+	tcpServer := tcp.Server{
+		MsgProcessor: func(b []byte) []byte {
+			return b
+		},
+	}
+	dest, err := tcpServer.StartContext(context.Background(), &SocketConfig{Mptcp: MPTCPState_Enable})
+	common.Must(err)
+	defer tcpServer.Close()
+
+	ctx := context.Background()
+	dialer := DefaultSystemDialer{}
+	conn, err := dialer.Dial(ctx, nil, dest, &SocketConfig{
+		Mptcp: MPTCPState_Enable,
+	})
+	common.Must(err)
+	defer conn.Close()
+
+	_, err = conn.Write([]byte("abcd"))
+	common.Must(err)
+
+	b := buf.New()
+	common.Must2(b.ReadFrom(conn))
+	if r := cmp.Diff(b.Bytes(), []byte("abcd")); r != "" {
+		t.Fatal(r)
+	}
+}