Repository: v2fly/v2ray-core
Pull request #3109 (2024-08-08 14:31:19)

feat: add MPTCP support
### Background

Multipath TCP or MPTCP is an extension to the standard TCP and is described in RFC 8684. It allows a device to make use of multiple interfaces at once to send and receive TCP packets over a single MPTCP connection. MPTCP can aggregate the bandwidth of multiple interfaces or prefer the one with the lowest latency.

Support for Multipath TCP was first added in Go 1.21. In a future Go release we may enable Multipath TCP by default on systems that support it.
[Go 1.21 Release Note](https://tip.golang.org/doc/go1.21#netpkgnet)

Other proxy softwares that written in Go had add it.

### Behavior

If `mptcp` is not set in `sockopt`, V2Ray will not do any action about MPTCP, and MPTCP is disabled by default on latest Go currently. If `mptcp` is set to `true`, V2Ray will try to enable MPTCP, but it only works on Linux now. On other platforms, it will make any impact.

If one of the server and the client does not support MPTCP, MPTCP will not work and the connection will be the same as a normal TCP connection.

### Go versions
This PR can only be compiled with Go 1.21 or newer versions. The minimal Go version in `v2ray-core` and `quic-go` in `main` branch is Go 1.21, so I think this PR not make any impact to this problem.

https://github.com/v2fly/v2ray-core/blob/8191faa6e0951fd1c84864ce7780863142104b22/go.mod#L3

### Test Results
On Linux, `sudo ip mptcp monitor` can monitor the status of MPTCP connections.
<img width="894" alt="image" src="https://github.com/user-attachments/assets/81639ef0-4ad8-44dd-8682-c7f77300dd95">

On other platforms, this PR will not make any impact currently. (I just tested it on macOS)


AkinoKaede: I think the errors reported by Lint are not caused by this PR
codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly) Report
Attention: Patch coverage is `29.16667%` with `34 lines` in your changes missing coverage. Please review.
> Project coverage is 35.60%. Comparing base [(`346ca66`)](https://app.codecov.io/gh/v2fly/v2ray-core/commit/346ca66c57ab1b6872908354d5f3e4152fff59da?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly) to head [(`7ac7bba`)](https://app.codecov.io/gh/v2fly/v2ray-core/commit/7ac7bba79c63518ef0163b7813e5bcc6aef1be66?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly).
> Report is 4 commits behind head on master.

| [Files](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly) | Patch % | Lines |
|---|---|---|
| [transport/internet/config.pb.go](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?src=pr&el=tree&filepath=transport%2Finternet%2Fconfig.pb.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly#diff-dHJhbnNwb3J0L2ludGVybmV0L2NvbmZpZy5wYi5nbw==) | 3.70% | [26 Missing :warning: ](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly) |
| [transport/internet/system\_listener.go](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?src=pr&el=tree&filepath=transport%2Finternet%2Fsystem_listener.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly#diff-dHJhbnNwb3J0L2ludGVybmV0L3N5c3RlbV9saXN0ZW5lci5nbw==) | 50.00% | [3 Missing and 1 partial :warning: ](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly) |
| [infra/conf/cfgcommon/socketcfg/socket.go](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?src=pr&el=tree&filepath=infra%2Fconf%2Fcfgcommon%2Fsocketcfg%2Fsocket.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly#diff-aW5mcmEvY29uZi9jZmdjb21tb24vc29ja2V0Y2ZnL3NvY2tldC5nbw==) | 71.42% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly) |
| [transport/internet/system\_dialer.go](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?src=pr&el=tree&filepath=transport%2Finternet%2Fsystem_dialer.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly#diff-dHJhbnNwb3J0L2ludGVybmV0L3N5c3RlbV9kaWFsZXIuZ28=) | 66.66% | [2 Missing :warning: ](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly) |

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #3109      +/-   ##
==========================================
+ Coverage   35.59%   35.60%   +0.01%     
==========================================
  Files         729      729              
  Lines       41613    41660      +47     
==========================================
+ Hits        14812    14834      +22     
- Misses      25167    25193      +26     
+ Partials     1634     1633       -1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/v2fly/v2ray-core/pull/3109?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=v2fly).


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/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/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/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/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..84f9f1e8 100644
--- a/transport/internet/system_listener.go
+++ b/transport/internet/system_listener.go
@@ -69,8 +69,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
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
 }


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/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)
+	}
+}