diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 00000000..a81cfc1c --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,22 @@ +name: Linter +on: [push, pull_request] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Get latest go version + id: version + run: | + echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') + + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: ${{ steps.version.outputs.go_version }} + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest diff --git a/.golangci.yaml b/.golangci.yaml index 05cf626a..a0764585 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -4,9 +4,7 @@ linters: - gofumpt - staticcheck - govet -# - gci - - gofmt - - goimports + - gci linters-settings: gci: diff --git a/Makefile b/Makefile index 500d2bac..039f5368 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ all:linux-amd64 linux-arm64\ windows-amd64 windows-arm64\ docker: - GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ + $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ darwin-amd64v3: GOARCH=amd64 GOOS=darwin GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ diff --git a/README.md b/README.md index dad1b7d3..86959230 100644 --- a/README.md +++ b/README.md @@ -212,16 +212,16 @@ proxies: grpc-service-name: grpcname ``` -### IPTABLES auto-configuration -Only work on Linux OS who support `iptables`, Clash will auto-configuration iptables for tproxy listener when `tproxy-port` value isn't zero. +### IPTABLES configuration +Work on Linux OS who's supported `iptables` -If `TPROXY` is enabled, the `TUN` must be disabled. ```yaml # Enable the TPROXY listener tproxy-port: 9898 -# Disable the TUN listener -tun: - enable: false + +iptables: + enable: true # default is false + inbound-interface: eth0 # detect the inbound interface, default is 'lo' ``` diff --git a/config/config.go b/config/config.go index 702d93a1..34794424 100644 --- a/config/config.go +++ b/config/config.go @@ -120,6 +120,12 @@ type Script struct { ShortcutsCode map[string]string `yaml:"shortcuts" json:"shortcuts"` } +// IPTables config +type IPTables struct { + Enable bool `yaml:"enable" json:"enable"` + InboundInterface string `yaml:"inbound-interface" json:"inbound-interface"` +} + // Experimental config type Experimental struct{} @@ -127,6 +133,7 @@ type Experimental struct{} type Config struct { General *General Tun *Tun + IPTables *IPTables DNS *DNS Experimental *Experimental Hosts *trie.DomainTrie @@ -196,6 +203,7 @@ type RawConfig struct { Hosts map[string]string `yaml:"hosts"` DNS RawDNS `yaml:"dns"` Tun RawTun `yaml:"tun"` + IPTables IPTables `yaml:"iptables"` Experimental Experimental `yaml:"experimental"` Profile Profile `yaml:"profile"` Proxy []map[string]any `yaml:"proxies"` @@ -234,9 +242,13 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { Enable: false, Device: "", Stack: C.TunGvisor, - DNSHijack: []string{"198.18.0.2:53"}, // default hijack all dns query + DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query AutoRoute: true, }, + IPTables: IPTables{ + Enable: false, + InboundInterface: "lo", + }, DNS: RawDNS{ Enable: false, UseHosts: true, @@ -255,8 +267,6 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { "1.0.0.1", }, NameServer: []string{ - "223.5.5.5", - "119.29.29.29", "https://doh.pub/dns-query", "tls://223.5.5.5:853", }, @@ -288,6 +298,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { startTime := time.Now() config.Experimental = &rawCfg.Experimental config.Profile = &rawCfg.Profile + config.IPTables = &rawCfg.IPTables general, err := parseGeneral(rawCfg) if err != nil { @@ -630,7 +641,7 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie, error) { if ip == nil { return nil, fmt.Errorf("%s is not a valid IP", ipStr) } - tree.Insert(domain, ip) + _ = tree.Insert(domain, ip) } } @@ -655,7 +666,7 @@ func hostWithDefaultPort(host string, defPort string) (string, error) { } func parseNameServer(servers []string) ([]dns.NameServer, error) { - nameservers := []dns.NameServer{} + var nameservers []dns.NameServer for idx, server := range servers { // parse without scheme .e.g 8.8.8.8:53 @@ -727,7 +738,7 @@ func parseNameServerPolicy(nsPolicy map[string]string) (map[string]dns.NameServe } func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) { - ipNets := []*net.IPNet{} + var ipNets []*net.IPNet for idx, ip := range ips { _, ipnet, err := net.ParseCIDR(ip) @@ -829,7 +840,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie, rules []C.Rule) (*DNS, if len(cfg.FakeIPFilter) != 0 { host = trie.New() for _, domain := range cfg.FakeIPFilter { - host.Insert(domain, true) + _ = host.Insert(domain, true) } } diff --git a/go.mod b/go.mod index 31f2ddaf..9bda4cdc 100644 --- a/go.mod +++ b/go.mod @@ -26,10 +26,10 @@ require ( golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6 - golang.zx2c4.com/wireguard/windows v0.5.3 + golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 - gvisor.dev/gvisor v0.0.0-20220315202956-f1399ecf1672 + gvisor.dev/gvisor v0.0.0-20220319025644-e785bfc153f5 ) require ( @@ -54,7 +54,6 @@ require ( golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect - ) replace golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 => github.com/MetaCubeX/wintun-go v0.0.0-20220319102620-bbc5e6b2015e diff --git a/go.sum b/go.sum index 08b6d877..63a7f0fc 100644 --- a/go.sum +++ b/go.sum @@ -341,8 +341,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6 h1:kgBK1EGuTIYbwoKROmsoV0FQp08gnCcVa110A4Unqhk= golang.zx2c4.com/wireguard v0.0.0-20220318042302-193cf8d6a5d6/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U= -golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= -golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= +golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469 h1:SEYkJAIuYAsSAPkCffOiYLtq5brBDSI+L0mRjSsvSTY= +golang.zx2c4.com/wireguard/windows v0.5.4-0.20220317000008-6432784c2469/go.mod h1:1CeiatTZwcwSFA3cAtMm8CQoroviTldnxd7DOgM/vI4= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -385,8 +385,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -gvisor.dev/gvisor v0.0.0-20220315202956-f1399ecf1672 h1:aXIFpjZYl3zv2rQyr4rSit5Uq0k7BVXC8lJaDa4Cg7M= -gvisor.dev/gvisor v0.0.0-20220315202956-f1399ecf1672/go.mod h1:V4WNP2Uwtx69eOhvLDSQ734EaTJTaBI3P8KgRAlROsg= +gvisor.dev/gvisor v0.0.0-20220319025644-e785bfc153f5 h1:a3DiynlmTM9IGW+twgZSDhYNGcnxvU/+pbzrl1xMcJ4= +gvisor.dev/gvisor v0.0.0-20220319025644-e785bfc153f5/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 6bbfe6e1..4a9b7c8c 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -2,15 +2,12 @@ package executor import ( "fmt" - "github.com/Dreamacro/clash/listener/tun/ipstack/commons" "net" "os" "runtime" "strconv" "sync" - "github.com/Dreamacro/clash/listener/tproxy" - "github.com/Dreamacro/clash/adapter" "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/component/auth" @@ -27,6 +24,7 @@ import ( "github.com/Dreamacro/clash/dns" P "github.com/Dreamacro/clash/listener" authStore "github.com/Dreamacro/clash/listener/auth" + "github.com/Dreamacro/clash/listener/tproxy" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" ) @@ -79,8 +77,8 @@ func ApplyConfig(cfg *config.Config, force bool) { updateRules(cfg.Rules, cfg.RuleProviders) updateDNS(cfg.DNS, cfg.Tun) updateGeneral(cfg.General, force) - updateTun(cfg.Tun) - autoUpdateIPTables(cfg.DNS, cfg.General, cfg.Tun) + updateIPTables(cfg) + updateTun(cfg.Tun, cfg.DNS) updateExperimental(cfg) updateHosts(cfg.Hosts) loadProvider(cfg.RuleProviders, cfg.Providers) @@ -211,8 +209,8 @@ func loadProvider(ruleProviders map[string]*provider.RuleProvider, proxyProvider } } -func updateTun(tun *config.Tun) { - P.ReCreateTun(tun, tunnel.TCPIn(), tunnel.UDPIn()) +func updateTun(tun *config.Tun, dns *config.DNS) { + P.ReCreateTun(tun, dns, tunnel.TCPIn(), tunnel.UDPIn()) } func updateGeneral(general *config.General, force bool) { @@ -301,60 +299,67 @@ func patchSelectGroup(proxies map[string]C.Proxy) { } } -func autoUpdateIPTables(dns *config.DNS, general *config.General, tun *config.Tun) { - if runtime.GOOS != "linux" || general.TProxyPort == 0 { +func updateIPTables(cfg *config.Config) { + tproxy.CleanupTProxyIPTables() + + iptables := cfg.IPTables + if runtime.GOOS != "linux" || !iptables.Enable { return } var err error defer func() { if err != nil { - log.Errorln("Setting iptables failed: %s", err.Error()) + log.Errorln("[IPTABLES] setting iptables failed: %s", err.Error()) os.Exit(2) } }() - if !dns.Enable || dns.Listen == "" { + var ( + inboundInterface = "lo" + tProxyPort = cfg.General.TProxyPort + dnsCfg = cfg.DNS + ) + + if tProxyPort == 0 { + err = fmt.Errorf("tproxy-port must be greater than zero") + return + } + + if !dnsCfg.Enable { err = fmt.Errorf("DNS server must be enable") return } - if tun.Enable { - err = fmt.Errorf("TUN device must be disabe") + _, dnsPortStr, err := net.SplitHostPort(dnsCfg.Listen) + if err != nil { + err = fmt.Errorf("DNS server must be correct") return } - err = updateIPTables(dns, general) - go commons.DefaultInterfaceChangeMonitor(func(_ string) { - updateIPTables(dns, general) - }) -} - -func updateIPTables(dns *config.DNS, general *config.General) error { - tproxy.CleanUpTProxyLinuxIPTables() - - _, dnsPortStr, err := net.SplitHostPort(dns.Listen) - if dnsPortStr == "0" || dnsPortStr == "" || err != nil { - return err + dnsPort, err := strconv.ParseUint(dnsPortStr, 10, 16) + if err != nil { + err = fmt.Errorf("DNS server must be correct") + return } - dnsPort, err := strconv.Atoi(dnsPortStr) - if err != nil { - return err + if iptables.InboundInterface != "" { + inboundInterface = iptables.InboundInterface } if dialer.DefaultRoutingMark.Load() == 0 { dialer.DefaultRoutingMark.Store(2158) } - if general.AutoIptables { - err = tproxy.SetTProxyLinuxIPTables(dialer.DefaultInterface.Load(), general.TProxyPort, dnsPort) + + err = tproxy.SetTProxyIPTables(inboundInterface, uint16(tProxyPort), uint16(dnsPort)) + if err != nil { + return } - return err + + log.Infoln("[IPTABLES] Setting iptables completed") } func Cleanup() { P.Cleanup() - if runtime.GOOS == "linux" { - tproxy.CleanUpTProxyLinuxIPTables() - } + tproxy.CleanupTProxyIPTables() } diff --git a/listener/listener.go b/listener/listener.go index aae8a83b..f4efd356 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -310,7 +310,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address()) } -func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateTun(tunConf *config.Tun, dnsCfg *config.DNS, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tunMux.Lock() defer tunMux.Unlock() @@ -330,7 +330,7 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- * if !tunConf.Enable { return } - tunStackListener, err = tun.New(tunConf, tcpIn, udpIn) + tunStackListener, err = tun.New(tunConf, dnsCfg, tcpIn, udpIn) if err != nil { log.Warnln("Failed to start TUN interface: %s", err.Error()) } diff --git a/listener/tproxy/tproxy_linux_iptables.go b/listener/tproxy/tproxy_linux_iptables.go index ae6ad6a8..fbd0d2d2 100644 --- a/listener/tproxy/tproxy_linux_iptables.go +++ b/listener/tproxy/tproxy_linux_iptables.go @@ -11,9 +11,9 @@ import ( ) var ( - dnsPort = 0 - tProxyPort = 0 - interfaceName = "" + dnsPort uint16 + tProxyPort uint16 + interfaceName string ) const ( @@ -21,7 +21,7 @@ const ( PROXY_ROUTE_TABLE = "0x2d0" ) -func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error { +func SetTProxyIPTables(ifname string, tport uint16, dport uint16) error { var err error if err = execCmd("iptables -V"); err != nil { return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS) @@ -40,11 +40,13 @@ func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error { execCmd(fmt.Sprintf("ip -f inet route add local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE)) // set FORWARD - execCmd("sysctl -w net.ipv4.ip_forward=1") - execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName)) - execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -j ACCEPT", interfaceName)) - execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName)) - execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName)) + if interfaceName != "lo" { + execCmd("sysctl -w net.ipv4.ip_forward=1") + execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -j ACCEPT", interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName)) + } // set clash divert execCmd("iptables -t mangle -N clash_divert") @@ -70,7 +72,9 @@ func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error { 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 - execCmd(fmt.Sprintf("iptables -t nat -A POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName)) + if interfaceName != "lo" { + execCmd(fmt.Sprintf("iptables -t nat -A POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName)) + } // set output execCmd("iptables -t mangle -N clash_output") @@ -95,16 +99,15 @@ func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error { execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j clash_dns_output") execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j clash_dns_output") - log.Infoln("[TPROXY] Setting iptables completed") return nil } -func CleanUpTProxyLinuxIPTables() { - if interfaceName == "" || tProxyPort == 0 || dnsPort == 0 { +func CleanupTProxyIPTables() { + if runtime.GOOS != "linux" || interfaceName == "" || tProxyPort == 0 || dnsPort == 0 { return } - log.Warnln("Clean up tproxy linux iptables") + log.Warnln("Cleanup tproxy linux iptables") if int(dialer.DefaultRoutingMark.Load()) == 2158 { dialer.DefaultRoutingMark.Store(0) @@ -119,10 +122,12 @@ func CleanUpTProxyLinuxIPTables() { execCmd(fmt.Sprintf("ip -f inet route del local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE)) // clean FORWARD - execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName)) - execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName)) - execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName)) - execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -j ACCEPT", interfaceName)) + if interfaceName != "lo" { + execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -j ACCEPT", interfaceName)) + } // 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)) @@ -130,7 +135,9 @@ func CleanUpTProxyLinuxIPTables() { execCmd("iptables -t mangle -D PREROUTING -j clash_prerouting") // clean POSTROUTING - execCmd(fmt.Sprintf("iptables -t nat -D POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName)) + if interfaceName != "lo" { + execCmd(fmt.Sprintf("iptables -t nat -D POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName)) + } // clean OUTPUT execCmd(fmt.Sprintf("iptables -t mangle -D OUTPUT -o %s -j clash_output", interfaceName)) @@ -146,6 +153,10 @@ func CleanUpTProxyLinuxIPTables() { execCmd("iptables -t mangle -X clash_output") execCmd("iptables -t nat -F clash_dns_output") execCmd("iptables -t nat -X clash_dns_output") + + interfaceName = "" + tProxyPort = 0 + dnsPort = 0 } func addLocalnetworkToChain(chain string) { @@ -167,11 +178,11 @@ func addLocalnetworkToChain(chain string) { } func execCmd(cmdStr string) error { - log.Debugln("[TPROXY] %s", cmdStr) + log.Debugln("[IPTABLES] %s", cmdStr) _, err := cmd.ExecCmd(cmdStr) if err != nil { - log.Warnln("[TPROXY] exec cmd: %v", err) + log.Warnln("[IPTABLES] exec cmd: %v", err) } return err diff --git a/listener/tun/device/tun/tun_wireguard.go b/listener/tun/device/tun/tun_wireguard.go index 1e1d5527..dabd1252 100644 --- a/listener/tun/device/tun/tun_wireguard.go +++ b/listener/tun/device/tun/tun_wireguard.go @@ -5,6 +5,7 @@ package tun import ( "fmt" "runtime" + "strings" "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/listener/tun/device" @@ -50,9 +51,18 @@ func Open(name string, mtu uint32) (_ device.Device, err error) { } nt, err := tun.CreateTUN(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way + + // retry if abnormal exit on Windows at last time + if err != nil && runtime.GOOS == "windows" && + strings.HasSuffix(err.Error(), "file already exists.") { + + nt, err = tun.CreateTUN(t.name, forcedMTU) + } + if err != nil { return nil, fmt.Errorf("create tun: %w", err) } + t.nt = nt.(*tun.NativeTun) tunMTU, err := nt.MTU() diff --git a/listener/tun/ipstack/commons/router_windows.go b/listener/tun/ipstack/commons/router_windows.go index 8c761c66..4599f999 100644 --- a/listener/tun/ipstack/commons/router_windows.go +++ b/listener/tun/ipstack/commons/router_windows.go @@ -39,9 +39,11 @@ startOver: tryTimes++ var ( - luid = winipcfg.LUID(dev.(*tun.TUN).LUID()) - ip = addr.Masked().Addr().Next() - addresses = []netip.Prefix{netip.PrefixFrom(ip, addr.Bits())} + luid = winipcfg.LUID(dev.(*tun.TUN).LUID()) + ip = addr.Masked().Addr().Next() + gw = ip.Next() + addresses = []netip.Prefix{netip.PrefixFrom(ip, addr.Bits())} + dnsAddress = []netip.Addr{gw} family4 = winipcfg.AddressFamily(windows.AF_INET) familyV6 = winipcfg.AddressFamily(windows.AF_INET6) @@ -123,7 +125,7 @@ startOver: // add gateway deduplicatedRoutes = append(deduplicatedRoutes, &winipcfg.RouteData{ Destination: addr.Masked(), - NextHop: addr.Masked().Addr().Next().Next(), + NextHop: gw, Metric: 0, }) @@ -193,12 +195,11 @@ startOver: return fmt.Errorf("unable to set v6 metric and MTU: %w", err) } - dnsAdds := []netip.Addr{netip.MustParseAddr("198.18.0.2")} - err = luid.SetDNS(family4, dnsAdds, nil) + err = luid.SetDNS(family4, dnsAddress, nil) if err == windows.ERROR_NOT_FOUND && retryOnFailure { goto startOver } else if err != nil { - return fmt.Errorf("unable to set DNS %s %s: %w", "198.18.0.2", "nil", err) + return fmt.Errorf("unable to set DNS %s %s: %w", dnsAddress[0].String(), "nil", err) } wintunInterfaceName = dev.Name() diff --git a/listener/tun/tun_adapter.go b/listener/tun/tun_adapter.go index 40cf5909..1e1f6ad4 100644 --- a/listener/tun/tun_adapter.go +++ b/listener/tun/tun_adapter.go @@ -2,12 +2,6 @@ package tun import ( "fmt" - "net/netip" - "net/url" - "runtime" - "strings" - "time" - "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/cmd" "github.com/Dreamacro/clash/component/process" @@ -21,12 +15,21 @@ import ( "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor" "github.com/Dreamacro/clash/listener/tun/ipstack/system" "github.com/Dreamacro/clash/log" + "net/netip" + "net/url" + "runtime" + "strings" ) // New TunAdapter -func New(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) { +func New(tunConf *config.Tun, dnsConf *config.DNS, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) { + var tunAddressPrefix string + if dnsConf.FakeIPRange != nil { + tunAddressPrefix = dnsConf.FakeIPRange.IPNet().String() + } + var ( - tunAddress, _ = netip.ParsePrefix("198.18.0.1/16") + tunAddress, _ = netip.ParsePrefix(tunAddressPrefix) devName = tunConf.Device stackType = tunConf.Stack autoRoute = tunConf.AutoRoute @@ -42,21 +45,19 @@ func New(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound. devName = generateDeviceName() } - //if !tunAddress.IsValid() || !tunAddress.Addr().Is4() { - // tunAddress = netip.MustParsePrefix("198.18.0.1/16") - //} + if !tunAddress.IsValid() || !tunAddress.Addr().Is4() { + tunAddress = netip.MustParsePrefix("198.18.0.1/16") + } process.AppendLocalIPs(tunAddress.Masked().Addr().Next().AsSlice()) // open tun device - for i := 1; i < 3; i++ { - time.Sleep(time.Second * 1) - tunDevice, err = parseDevice(devName, uint32(mtu)) - if err != nil { - return nil, fmt.Errorf("can't open tun: %w", err) - } - break + + tunDevice, err = parseDevice(devName, uint32(mtu)) + if err != nil { + return nil, fmt.Errorf("can't open tun: %w", err) } + // new ip stack switch stackType { case C.TunGvisor: diff --git a/main.go b/main.go index af71e413..ed9d76c3 100644 --- a/main.go +++ b/main.go @@ -48,7 +48,7 @@ func init() { } func main() { - maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) + _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) if version { fmt.Printf("Clash Meta %s %s %s with %s %s\n", C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime) return