diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index 4c8a2247..9387f7de 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -164,6 +164,8 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) option.Filter, option.ExcludeFilter, option.ExcludeType, + option.TestTimeout, + option.MaxFailedTimes, providers, }), disableUDP: option.DisableUDP, diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index 0ea3685b..b39ee3a6 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -31,14 +31,18 @@ type GroupBase struct { failedTesting atomic.Bool proxies [][]C.Proxy versions []atomic.Uint32 + TestTimeout int + maxFailedTimes int } type GroupBaseOption struct { outbound.BaseOption - filter string - excludeFilter string - excludeType string - providers []provider.ProxyProvider + filter string + excludeFilter string + excludeType string + TestTimeout int + maxFailedTimes int + providers []provider.ProxyProvider } func NewGroupBase(opt GroupBaseOption) *GroupBase { @@ -66,6 +70,15 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase { excludeTypeArray: excludeTypeArray, providers: opt.providers, failedTesting: atomic.NewBool(false), + TestTimeout: opt.TestTimeout, + maxFailedTimes: opt.maxFailedTimes, + } + + if gb.TestTimeout == 0 { + gb.TestTimeout = 5000 + } + if gb.maxFailedTimes == 0 { + gb.maxFailedTimes = 5 } gb.proxies = make([][]C.Proxy, len(opt.providers)) @@ -240,13 +253,13 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) { log.Debugln("ProxyGroup: %s first failed", gb.Name()) gb.failedTime = time.Now() } else { - if time.Since(gb.failedTime) > gb.failedTimeoutInterval() { + if time.Since(gb.failedTime) > time.Duration(gb.TestTimeout)*time.Millisecond { gb.failedTimes = 0 return } log.Debugln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes) - if gb.failedTimes >= gb.maxFailedTimes() { + if gb.failedTimes >= gb.maxFailedTimes { log.Warnln("because %s failed multiple times, active health check", gb.Name()) gb.healthCheck() } @@ -275,20 +288,8 @@ func (gb *GroupBase) healthCheck() { gb.failedTimes = 0 } -func (gb *GroupBase) failedIntervalTime() int64 { - return 5 * time.Second.Milliseconds() -} - func (gb *GroupBase) onDialSuccess() { if !gb.failedTesting.Load() { gb.failedTimes = 0 } } - -func (gb *GroupBase) maxFailedTimes() int { - return 5 -} - -func (gb *GroupBase) failedTimeoutInterval() time.Duration { - return 5 * time.Second -} diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 976a2e89..4cb0db00 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -266,6 +266,8 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide option.Filter, option.ExcludeFilter, option.ExcludeType, + option.TestTimeout, + option.MaxFailedTimes, providers, }), strategyFn: strategyFn, diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 96b23eb2..74947587 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -29,6 +29,7 @@ type GroupCommonOption struct { URL string `group:"url,omitempty"` Interval int `group:"interval,omitempty"` TestTimeout int `group:"timeout,omitempty"` + MaxFailedTimes int `group:"max-failed-times,omitempty"` Lazy bool `group:"lazy,omitempty"` DisableUDP bool `group:"disable-udp,omitempty"` Filter string `group:"filter,omitempty"` diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 6a8e8bb1..07fbcd95 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -160,6 +160,8 @@ func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Re "", "", "", + 5000, + 5, providers, }), Hidden: option.Hidden, diff --git a/adapter/outboundgroup/selector.go b/adapter/outboundgroup/selector.go index 3ac740f4..20eca70f 100644 --- a/adapter/outboundgroup/selector.go +++ b/adapter/outboundgroup/selector.go @@ -114,6 +114,8 @@ func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider) option.Filter, option.ExcludeFilter, option.ExcludeType, + option.TestTimeout, + option.MaxFailedTimes, providers, }), selected: "COMPATIBLE", diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 8439772c..5da44f38 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -235,6 +235,8 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o option.Filter, option.ExcludeFilter, option.ExcludeType, + option.TestTimeout, + option.MaxFailedTimes, providers, }), fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10), diff --git a/config/config.go b/config/config.go index c5f4bb77..d4b9ad89 100644 --- a/config/config.go +++ b/config/config.go @@ -152,6 +152,7 @@ type IPTables struct { Enable bool `yaml:"enable" json:"enable"` InboundInterface string `yaml:"inbound-interface" json:"inbound-interface"` Bypass []string `yaml:"bypass" json:"bypass"` + DnsRedirect bool `yaml:"dns-redirect" json:"dns-redirect"` } type Sniffer struct { @@ -440,6 +441,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { Enable: false, InboundInterface: "lo", Bypass: []string{}, + DnsRedirect: true, }, NTP: RawNTP{ Enable: false, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index e4a31a79..14e826d7 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -478,6 +478,9 @@ func updateIPTables(cfg *config.Config) { bypass = iptables.Bypass tProxyPort = cfg.General.TProxyPort dnsCfg = cfg.DNS + DnsRedirect = iptables.DnsRedirect + + dnsPort netip.AddrPort ) if tProxyPort == 0 { @@ -485,15 +488,17 @@ func updateIPTables(cfg *config.Config) { return } - if !dnsCfg.Enable { - err = fmt.Errorf("DNS server must be enable") - return - } + if DnsRedirect { + if !dnsCfg.Enable { + err = fmt.Errorf("DNS server must be enable") + return + } - dnsPort, err := netip.ParseAddrPort(dnsCfg.Listen) - if err != nil { - err = fmt.Errorf("DNS server must be correct") - return + dnsPort, err = netip.ParseAddrPort(dnsCfg.Listen) + if err != nil { + err = fmt.Errorf("DNS server must be correct") + return + } } if iptables.InboundInterface != "" { @@ -504,7 +509,7 @@ func updateIPTables(cfg *config.Config) { dialer.DefaultRoutingMark.Store(2158) } - err = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), dnsPort.Port()) + err = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), DnsRedirect, dnsPort.Port()) if err != nil { return } diff --git a/listener/tproxy/tproxy_iptables.go b/listener/tproxy/tproxy_iptables.go index 5ddd7b4c..6c6e2cc8 100644 --- a/listener/tproxy/tproxy_iptables.go +++ b/listener/tproxy/tproxy_iptables.go @@ -15,6 +15,7 @@ var ( dnsPort uint16 tProxyPort uint16 interfaceName string + DnsRedirect bool ) const ( @@ -22,7 +23,7 @@ const ( PROXY_ROUTE_TABLE = "0x2d0" ) -func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint16) error { +func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dnsredir bool, dport uint16) error { if _, err := cmd.ExecCmd("iptables -V"); err != nil { return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS) } @@ -33,6 +34,7 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 interfaceName = ifname tProxyPort = tport + DnsRedirect = dnsredir dnsPort = dport // add route @@ -58,8 +60,10 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 execCmd("iptables -t mangle -N mihomo_prerouting") execCmd("iptables -t mangle -F mihomo_prerouting") execCmd("iptables -t mangle -A mihomo_prerouting -s 172.17.0.0/16 -j RETURN") - execCmd("iptables -t mangle -A mihomo_prerouting -p udp --dport 53 -j ACCEPT") - execCmd("iptables -t mangle -A mihomo_prerouting -p tcp --dport 53 -j ACCEPT") + if DnsRedirect { + execCmd("iptables -t mangle -A mihomo_prerouting -p udp --dport 53 -j ACCEPT") + execCmd("iptables -t mangle -A mihomo_prerouting -p tcp --dport 53 -j ACCEPT") + } execCmd("iptables -t mangle -A mihomo_prerouting -m addrtype --dst-type LOCAL -j RETURN") addLocalnetworkToChain("mihomo_prerouting", bypass) execCmd("iptables -t mangle -A mihomo_prerouting -p tcp -m socket -j mihomo_divert") @@ -68,8 +72,10 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_prerouting -p udp -j TPROXY --on-port %d --tproxy-mark %s/%s", tProxyPort, PROXY_FWMARK, PROXY_FWMARK)) execCmd("iptables -t mangle -A PREROUTING -j mihomo_prerouting") - execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) - execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + if DnsRedirect { + execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + } // set post routing if interfaceName != "lo" { @@ -80,8 +86,10 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 execCmd("iptables -t mangle -N mihomo_output") execCmd("iptables -t mangle -F mihomo_output") execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_output -m mark --mark %#x -j RETURN", dialer.DefaultRoutingMark.Load())) - execCmd("iptables -t mangle -A mihomo_output -p udp -m multiport --dports 53,123,137 -j ACCEPT") - execCmd("iptables -t mangle -A mihomo_output -p tcp --dport 53 -j ACCEPT") + if DnsRedirect { + execCmd("iptables -t mangle -A mihomo_output -p udp -m multiport --dports 53,123,137 -j ACCEPT") + execCmd("iptables -t mangle -A mihomo_output -p tcp --dport 53 -j ACCEPT") + } execCmd("iptables -t mangle -A mihomo_output -m addrtype --dst-type LOCAL -j RETURN") execCmd("iptables -t mangle -A mihomo_output -m addrtype --dst-type BROADCAST -j RETURN") addLocalnetworkToChain("mihomo_output", bypass) @@ -90,20 +98,22 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 execCmd(fmt.Sprintf("iptables -t mangle -I OUTPUT -o %s -j mihomo_output", interfaceName)) // set dns output - execCmd("iptables -t nat -N mihomo_dns_output") - execCmd("iptables -t nat -F mihomo_dns_output") - execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -m mark --mark %#x -j RETURN", dialer.DefaultRoutingMark.Load())) - execCmd("iptables -t nat -A mihomo_dns_output -s 172.17.0.0/16 -j RETURN") - execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p udp -j REDIRECT --to-ports %d", dnsPort)) - execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p tcp -j REDIRECT --to-ports %d", dnsPort)) - execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j mihomo_dns_output") - execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j mihomo_dns_output") + if DnsRedirect { + execCmd("iptables -t nat -N mihomo_dns_output") + execCmd("iptables -t nat -F mihomo_dns_output") + execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -m mark --mark %#x -j RETURN", dialer.DefaultRoutingMark.Load())) + execCmd("iptables -t nat -A mihomo_dns_output -s 172.17.0.0/16 -j RETURN") + execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p udp -j REDIRECT --to-ports %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p tcp -j REDIRECT --to-ports %d", dnsPort)) + execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j mihomo_dns_output") + execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j mihomo_dns_output") + } return nil } func CleanupTProxyIPTables() { - if runtime.GOOS != "linux" || interfaceName == "" || tProxyPort == 0 || dnsPort == 0 { + if runtime.GOOS != "linux" || interfaceName == "" || tProxyPort == 0 { return } @@ -130,8 +140,10 @@ func CleanupTProxyIPTables() { } // clean PREROUTING - execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) - execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + if DnsRedirect { + execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + } execCmd("iptables -t mangle -D PREROUTING -j mihomo_prerouting") // clean POSTROUTING @@ -141,8 +153,10 @@ func CleanupTProxyIPTables() { // clean OUTPUT execCmd(fmt.Sprintf("iptables -t mangle -D OUTPUT -o %s -j mihomo_output", interfaceName)) - execCmd("iptables -t nat -D OUTPUT -p tcp --dport 53 -j mihomo_dns_output") - execCmd("iptables -t nat -D OUTPUT -p udp --dport 53 -j mihomo_dns_output") + if DnsRedirect { + execCmd("iptables -t nat -D OUTPUT -p tcp --dport 53 -j mihomo_dns_output") + execCmd("iptables -t nat -D OUTPUT -p udp --dport 53 -j mihomo_dns_output") + } // clean chain execCmd("iptables -t mangle -F mihomo_prerouting") @@ -151,9 +165,10 @@ func CleanupTProxyIPTables() { execCmd("iptables -t mangle -X mihomo_divert") execCmd("iptables -t mangle -F mihomo_output") execCmd("iptables -t mangle -X mihomo_output") - execCmd("iptables -t nat -F mihomo_dns_output") - execCmd("iptables -t nat -X mihomo_dns_output") - + if DnsRedirect { + execCmd("iptables -t nat -F mihomo_dns_output") + execCmd("iptables -t nat -X mihomo_dns_output") + } interfaceName = "" tProxyPort = 0 dnsPort = 0