github.com/metacubex/mihomo@v1.18.5/listener/tproxy/tproxy_iptables.go (about)

     1  package tproxy
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"runtime"
     8  
     9  	"github.com/metacubex/mihomo/common/cmd"
    10  	"github.com/metacubex/mihomo/component/dialer"
    11  	"github.com/metacubex/mihomo/log"
    12  )
    13  
    14  var (
    15  	dnsPort       uint16
    16  	tProxyPort    uint16
    17  	interfaceName string
    18  	DnsRedirect   bool
    19  )
    20  
    21  const (
    22  	PROXY_FWMARK      = "0x2d0"
    23  	PROXY_ROUTE_TABLE = "0x2d0"
    24  )
    25  
    26  func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dnsredir bool, dport uint16) error {
    27  	if _, err := cmd.ExecCmd("iptables -V"); err != nil {
    28  		return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS)
    29  	}
    30  
    31  	if ifname == "" {
    32  		return errors.New("the 'interface-name' can not be empty")
    33  	}
    34  
    35  	interfaceName = ifname
    36  	tProxyPort = tport
    37  	DnsRedirect = dnsredir
    38  	dnsPort = dport
    39  
    40  	// add route
    41  	execCmd(fmt.Sprintf("ip -f inet rule add fwmark %s lookup %s", PROXY_FWMARK, PROXY_ROUTE_TABLE))
    42  	execCmd(fmt.Sprintf("ip -f inet route add local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE))
    43  
    44  	// set FORWARD
    45  	if interfaceName != "lo" {
    46  		execCmd("sysctl -w net.ipv4.ip_forward=1")
    47  		execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName))
    48  		execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -j ACCEPT", interfaceName))
    49  		execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName))
    50  		execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName))
    51  	}
    52  
    53  	// set mihomo divert
    54  	execCmd("iptables -t mangle -N mihomo_divert")
    55  	execCmd("iptables -t mangle -F mihomo_divert")
    56  	execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_divert -j MARK --set-mark %s", PROXY_FWMARK))
    57  	execCmd("iptables -t mangle -A mihomo_divert -j ACCEPT")
    58  
    59  	// set pre routing
    60  	execCmd("iptables -t mangle -N mihomo_prerouting")
    61  	execCmd("iptables -t mangle -F mihomo_prerouting")
    62  	execCmd("iptables -t mangle -A mihomo_prerouting -s 172.17.0.0/16 -j RETURN")
    63  	if DnsRedirect {
    64  		execCmd("iptables -t mangle -A mihomo_prerouting -p udp --dport 53 -j ACCEPT")
    65  		execCmd("iptables -t mangle -A mihomo_prerouting -p tcp --dport 53 -j ACCEPT")
    66  	}
    67  	execCmd("iptables -t mangle -A mihomo_prerouting -m addrtype --dst-type LOCAL -j RETURN")
    68  	addLocalnetworkToChain("mihomo_prerouting", bypass)
    69  	execCmd("iptables -t mangle -A mihomo_prerouting -p tcp -m socket -j mihomo_divert")
    70  	execCmd("iptables -t mangle -A mihomo_prerouting -p udp -m socket -j mihomo_divert")
    71  	execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_prerouting -p tcp -j TPROXY --on-port %d --tproxy-mark %s/%s", tProxyPort, PROXY_FWMARK, PROXY_FWMARK))
    72  	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))
    73  	execCmd("iptables -t mangle -A PREROUTING -j mihomo_prerouting")
    74  
    75  	if DnsRedirect {
    76  		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))
    77  		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))
    78  	}
    79  
    80  	// set post routing
    81  	if interfaceName != "lo" {
    82  		execCmd(fmt.Sprintf("iptables -t nat -A POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName))
    83  	}
    84  
    85  	// set output
    86  	execCmd("iptables -t mangle -N mihomo_output")
    87  	execCmd("iptables -t mangle -F mihomo_output")
    88  	execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_output -m mark --mark %#x -j RETURN", dialer.DefaultRoutingMark.Load()))
    89  	if DnsRedirect {
    90  		execCmd("iptables -t mangle -A mihomo_output -p udp -m multiport --dports 53,123,137 -j ACCEPT")
    91  		execCmd("iptables -t mangle -A mihomo_output -p tcp --dport 53 -j ACCEPT")
    92  	}
    93  	execCmd("iptables -t mangle -A mihomo_output -m addrtype --dst-type LOCAL -j RETURN")
    94  	execCmd("iptables -t mangle -A mihomo_output -m addrtype --dst-type BROADCAST -j RETURN")
    95  	addLocalnetworkToChain("mihomo_output", bypass)
    96  	execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_output -p tcp -j MARK --set-mark %s", PROXY_FWMARK))
    97  	execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_output -p udp -j MARK --set-mark %s", PROXY_FWMARK))
    98  	execCmd(fmt.Sprintf("iptables -t mangle -I OUTPUT -o %s -j mihomo_output", interfaceName))
    99  
   100  	// set dns output
   101  	if DnsRedirect {
   102  		execCmd("iptables -t nat -N mihomo_dns_output")
   103  		execCmd("iptables -t nat -F mihomo_dns_output")
   104  		execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -m mark --mark %#x -j RETURN", dialer.DefaultRoutingMark.Load()))
   105  		execCmd("iptables -t nat -A mihomo_dns_output -s 172.17.0.0/16 -j RETURN")
   106  		execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p udp -j REDIRECT --to-ports %d", dnsPort))
   107  		execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p tcp -j REDIRECT --to-ports %d", dnsPort))
   108  		execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j mihomo_dns_output")
   109  		execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j mihomo_dns_output")
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func CleanupTProxyIPTables() {
   116  	if runtime.GOOS != "linux" || interfaceName == "" || tProxyPort == 0 {
   117  		return
   118  	}
   119  
   120  	log.Warnln("Cleanup tproxy linux iptables")
   121  
   122  	if int(dialer.DefaultRoutingMark.Load()) == 2158 {
   123  		dialer.DefaultRoutingMark.Store(0)
   124  	}
   125  
   126  	if _, err := cmd.ExecCmd("iptables -t mangle -L mihomo_divert"); err != nil {
   127  		return
   128  	}
   129  
   130  	// clean route
   131  	execCmd(fmt.Sprintf("ip -f inet rule del fwmark %s lookup %s", PROXY_FWMARK, PROXY_ROUTE_TABLE))
   132  	execCmd(fmt.Sprintf("ip -f inet route del local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE))
   133  
   134  	// clean FORWARD
   135  	if interfaceName != "lo" {
   136  		execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName))
   137  		execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName))
   138  		execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName))
   139  		execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -j ACCEPT", interfaceName))
   140  	}
   141  
   142  	// clean PREROUTING
   143  	if DnsRedirect {
   144  		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))
   145  		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))
   146  	}
   147  	execCmd("iptables -t mangle -D PREROUTING -j mihomo_prerouting")
   148  
   149  	// clean POSTROUTING
   150  	if interfaceName != "lo" {
   151  		execCmd(fmt.Sprintf("iptables -t nat -D POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName))
   152  	}
   153  
   154  	// clean OUTPUT
   155  	execCmd(fmt.Sprintf("iptables -t mangle -D OUTPUT -o %s -j mihomo_output", interfaceName))
   156  	if DnsRedirect {
   157  		execCmd("iptables -t nat -D OUTPUT -p tcp --dport 53 -j mihomo_dns_output")
   158  		execCmd("iptables -t nat -D OUTPUT -p udp --dport 53 -j mihomo_dns_output")
   159  	}
   160  
   161  	// clean chain
   162  	execCmd("iptables -t mangle -F mihomo_prerouting")
   163  	execCmd("iptables -t mangle -X mihomo_prerouting")
   164  	execCmd("iptables -t mangle -F mihomo_divert")
   165  	execCmd("iptables -t mangle -X mihomo_divert")
   166  	execCmd("iptables -t mangle -F mihomo_output")
   167  	execCmd("iptables -t mangle -X mihomo_output")
   168  	if DnsRedirect {
   169  		execCmd("iptables -t nat -F mihomo_dns_output")
   170  		execCmd("iptables -t nat -X mihomo_dns_output")
   171  	}
   172  	interfaceName = ""
   173  	tProxyPort = 0
   174  	dnsPort = 0
   175  }
   176  
   177  func addLocalnetworkToChain(chain string, bypass []string) {
   178  	for _, bp := range bypass {
   179  		_, _, err := net.ParseCIDR(bp)
   180  		if err != nil {
   181  			log.Warnln("[IPTABLES] %s", err)
   182  			continue
   183  		}
   184  		execCmd(fmt.Sprintf("iptables -t mangle -A %s -d %s -j RETURN", chain, bp))
   185  	}
   186  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 0.0.0.0/8 -j RETURN", chain))
   187  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 10.0.0.0/8 -j RETURN", chain))
   188  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 100.64.0.0/10 -j RETURN", chain))
   189  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 127.0.0.0/8 -j RETURN", chain))
   190  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 169.254.0.0/16 -j RETURN", chain))
   191  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 172.16.0.0/12 -j RETURN", chain))
   192  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.0.0.0/24 -j RETURN", chain))
   193  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.0.2.0/24 -j RETURN", chain))
   194  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.88.99.0/24 -j RETURN", chain))
   195  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.168.0.0/16 -j RETURN", chain))
   196  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 198.51.100.0/24 -j RETURN", chain))
   197  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 203.0.113.0/24 -j RETURN", chain))
   198  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 224.0.0.0/4 -j RETURN", chain))
   199  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 240.0.0.0/4 -j RETURN", chain))
   200  	execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 255.255.255.255/32 -j RETURN", chain))
   201  }
   202  
   203  func execCmd(cmdStr string) {
   204  	log.Debugln("[IPTABLES] %s", cmdStr)
   205  
   206  	_, err := cmd.ExecCmd(cmdStr)
   207  	if err != nil {
   208  		log.Warnln("[IPTABLES] exec cmd: %v", err)
   209  	}
   210  }