diff --git a/config/config.go b/config/config.go index 9cad0915..e0efcf7d 100644 --- a/config/config.go +++ b/config/config.go @@ -213,7 +213,6 @@ type Experimental struct { // Config is clash config manager type Config struct { General *General - Tun *Tun IPTables *IPTables DNS *DNS Experimental *Experimental @@ -496,11 +495,10 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.DNS = dnsCfg - tunCfg, err := parseTun(rawCfg.Tun, config.General, dnsCfg) + err = parseTun(rawCfg.Tun, config.General, dnsCfg) if err != nil { return nil, err } - config.Tun = tunCfg config.Users = parseAuthentication(rawCfg.Authentication) @@ -1126,7 +1124,7 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser { return users } -func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { +func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) error { var dnsHijack []netip.AddrPort for _, d := range rawTun.DNSHijack { @@ -1136,7 +1134,7 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { d = strings.Replace(d, "any", "0.0.0.0", 1) addrPort, err := netip.ParseAddrPort(d) if err != nil { - return nil, fmt.Errorf("parse dns-hijack url error: %w", err) + return fmt.Errorf("parse dns-hijack url error: %w", err) } dnsHijack = append(dnsHijack, addrPort) @@ -1150,7 +1148,7 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { } tunAddressPrefix = netip.PrefixFrom(tunAddressPrefix.Addr(), 30) - return &Tun{ + general.Tun = Tun{ Enable: rawTun.Enable, Device: rawTun.Device, Stack: rawTun.Stack, @@ -1174,7 +1172,9 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { ExcludePackage: rawTun.ExcludePackage, EndpointIndependentNat: rawTun.EndpointIndependentNat, UDPTimeout: rawTun.UDPTimeout, - }, nil + } + + return nil } func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 1a785aed..760cb52f 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -86,7 +86,7 @@ func ApplyConfig(cfg *config.Config, force bool) { loadRuleProvider(cfg.RuleProviders) updateGeneral(cfg.General, force) updateIPTables(cfg) - updateTun(cfg.Tun) + updateTun(cfg.General) updateExperimental(cfg) log.SetLevel(cfg.General.LogLevel) @@ -258,12 +258,12 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) { wg.Wait() } -func updateTun(tun *config.Tun) { - if tun == nil { +func updateTun(general *config.General) { + if general == nil { return } - P.ReCreateTun(*tun, tunnel.TCPIn(), tunnel.UDPIn()) - P.ReCreateRedirToTun(tun.RedirectToTun) + P.ReCreateTun(general.Tun, tunnel.TCPIn(), tunnel.UDPIn()) + P.ReCreateRedirToTun(general.Tun.RedirectToTun) } func updateSniffer(sniffer *config.Sniffer) { @@ -403,7 +403,7 @@ func updateIPTables(cfg *config.Config) { } }() - if cfg.Tun.Enable { + if cfg.General.Tun.Enable { err = fmt.Errorf("when tun is enabled, iptables cannot be set automatically") return } diff --git a/hub/route/configs.go b/hub/route/configs.go index 82f65284..773b7372 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -1,14 +1,16 @@ package route import ( - "github.com/Dreamacro/clash/component/dialer" "net/http" + "net/netip" "path/filepath" "sync" + "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/constant" + C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/hub/executor" P "github.com/Dreamacro/clash/listener" "github.com/Dreamacro/clash/log" @@ -38,7 +40,7 @@ type configSchema struct { RedirPort *int `json:"redir-port"` TProxyPort *int `json:"tproxy-port"` MixedPort *int `json:"mixed-port"` - Tun *config.Tun `json:"tun"` + Tun *tunSchema `json:"tun"` AllowLan *bool `json:"allow-lan"` BindAddress *string `json:"bind-address"` Mode *tunnel.TunnelMode `json:"mode"` @@ -49,6 +51,32 @@ type configSchema struct { InterfaceName *string `json:"interface-name"` } +type tunSchema struct { + Enable bool `yaml:"enable" json:"enable"` + Device *string `yaml:"device" json:"device"` + Stack *C.TUNStack `yaml:"stack" json:"stack"` + DNSHijack *[]netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"` + AutoRoute *bool `yaml:"auto-route" json:"auto-route"` + AutoDetectInterface *bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` + //RedirectToTun []string `yaml:"-" json:"-"` + + MTU *uint32 `yaml:"mtu" json:"mtu,omitempty"` + //Inet4Address *[]config.ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` + Inet6Address *[]config.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` + StrictRoute *bool `yaml:"strict-route" json:"strict_route,omitempty"` + Inet4RouteAddress *[]config.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` + Inet6RouteAddress *[]config.ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"` + IncludeUID *[]uint32 `yaml:"include-uid" json:"include_uid,omitempty"` + IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` + ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` + ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` + IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include_android_user,omitempty"` + IncludePackage *[]string `yaml:"include-package" json:"include_package,omitempty"` + ExcludePackage *[]string `yaml:"exclude-package" json:"exclude_package,omitempty"` + EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` + UDPTimeout *int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` +} + func getConfigs(w http.ResponseWriter, r *http.Request) { general := executor.GetGeneral() render.JSON(w, r, general) @@ -62,6 +90,64 @@ func pointerOrDefault(p *int, def int) int { return def } +func pointerOrDefaultTun(p *tunSchema, def config.Tun) config.Tun { + if p != nil { + def.Enable = p.Enable + if p.Device != nil { + def.Device = *p.Device + } + if p.Stack != nil { + def.Stack = *p.Stack + } + if p.DNSHijack != nil { + def.DNSHijack = *p.DNSHijack + } + if p.AutoRoute != nil { + def.AutoRoute = *p.AutoRoute + } + if p.AutoDetectInterface != nil { + def.AutoDetectInterface = *p.AutoDetectInterface + } + if p.MTU != nil { + def.MTU = *p.MTU + } + //if p.Inet4Address != nil { + // def.Inet4Address = *p.Inet4Address + //} + if p.Inet6Address != nil { + def.Inet6Address = *p.Inet6Address + } + if p.IncludeUID != nil { + def.IncludeUID = *p.IncludeUID + } + if p.IncludeUIDRange != nil { + def.IncludeUIDRange = *p.IncludeUIDRange + } + if p.ExcludeUID != nil { + def.ExcludeUID = *p.ExcludeUID + } + if p.ExcludeUIDRange != nil { + def.ExcludeUIDRange = *p.ExcludeUIDRange + } + if p.IncludeAndroidUser != nil { + def.IncludeAndroidUser = *p.IncludeAndroidUser + } + if p.IncludePackage != nil { + def.IncludePackage = *p.IncludePackage + } + if p.ExcludePackage != nil { + def.ExcludePackage = *p.ExcludePackage + } + if p.EndpointIndependentNat != nil { + def.EndpointIndependentNat = *p.EndpointIndependentNat + } + if p.UDPTimeout != nil { + def.UDPTimeout = *p.UDPTimeout + } + } + return def +} + func patchConfigs(w http.ResponseWriter, r *http.Request) { general := &configSchema{} if err := render.DecodeJSON(r.Body, general); err != nil { @@ -100,6 +186,7 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort), tcpIn, udpIn) P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn) P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn) + P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tcpIn, udpIn) if general.Mode != nil { tunnel.SetMode(*general.Mode) diff --git a/listener/listener.go b/listener/listener.go index 4983751b..e608cb1e 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -52,7 +52,7 @@ var ( autoRedirMux sync.Mutex tcMux sync.Mutex - lastTunConf config.Tun + LastTunConf config.Tun ) type Ports struct { @@ -339,7 +339,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P func ReCreateTun(tunConf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tunMux.Lock() defer func() { - lastTunConf = tunConf + LastTunConf = tunConf tunMux.Unlock() }() @@ -351,7 +351,7 @@ func ReCreateTun(tunConf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *i } }() - if !hasTunConfigChange(tunConf) { + if !hasTunConfigChange(&tunConf) { if tunLister != nil { tunLister.FlushDefaultInterface() } @@ -512,34 +512,83 @@ func genAddr(host string, port int, allowLan bool) string { return fmt.Sprintf("127.0.0.1:%d", port) } -func hasTunConfigChange(tunConf config.Tun) bool { - if lastTunConf.Enable != tunConf.Enable || - lastTunConf.Device != tunConf.Device || - lastTunConf.Stack != tunConf.Stack || - lastTunConf.AutoRoute != tunConf.AutoRoute || - lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface { +func hasTunConfigChange(tunConf *config.Tun) bool { + if LastTunConf.Enable != tunConf.Enable || + LastTunConf.Device != tunConf.Device || + LastTunConf.Stack != tunConf.Stack || + LastTunConf.AutoRoute != tunConf.AutoRoute || + LastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface || + LastTunConf.MTU != tunConf.MTU || + LastTunConf.StrictRoute != tunConf.StrictRoute || + LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat || + LastTunConf.UDPTimeout != tunConf.UDPTimeout { return true } - if len(lastTunConf.DNSHijack) != len(tunConf.DNSHijack) { + if len(LastTunConf.DNSHijack) != len(tunConf.DNSHijack) { return true } - sort.Slice(lastTunConf.DNSHijack, func(i, j int) bool { - return lastTunConf.DNSHijack[i].Addr().Less(lastTunConf.DNSHijack[j].Addr()) - }) - sort.Slice(tunConf.DNSHijack, func(i, j int) bool { return tunConf.DNSHijack[i].Addr().Less(tunConf.DNSHijack[j].Addr()) }) - for i, dns := range tunConf.DNSHijack { - if dns != lastTunConf.DNSHijack[i] { - return true - } - } + sort.Slice(tunConf.Inet4Address, func(i, j int) bool { + return tunConf.Inet4Address[i].Build().String() < tunConf.Inet4Address[j].Build().String() + }) - if slices.Equal(tunConf.Inet4Address, lastTunConf.Inet4Address) && slices.Equal(tunConf.Inet6Address, lastTunConf.Inet6Address) { + sort.Slice(tunConf.Inet6Address, func(i, j int) bool { + return tunConf.Inet6Address[i].Build().String() < tunConf.Inet6Address[j].Build().String() + }) + + sort.Slice(tunConf.Inet4RouteAddress, func(i, j int) bool { + return tunConf.Inet4RouteAddress[i].Build().String() < tunConf.Inet4RouteAddress[j].Build().String() + }) + + sort.Slice(tunConf.Inet6RouteAddress, func(i, j int) bool { + return tunConf.Inet6RouteAddress[i].Build().String() < tunConf.Inet6RouteAddress[j].Build().String() + }) + + sort.Slice(tunConf.IncludeUID, func(i, j int) bool { + return tunConf.IncludeUID[i] < tunConf.IncludeUID[j] + }) + + sort.Slice(tunConf.IncludeUIDRange, func(i, j int) bool { + return tunConf.IncludeUIDRange[i] < tunConf.IncludeUIDRange[j] + }) + + sort.Slice(tunConf.ExcludeUID, func(i, j int) bool { + return tunConf.ExcludeUID[i] < tunConf.ExcludeUID[j] + }) + + sort.Slice(tunConf.ExcludeUIDRange, func(i, j int) bool { + return tunConf.ExcludeUIDRange[i] < tunConf.ExcludeUIDRange[j] + }) + + sort.Slice(tunConf.IncludeAndroidUser, func(i, j int) bool { + return tunConf.IncludeAndroidUser[i] < tunConf.IncludeAndroidUser[j] + }) + + sort.Slice(tunConf.IncludePackage, func(i, j int) bool { + return tunConf.IncludePackage[i] < tunConf.IncludePackage[j] + }) + + sort.Slice(tunConf.ExcludePackage, func(i, j int) bool { + return tunConf.ExcludePackage[i] < tunConf.ExcludePackage[j] + }) + + if !slices.Equal(tunConf.DNSHijack, LastTunConf.DNSHijack) || + !slices.Equal(tunConf.Inet4Address, LastTunConf.Inet4Address) || + !slices.Equal(tunConf.Inet6Address, LastTunConf.Inet6Address) || + !slices.Equal(tunConf.Inet4RouteAddress, LastTunConf.Inet4RouteAddress) || + !slices.Equal(tunConf.Inet6RouteAddress, LastTunConf.Inet6RouteAddress) || + !slices.Equal(tunConf.IncludeUID, LastTunConf.IncludeUID) || + !slices.Equal(tunConf.IncludeUIDRange, LastTunConf.IncludeUIDRange) || + !slices.Equal(tunConf.ExcludeUID, LastTunConf.ExcludeUID) || + !slices.Equal(tunConf.ExcludeUIDRange, LastTunConf.ExcludeUIDRange) || + !slices.Equal(tunConf.IncludeAndroidUser, LastTunConf.IncludeAndroidUser) || + !slices.Equal(tunConf.IncludePackage, LastTunConf.IncludePackage) || + !slices.Equal(tunConf.ExcludePackage, LastTunConf.ExcludePackage) { return true } @@ -551,5 +600,5 @@ func Cleanup(wait bool) { tunLister.Close() tunLister = nil } - lastTunConf = config.Tun{} + LastTunConf = config.Tun{} }