//go:build linux package tun import ( "os/exec" "strings" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" ) func (r *autoRedirect) setupIPTables() error { if r.enableIPv4 { err := r.setupIPTablesForFamily(r.iptablesPath) if err != nil { return E.Cause(err, "setup iptables") } } if r.enableIPv6 { err := r.setupIPTablesForFamily(r.ip6tablesPath) if err != nil { return E.Cause(err, "setup ip6tables") } } return nil } func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error { tableNameOutput := r.tableName + "-output" redirectPort := r.redirectPort() // OUTPUT err := r.runShell(iptablesPath, "-t nat -N", tableNameOutput) if err != nil { return err } err = r.runShell(iptablesPath, "-t nat -A", tableNameOutput, "-p tcp -o", r.tunOptions.Name, "-j REDIRECT --to-ports", redirectPort) if err != nil { return err } err = r.runShell(iptablesPath, "-t nat -I OUTPUT -j", tableNameOutput) if err != nil { return err } if r.redirectServer != nil { tableNameInput := r.tableName + "-input" err = r.runShell(iptablesPath, "-t filter -N", tableNameInput) if err != nil { return err } err = r.runShell(iptablesPath, "-t filter -A", tableNameInput, "-p tcp --dport", redirectPort, "-m conntrack ! --ctstate DNAT", "-j REJECT --reject-with tcp-reset") if err != nil { return err } err = r.runShell(iptablesPath, "-t filter -I INPUT -j", tableNameInput) if err != nil { return err } } return nil } func (r *autoRedirect) cleanupIPTables() { if r.enableIPv4 { r.cleanupIPTablesForFamily(r.iptablesPath) } if r.enableIPv6 { r.cleanupIPTablesForFamily(r.ip6tablesPath) } } func (r *autoRedirect) cleanupIPTablesForFamily(iptablesPath string) { tableNameOutput := r.tableName + "-output" tableNameInput := r.tableName + "-input" _ = r.runShell(iptablesPath, "-t nat -D OUTPUT -j", tableNameOutput) _ = r.runShell(iptablesPath, "-t nat -F", tableNameOutput) _ = r.runShell(iptablesPath, "-t nat -X", tableNameOutput) _ = r.runShell(iptablesPath, "-t filter -D INPUT -j", tableNameInput) _ = r.runShell(iptablesPath, "-t filter -F", tableNameInput) _ = r.runShell(iptablesPath, "-t filter -X", tableNameInput) } func (r *autoRedirect) runShell(commands ...any) error { commandStr := strings.Join(F.MapToString(commands), " ") var command *exec.Cmd if r.androidSu { command = exec.Command(r.suPath, "-c", commandStr) } else { commandArray := strings.Split(commandStr, " ") command = exec.Command(commandArray[0], commandArray[1:]...) } combinedOutput, err := command.CombinedOutput() if err != nil { return E.Extend(err, F.ToString(commandStr, ": ", string(combinedOutput))) } return nil }