Skip to content

Commit

Permalink
chore: better keepalive handle
Browse files Browse the repository at this point in the history
  • Loading branch information
wwqgtxx committed Aug 14, 2024
1 parent 24c6e7d commit f20f371
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 119 deletions.
23 changes: 23 additions & 0 deletions common/net/tcp_keepalive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net

import (
"net"
"runtime"
"time"
)

var (
KeepAliveIdle = 0 * time.Second
KeepAliveInterval = 0 * time.Second
DisableKeepAlive = false
)

func TCPKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
if runtime.GOOS == "android" || DisableKeepAlive {
_ = tcp.SetKeepAlive(false)
} else {
tcpKeepAlive(tcp)
}
}
}
8 changes: 3 additions & 5 deletions common/net/tcp_keepalive_go122.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ package net

import "net"

func TCPKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAlive(true)
_ = tcp.SetKeepAlivePeriod(KeepAliveInterval)
}
func tcpKeepAlive(tcp *net.TCPConn) {
_ = tcp.SetKeepAlive(true)
_ = tcp.SetKeepAlivePeriod(KeepAliveInterval)
}
18 changes: 11 additions & 7 deletions common/net/tcp_keepalive_go123.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ package net

import "net"

func TCPKeepAlive(c net.Conn) {
if tcp, ok := c.(*net.TCPConn); ok {
_ = tcp.SetKeepAliveConfig(net.KeepAliveConfig{
Enable: true,
Idle: KeepAliveIdle,
Interval: KeepAliveInterval,
})
func tcpKeepAlive(tcp *net.TCPConn) {
config := net.KeepAliveConfig{
Enable: true,
Idle: KeepAliveIdle,
Interval: KeepAliveInterval,
}
if !SupportTCPKeepAliveCount() {
// it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1
// for Count on those old Windows if you intend to customize the TCP keep-alive settings.
config.Count = -1
}
_ = tcp.SetKeepAliveConfig(config)
}
15 changes: 15 additions & 0 deletions common/net/tcp_keepalive_go123_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build go1.23 && unix

package net

func SupportTCPKeepAliveIdle() bool {
return true
}

func SupportTCPKeepAliveInterval() bool {
return true
}

func SupportTCPKeepAliveCount() bool {
return true
}
63 changes: 63 additions & 0 deletions common/net/tcp_keepalive_go123_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//go:build go1.23 && windows

// copy and modify from golang1.23's internal/syscall/windows/version_windows.go

package net

import (
"errors"
"sync"
"syscall"

"github.com/metacubex/mihomo/constant/features"

"golang.org/x/sys/windows"
)

var (
supportTCPKeepAliveIdle bool
supportTCPKeepAliveInterval bool
supportTCPKeepAliveCount bool
)

var initTCPKeepAlive = sync.OnceFunc(func() {
s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_NO_HANDLE_INHERIT)
if err != nil {
// Fallback to checking the Windows version.
major, build := features.WindowsMajorVersion, features.WindowsBuildNumber
supportTCPKeepAliveIdle = major >= 10 && build >= 16299
supportTCPKeepAliveInterval = major >= 10 && build >= 16299
supportTCPKeepAliveCount = major >= 10 && build >= 15063
return
}
defer windows.Closesocket(s)
var optSupported = func(opt int) bool {
err := windows.SetsockoptInt(s, syscall.IPPROTO_TCP, opt, 1)
return !errors.Is(err, syscall.WSAENOPROTOOPT)
}
supportTCPKeepAliveIdle = optSupported(windows.TCP_KEEPIDLE)
supportTCPKeepAliveInterval = optSupported(windows.TCP_KEEPINTVL)
supportTCPKeepAliveCount = optSupported(windows.TCP_KEEPCNT)
})

// SupportTCPKeepAliveIdle indicates whether TCP_KEEPIDLE is supported.
// The minimal requirement is Windows 10.0.16299.
func SupportTCPKeepAliveIdle() bool {
initTCPKeepAlive()
return supportTCPKeepAliveIdle
}

// SupportTCPKeepAliveInterval indicates whether TCP_KEEPINTVL is supported.
// The minimal requirement is Windows 10.0.16299.
func SupportTCPKeepAliveInterval() bool {
initTCPKeepAlive()
return supportTCPKeepAliveInterval
}

// SupportTCPKeepAliveCount indicates whether TCP_KEEPCNT is supported.
// supports TCP_KEEPCNT.
// The minimal requirement is Windows 10.0.15063.
func SupportTCPKeepAliveCount() bool {
initTCPKeepAlive()
return supportTCPKeepAliveCount
}
6 changes: 0 additions & 6 deletions common/net/tcpip.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ import (
"fmt"
"net"
"strings"
"time"
)

var (
KeepAliveIdle = 0 * time.Second
KeepAliveInterval = 0 * time.Second
)

func SplitNetworkType(s string) (string, string, error) {
Expand Down
68 changes: 34 additions & 34 deletions component/dialer/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"time"

"github.com/metacubex/mihomo/component/resolver"
"github.com/metacubex/mihomo/constant/features"
"github.com/metacubex/mihomo/log"
)

Expand Down Expand Up @@ -79,29 +78,29 @@ func DialContext(ctx context.Context, network, address string, options ...Option
}

func ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort, options ...Option) (net.PacketConn, error) {
if features.CMFA && DefaultSocketHook != nil {
return listenPacketHooked(ctx, network, address)
}

cfg := applyOptions(options...)

lc := &net.ListenConfig{}
if cfg.interfaceName != "" {
bind := bindIfaceToListenConfig
if cfg.fallbackBind {
bind = fallbackBindIfaceToListenConfig
}
addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort)
if err != nil {
return nil, err
}
address = addr
}
if cfg.addrReuse {
addrReuseToListenConfig(lc)
}
if cfg.routingMark != 0 {
bindMarkToListenConfig(cfg.routingMark, lc, network, address)
if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CFMA)
socketHookToListenConfig(lc)
} else {
if cfg.interfaceName != "" {
bind := bindIfaceToListenConfig
if cfg.fallbackBind {
bind = fallbackBindIfaceToListenConfig
}
addr, err := bind(cfg.interfaceName, lc, network, address, rAddrPort)
if err != nil {
return nil, err
}
address = addr
}
if cfg.routingMark != 0 {
bindMarkToListenConfig(cfg.routingMark, lc, network, address)
}
}

return lc.ListenPacket(ctx, network, address)
Expand Down Expand Up @@ -149,25 +148,26 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
setMultiPathTCP(dialer)
}

if features.CMFA && DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo in CMFA
return dialContextHooked(ctx, dialer, network, address)
}

if opt.interfaceName != "" {
bind := bindIfaceToDialer
if opt.fallbackBind {
bind = fallbackBindIfaceToDialer
if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CFMA)
socketHookToToDialer(dialer)
} else {
if opt.interfaceName != "" {
bind := bindIfaceToDialer
if opt.fallbackBind {
bind = fallbackBindIfaceToDialer
}
if err := bind(opt.interfaceName, dialer, network, destination); err != nil {
return nil, err
}
}
if err := bind(opt.interfaceName, dialer, network, destination); err != nil {
return nil, err
if opt.routingMark != 0 {
bindMarkToDialer(opt.routingMark, dialer, network, destination)
}
if opt.tfo && !DisableTFO {
return dialTFO(ctx, *dialer, network, address)
}
}
if opt.routingMark != 0 {
bindMarkToDialer(opt.routingMark, dialer, network, destination)
}
if opt.tfo && !DisableTFO {
return dialTFO(ctx, *dialer, network, address)
}

return dialer.DialContext(ctx, network, address)
}

Expand Down
38 changes: 0 additions & 38 deletions component/dialer/patch_android.go

This file was deleted.

21 changes: 0 additions & 21 deletions component/dialer/patch_common.go

This file was deleted.

27 changes: 27 additions & 0 deletions component/dialer/socket_hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package dialer

import (
"context"
"net"
"syscall"
)

// SocketControl
// never change type traits because it's used in CFMA
type SocketControl func(network, address string, conn syscall.RawConn) error

// DefaultSocketHook
// never change type traits because it's used in CFMA
var DefaultSocketHook SocketControl

func socketHookToToDialer(dialer *net.Dialer) {
addControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error {
return DefaultSocketHook(network, address, c)
})
}

func socketHookToListenConfig(lc *net.ListenConfig) {
addControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error {
return DefaultSocketHook(network, address, c)
})
}
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ type RawConfig struct {
GlobalUA string `yaml:"global-ua"`
KeepAliveIdle int `yaml:"keep-alive-idle"`
KeepAliveInterval int `yaml:"keep-alive-interval"`
DisableKeepAlive bool `yaml:"disable-keep-alive"`

Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"`
ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
Expand Down Expand Up @@ -657,6 +658,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
if cfg.KeepAliveInterval != 0 {
N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
}
N.DisableKeepAlive = cfg.DisableKeepAlive

updater.ExternalUIPath = cfg.ExternalUI
// checkout externalUI exist
Expand Down
5 changes: 3 additions & 2 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ external-doh-server: /dns-query
global-client-fingerprint: chrome

# TCP keep alive interval
# keep-alive-idle: 7200
# keep-alive-interval: 75
# disable-keep-alive: false #目前在android端强制为true
# keep-alive-idle: 15
# keep-alive-interval: 15

# routing-mark:6666 # 配置 fwmark 仅用于 Linux
experimental:
Expand Down
8 changes: 2 additions & 6 deletions listener/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"net"

"github.com/metacubex/mihomo/adapter/inbound"
N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/auth"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
authStore "github.com/metacubex/mihomo/listener/auth"
)

Expand Down Expand Up @@ -74,11 +74,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, authenticator auth.Authe
}
continue
}
if features.CMFA {
if t, ok := conn.(*net.TCPConn); ok {
t.SetKeepAlive(false)
}
}
N.TCPKeepAlive(conn)
if isDefault { // only apply on default listener
if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {
_ = conn.Close()
Expand Down

0 comments on commit f20f371

Please sign in to comment.