Skip to content

Commit 82582b0

Browse files
committed
feat: support routing-mark for listeners except tun/redir/tproxy and top-level external-controller-routing-mark
1 parent 084a09e commit 82582b0

48 files changed

Lines changed: 385 additions & 271 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

adapter/inbound/listen.go

Lines changed: 54 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,80 @@ import (
55
"fmt"
66
"net"
77
"net/netip"
8-
"sync"
8+
"syscall"
99

10+
"github.com/metacubex/mihomo/common/atomic"
1011
"github.com/metacubex/mihomo/component/keepalive"
1112
"github.com/metacubex/mihomo/component/mptcp"
1213

1314
"github.com/metacubex/tfo-go"
1415
)
1516

1617
var (
17-
lc = tfo.ListenConfig{
18-
DisableTFO: true,
19-
}
20-
mutex sync.RWMutex
18+
globalTFO = atomic.NewBool(false)
19+
globalMPTCP = atomic.NewBool(false)
2120
)
2221

2322
func SetTfo(open bool) {
24-
mutex.Lock()
25-
defer mutex.Unlock()
26-
lc.DisableTFO = !open
23+
globalTFO.Store(open)
2724
}
2825

2926
func Tfo() bool {
30-
mutex.RLock()
31-
defer mutex.RUnlock()
32-
return !lc.DisableTFO
27+
return globalTFO.Load()
3328
}
3429

3530
func SetMPTCP(open bool) {
36-
mutex.Lock()
37-
defer mutex.Unlock()
38-
mptcp.SetNetListenConfig(&lc.ListenConfig, open)
31+
globalMPTCP.Store(open)
3932
}
4033

4134
func MPTCP() bool {
42-
mutex.RLock()
43-
defer mutex.RUnlock()
44-
return mptcp.GetNetListenConfig(&lc.ListenConfig)
35+
return globalMPTCP.Load()
36+
}
37+
38+
type controlFn = func(network, address string, c syscall.RawConn) error
39+
40+
type ListenerConfig struct {
41+
routeMark int
42+
}
43+
44+
func NewListenerConfig() *ListenerConfig {
45+
return &ListenerConfig{}
46+
}
47+
48+
func (l *ListenerConfig) SetRouteMark(mark int) {
49+
l.routeMark = mark
50+
}
51+
52+
func (l ListenerConfig) newListenConfig() *tfo.ListenConfig {
53+
lc := tfo.ListenConfig{DisableTFO: !Tfo()}
54+
keepalive.SetNetListenConfig(&lc.ListenConfig)
55+
mptcp.SetNetListenConfig(&lc.ListenConfig, MPTCP())
56+
lc.Control = func(network, address string, c syscall.RawConn) error {
57+
if l.routeMark != 0 {
58+
err := bindMarkToControl(l.routeMark)(network, address, c)
59+
if err != nil {
60+
return err
61+
}
62+
}
63+
return nil
64+
}
65+
return &lc
66+
}
67+
68+
func (l ListenerConfig) Listen(ctx context.Context, network, address string) (net.Listener, error) {
69+
address, err := preResolve(network, address)
70+
if err != nil {
71+
return nil, err
72+
}
73+
return l.newListenConfig().Listen(ctx, network, address)
74+
}
75+
76+
func (l ListenerConfig) ListenPacket(ctx context.Context, network, address string) (net.PacketConn, error) {
77+
address, err := preResolve(network, address)
78+
if err != nil {
79+
return nil, err
80+
}
81+
return l.newListenConfig().ListenPacket(ctx, network, address)
4582
}
4683

4784
func preResolve(network, address string) (string, error) {
@@ -67,41 +104,3 @@ func preResolve(network, address string) (string, error) {
67104
}
68105
return address, nil
69106
}
70-
71-
func ListenContext(ctx context.Context, network, address string) (net.Listener, error) {
72-
address, err := preResolve(network, address)
73-
if err != nil {
74-
return nil, err
75-
}
76-
77-
mutex.RLock()
78-
defer mutex.RUnlock()
79-
return lc.Listen(ctx, network, address)
80-
}
81-
82-
func Listen(network, address string) (net.Listener, error) {
83-
return ListenContext(context.Background(), network, address)
84-
}
85-
86-
func ListenPacketContext(ctx context.Context, network, address string) (net.PacketConn, error) {
87-
address, err := preResolve(network, address)
88-
if err != nil {
89-
return nil, err
90-
}
91-
92-
mutex.RLock()
93-
defer mutex.RUnlock()
94-
return lc.ListenPacket(ctx, network, address)
95-
}
96-
97-
func ListenPacket(network, address string) (net.PacketConn, error) {
98-
return ListenPacketContext(context.Background(), network, address)
99-
}
100-
101-
func init() {
102-
keepalive.SetDisableKeepAliveCallback.Register(func(b bool) {
103-
mutex.Lock()
104-
defer mutex.Unlock()
105-
keepalive.SetNetListenConfig(&lc.ListenConfig)
106-
})
107-
}

adapter/inbound/mark_linux.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package inbound
2+
3+
import (
4+
"syscall"
5+
)
6+
7+
func bindMarkToControl(mark int) controlFn {
8+
return func(network, address string, c syscall.RawConn) (err error) {
9+
var innerErr error
10+
err = c.Control(func(fd uintptr) {
11+
innerErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)
12+
})
13+
if innerErr != nil {
14+
err = innerErr
15+
}
16+
return
17+
}
18+
}

adapter/inbound/mark_other.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build !linux
2+
3+
package inbound
4+
5+
import (
6+
"syscall"
7+
)
8+
9+
func bindMarkToControl(mark int) controlFn {
10+
return func(network, address string, c syscall.RawConn) (err error) {
11+
return
12+
}
13+
}

component/keepalive/tcp_keepalive.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,12 @@ import (
66
"time"
77

88
"github.com/metacubex/mihomo/common/atomic"
9-
"github.com/metacubex/mihomo/common/utils"
109
)
1110

1211
var (
1312
keepAliveIdle = atomic.NewInt64(0)
1413
keepAliveInterval = atomic.NewInt64(0)
1514
disableKeepAlive = atomic.NewBool(false)
16-
17-
SetDisableKeepAliveCallback = utils.NewCallback[bool]()
1815
)
1916

2017
func SetKeepAliveIdle(t time.Duration) {
@@ -43,7 +40,6 @@ func SetDisableKeepAlive(disable bool) {
4340

4441
func setDisableKeepAlive(disable bool) {
4542
disableKeepAlive.Store(disable)
46-
SetDisableKeepAliveCallback.Emit(disable)
4743
}
4844

4945
func DisableKeepAlive() bool {

config/config.go

Lines changed: 67 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,17 @@ type GeoXUrl struct {
100100

101101
// Controller config
102102
type Controller struct {
103-
ExternalController string
104-
ExternalControllerTLS string
105-
ExternalControllerUnix string
106-
ExternalControllerPipe string
107-
ExternalUI string
108-
ExternalUIURL string
109-
ExternalUIName string
110-
ExternalDohServer string
111-
Secret string
112-
Cors Cors
103+
ExternalController string
104+
ExternalControllerTLS string
105+
ExternalControllerUnix string
106+
ExternalControllerPipe string
107+
ExternalControllerRoutingMark int
108+
ExternalUI string
109+
ExternalUIURL string
110+
ExternalUIName string
111+
ExternalDohServer string
112+
Secret string
113+
Cors Cors
113114
}
114115

115116
type Cors struct {
@@ -391,51 +392,52 @@ type RawTLS struct {
391392
}
392393

393394
type RawConfig struct {
394-
Port int `yaml:"port" json:"port"`
395-
SocksPort int `yaml:"socks-port" json:"socks-port"`
396-
RedirPort int `yaml:"redir-port" json:"redir-port"`
397-
TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"`
398-
MixedPort int `yaml:"mixed-port" json:"mixed-port"`
399-
ShadowSocksConfig string `yaml:"ss-config" json:"ss-config"`
400-
VmessConfig string `yaml:"vmess-config" json:"vmess-config"`
401-
InboundTfo bool `yaml:"inbound-tfo" json:"inbound-tfo"`
402-
InboundMPTCP bool `yaml:"inbound-mptcp" json:"inbound-mptcp"`
403-
Authentication []string `yaml:"authentication" json:"authentication"`
404-
SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes" json:"skip-auth-prefixes"`
405-
LanAllowedIPs []netip.Prefix `yaml:"lan-allowed-ips" json:"lan-allowed-ips"`
406-
LanDisAllowedIPs []netip.Prefix `yaml:"lan-disallowed-ips" json:"lan-disallowed-ips"`
407-
AllowLan bool `yaml:"allow-lan" json:"allow-lan"`
408-
BindAddress string `yaml:"bind-address" json:"bind-address"`
409-
Mode T.TunnelMode `yaml:"mode" json:"mode"`
410-
UnifiedDelay bool `yaml:"unified-delay" json:"unified-delay"`
411-
LogLevel log.LogLevel `yaml:"log-level" json:"log-level"`
412-
IPv6 bool `yaml:"ipv6" json:"ipv6"`
413-
ExternalController string `yaml:"external-controller" json:"external-controller"`
414-
ExternalControllerPipe string `yaml:"external-controller-pipe" json:"external-controller-pipe"`
415-
ExternalControllerUnix string `yaml:"external-controller-unix" json:"external-controller-unix"`
416-
ExternalControllerTLS string `yaml:"external-controller-tls" json:"external-controller-tls"`
417-
ExternalControllerCors RawCors `yaml:"external-controller-cors" json:"external-controller-cors"`
418-
ExternalUI string `yaml:"external-ui" json:"external-ui"`
419-
ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"`
420-
ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"`
421-
ExternalDohServer string `yaml:"external-doh-server" json:"external-doh-server"`
422-
Secret string `yaml:"secret" json:"secret"`
423-
Interface string `yaml:"interface-name" json:"interface-name"`
424-
RoutingMark int `yaml:"routing-mark" json:"routing-mark"`
425-
Tunnels []LC.Tunnel `yaml:"tunnels" json:"tunnels"`
426-
GeoAutoUpdate bool `yaml:"geo-auto-update" json:"geo-auto-update"`
427-
GeoUpdateInterval int `yaml:"geo-update-interval" json:"geo-update-interval"`
428-
GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"`
429-
GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"`
430-
GeositeMatcher string `yaml:"geosite-matcher" json:"geosite-matcher"`
431-
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
432-
FindProcessMode process.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"`
433-
GlobalClientFingerprint string `yaml:"global-client-fingerprint" json:"global-client-fingerprint"`
434-
GlobalUA string `yaml:"global-ua" json:"global-ua"`
435-
ETagSupport bool `yaml:"etag-support" json:"etag-support"`
436-
KeepAliveIdle int `yaml:"keep-alive-idle" json:"keep-alive-idle"`
437-
KeepAliveInterval int `yaml:"keep-alive-interval" json:"keep-alive-interval"`
438-
DisableKeepAlive bool `yaml:"disable-keep-alive" json:"disable-keep-alive"`
395+
Port int `yaml:"port" json:"port"`
396+
SocksPort int `yaml:"socks-port" json:"socks-port"`
397+
RedirPort int `yaml:"redir-port" json:"redir-port"`
398+
TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"`
399+
MixedPort int `yaml:"mixed-port" json:"mixed-port"`
400+
ShadowSocksConfig string `yaml:"ss-config" json:"ss-config"`
401+
VmessConfig string `yaml:"vmess-config" json:"vmess-config"`
402+
InboundTfo bool `yaml:"inbound-tfo" json:"inbound-tfo"`
403+
InboundMPTCP bool `yaml:"inbound-mptcp" json:"inbound-mptcp"`
404+
Authentication []string `yaml:"authentication" json:"authentication"`
405+
SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes" json:"skip-auth-prefixes"`
406+
LanAllowedIPs []netip.Prefix `yaml:"lan-allowed-ips" json:"lan-allowed-ips"`
407+
LanDisAllowedIPs []netip.Prefix `yaml:"lan-disallowed-ips" json:"lan-disallowed-ips"`
408+
AllowLan bool `yaml:"allow-lan" json:"allow-lan"`
409+
BindAddress string `yaml:"bind-address" json:"bind-address"`
410+
Mode T.TunnelMode `yaml:"mode" json:"mode"`
411+
UnifiedDelay bool `yaml:"unified-delay" json:"unified-delay"`
412+
LogLevel log.LogLevel `yaml:"log-level" json:"log-level"`
413+
IPv6 bool `yaml:"ipv6" json:"ipv6"`
414+
ExternalController string `yaml:"external-controller" json:"external-controller"`
415+
ExternalControllerRoutingMark int `yaml:"external-controller-routing-mark" json:"external-controller-routing-mark"`
416+
ExternalControllerPipe string `yaml:"external-controller-pipe" json:"external-controller-pipe"`
417+
ExternalControllerUnix string `yaml:"external-controller-unix" json:"external-controller-unix"`
418+
ExternalControllerTLS string `yaml:"external-controller-tls" json:"external-controller-tls"`
419+
ExternalControllerCors RawCors `yaml:"external-controller-cors" json:"external-controller-cors"`
420+
ExternalUI string `yaml:"external-ui" json:"external-ui"`
421+
ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"`
422+
ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"`
423+
ExternalDohServer string `yaml:"external-doh-server" json:"external-doh-server"`
424+
Secret string `yaml:"secret" json:"secret"`
425+
Interface string `yaml:"interface-name" json:"interface-name"`
426+
RoutingMark int `yaml:"routing-mark" json:"routing-mark"`
427+
Tunnels []LC.Tunnel `yaml:"tunnels" json:"tunnels"`
428+
GeoAutoUpdate bool `yaml:"geo-auto-update" json:"geo-auto-update"`
429+
GeoUpdateInterval int `yaml:"geo-update-interval" json:"geo-update-interval"`
430+
GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"`
431+
GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"`
432+
GeositeMatcher string `yaml:"geosite-matcher" json:"geosite-matcher"`
433+
TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
434+
FindProcessMode process.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"`
435+
GlobalClientFingerprint string `yaml:"global-client-fingerprint" json:"global-client-fingerprint"`
436+
GlobalUA string `yaml:"global-ua" json:"global-ua"`
437+
ETagSupport bool `yaml:"etag-support" json:"etag-support"`
438+
KeepAliveIdle int `yaml:"keep-alive-idle" json:"keep-alive-idle"`
439+
KeepAliveInterval int `yaml:"keep-alive-interval" json:"keep-alive-interval"`
440+
DisableKeepAlive bool `yaml:"disable-keep-alive" json:"disable-keep-alive"`
439441

440442
ProxyProvider map[string]map[string]any `yaml:"proxy-providers" json:"proxy-providers"`
441443
RuleProvider map[string]map[string]any `yaml:"rule-providers" json:"rule-providers"`
@@ -801,15 +803,16 @@ func parseController(cfg *RawConfig) (*Controller, error) {
801803
return nil, fmt.Errorf("external UI name is not local: %s", uiName)
802804
}
803805
return &Controller{
804-
ExternalController: cfg.ExternalController,
805-
ExternalUI: cfg.ExternalUI,
806-
ExternalUIURL: cfg.ExternalUIURL,
807-
ExternalUIName: cfg.ExternalUIName,
808-
Secret: cfg.Secret,
809-
ExternalControllerPipe: cfg.ExternalControllerPipe,
810-
ExternalControllerUnix: cfg.ExternalControllerUnix,
811-
ExternalControllerTLS: cfg.ExternalControllerTLS,
812-
ExternalDohServer: cfg.ExternalDohServer,
806+
ExternalController: cfg.ExternalController,
807+
ExternalUI: cfg.ExternalUI,
808+
ExternalUIURL: cfg.ExternalUIURL,
809+
ExternalUIName: cfg.ExternalUIName,
810+
Secret: cfg.Secret,
811+
ExternalControllerRoutingMark: cfg.ExternalControllerRoutingMark,
812+
ExternalControllerPipe: cfg.ExternalControllerPipe,
813+
ExternalControllerUnix: cfg.ExternalControllerUnix,
814+
ExternalControllerTLS: cfg.ExternalControllerTLS,
815+
ExternalDohServer: cfg.ExternalDohServer,
813816
Cors: Cors{
814817
AllowOrigins: cfg.ExternalControllerCors.AllowOrigins,
815818
AllowPrivateNetwork: cfg.ExternalControllerCors.AllowPrivateNetwork,

dns/server.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ func ReCreateServer(addr string, service resolver.Service) {
8181
address = addr
8282
server = &Server{service: service}
8383

84+
lc := inbound.NewListenerConfig()
85+
lc.SetRouteMark(0) // TODO: add route mark support for dns server
8486
go func() {
85-
p, err := inbound.ListenPacket("udp", addr)
87+
p, err := lc.ListenPacket(context.Background(), "udp", addr)
8688
if err != nil {
8789
log.Errorln("Start DNS server(UDP) error: %s", err.Error())
8890
return
@@ -98,7 +100,7 @@ func ReCreateServer(addr string, service resolver.Service) {
98100
}()
99101

100102
go func() {
101-
l, err := inbound.Listen("tcp", addr)
103+
l, err := lc.Listen(context.Background(), "tcp", addr)
102104
if err != nil {
103105
log.Errorln("Start DNS server(TCP) error: %s", err.Error())
104106
return

0 commit comments

Comments
 (0)