Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef19b7b22d | ||
|
|
c410f7050c | ||
|
|
d89ab3f207 | ||
|
|
219c612399 |
@@ -3,12 +3,9 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
@@ -30,10 +27,7 @@ func (r *autoRedirect) setupIPTables() error {
|
||||
}
|
||||
|
||||
func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error {
|
||||
tableNameInput := r.tableName + "-input"
|
||||
tableNameForward := r.tableName + "-forward"
|
||||
tableNameOutput := r.tableName + "-output"
|
||||
tableNamePreRouteing := r.tableName + "-prerouting"
|
||||
redirectPort := r.redirectPort()
|
||||
// OUTPUT
|
||||
err := r.runShell(iptablesPath, "-t nat -N", tableNameOutput)
|
||||
@@ -50,184 +44,6 @@ func (r *autoRedirect) setupIPTablesForFamily(iptablesPath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.GOOS == "android" {
|
||||
return nil
|
||||
}
|
||||
// INPUT
|
||||
err = r.runShell(iptablesPath, "-N", tableNameInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-A", tableNameInput,
|
||||
"-i", r.tunOptions.Name, "-j", "ACCEPT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-A", tableNameInput,
|
||||
"-o", r.tunOptions.Name, "-j", "ACCEPT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-I INPUT -j", tableNameInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// FORWARD
|
||||
err = r.runShell(iptablesPath, "-N", tableNameForward)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-A", tableNameForward,
|
||||
"-i", r.tunOptions.Name, "-j", "ACCEPT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-A", tableNameForward,
|
||||
"-o", r.tunOptions.Name, "-j", "ACCEPT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-I FORWARD -j", tableNameForward)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// PREROUTING
|
||||
err = r.runShell(iptablesPath, "-t nat -N", tableNamePreRouteing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
routeAddress []netip.Prefix
|
||||
routeExcludeAddress []netip.Prefix
|
||||
)
|
||||
if iptablesPath == r.iptablesPath {
|
||||
routeAddress = r.tunOptions.Inet4RouteAddress
|
||||
routeExcludeAddress = r.tunOptions.Inet4RouteExcludeAddress
|
||||
} else {
|
||||
routeAddress = r.tunOptions.Inet6RouteAddress
|
||||
routeExcludeAddress = r.tunOptions.Inet6RouteExcludeAddress
|
||||
}
|
||||
if len(routeAddress) > 0 && (len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0) {
|
||||
return E.New("`*_route_address` is conflict with `include_interface` or `include_uid`")
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-i", r.tunOptions.Name, "-j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, address := range routeExcludeAddress {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-d", address.String(), "-j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, name := range r.tunOptions.ExcludeInterface {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-i", name, "-j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, uid := range r.tunOptions.ExcludeUID {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-m owner --uid-owner", uid, "-j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !r.tunOptions.EXP_DisableDNSHijack {
|
||||
dnsServer := common.Find(r.tunOptions.DNSServers, func(it netip.Addr) bool {
|
||||
return it.Is4() == (iptablesPath == r.iptablesPath)
|
||||
})
|
||||
if !dnsServer.IsValid() {
|
||||
if iptablesPath == r.iptablesPath {
|
||||
if HasNextAddress(r.tunOptions.Inet4Address[0], 1) {
|
||||
dnsServer = r.tunOptions.Inet4Address[0].Addr().Next()
|
||||
}
|
||||
} else {
|
||||
if HasNextAddress(r.tunOptions.Inet6Address[0], 1) {
|
||||
dnsServer = r.tunOptions.Inet6Address[0].Addr().Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
if dnsServer.IsValid() {
|
||||
if len(routeAddress) > 0 {
|
||||
for _, address := range routeAddress {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-d", address.String(), "-p udp --dport 53 -j DNAT --to", dnsServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
|
||||
for _, name := range r.tunOptions.IncludeInterface {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-i", name, "-p udp --dport 53 -j DNAT --to", dnsServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, uidRange := range r.tunOptions.IncludeUID {
|
||||
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-m owner --uid-owner", uid, "-p udp --dport 53 -j DNAT --to", dnsServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-p udp --dport 53 -j DNAT --to", dnsServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing, "-m addrtype --dst-type LOCAL -j RETURN")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(routeAddress) > 0 {
|
||||
for _, address := range routeAddress {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-d", address.String(), "-p tcp -j REDIRECT --to-ports", redirectPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
|
||||
for _, name := range r.tunOptions.IncludeInterface {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-i", name, "-p tcp -j REDIRECT --to-ports", redirectPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, uidRange := range r.tunOptions.IncludeUID {
|
||||
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-m owner --uid-owner", uid, "-p tcp -j REDIRECT --to-ports", redirectPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
|
||||
"-p tcp -j REDIRECT --to-ports", redirectPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = r.runShell(iptablesPath, "-t nat -I PREROUTING -j", tableNamePreRouteing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -241,29 +57,11 @@ func (r *autoRedirect) cleanupIPTables() {
|
||||
}
|
||||
|
||||
func (r *autoRedirect) cleanupIPTablesForFamily(iptablesPath string) {
|
||||
tableNameInput := r.tableName + "-input"
|
||||
tableNameOutput := r.tableName + "-output"
|
||||
tableNameForward := r.tableName + "-forward"
|
||||
tableNamePreRouteing := r.tableName + "-prerouting"
|
||||
|
||||
_ = r.runShell(iptablesPath, "-t nat -D OUTPUT -j", tableNameOutput)
|
||||
_ = r.runShell(iptablesPath, "-t nat -F", tableNameOutput)
|
||||
_ = r.runShell(iptablesPath, "-t nat -X", tableNameOutput)
|
||||
if runtime.GOOS == "android" {
|
||||
return
|
||||
}
|
||||
|
||||
_ = r.runShell(iptablesPath, "-D INPUT -j", tableNameInput)
|
||||
_ = r.runShell(iptablesPath, "-F", tableNameInput)
|
||||
_ = r.runShell(iptablesPath, "-X", tableNameInput)
|
||||
|
||||
_ = r.runShell(iptablesPath, "-D FORWARD -j", tableNameForward)
|
||||
_ = r.runShell(iptablesPath, "-F", tableNameForward)
|
||||
_ = r.runShell(iptablesPath, "-X", tableNameForward)
|
||||
|
||||
_ = r.runShell(iptablesPath, "-t nat -D PREROUTING -j", tableNamePreRouteing)
|
||||
_ = r.runShell(iptablesPath, "-t nat -F", tableNamePreRouteing)
|
||||
_ = r.runShell(iptablesPath, "-t nat -X", tableNamePreRouteing)
|
||||
}
|
||||
|
||||
func (r *autoRedirect) runShell(commands ...any) error {
|
||||
|
||||
@@ -83,9 +83,8 @@ func (r *autoRedirect) Start() error {
|
||||
} else {
|
||||
if r.useNFTables {
|
||||
err = r.initializeNFTables()
|
||||
if err != nil && err != os.ErrInvalid {
|
||||
r.useNFTables = false
|
||||
r.logger.Debug("missing nftables support: ", err)
|
||||
if err != nil {
|
||||
return E.Cause(err, "missing nftables support")
|
||||
}
|
||||
}
|
||||
if len(r.tunOptions.Inet4Address) > 0 {
|
||||
|
||||
@@ -113,12 +113,6 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
Priority: nftables.ChainPriorityRef(*nftables.ChainPriorityNATDest + 2),
|
||||
Type: nftables.ChainTypeFilter,
|
||||
})
|
||||
if r.enableIPv4 {
|
||||
nftablesCreateExcludeDestinationIPSet(nft, table, chainPreRoutingUDP, 5, "inet4_local_address_set", nftables.TableFamilyIPv4, false)
|
||||
}
|
||||
if r.enableIPv6 {
|
||||
nftablesCreateExcludeDestinationIPSet(nft, table, chainPreRoutingUDP, 6, "inet6_local_address_set", nftables.TableFamilyIPv6, false)
|
||||
}
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chainPreRoutingUDP,
|
||||
@@ -128,10 +122,28 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: []byte{unix.IPPROTO_UDP},
|
||||
},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chainPreRoutingUDP,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyIIFNAME,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: nftablesIfname(r.tunOptions.Name),
|
||||
},
|
||||
&expr.Ct{
|
||||
Key: expr.CtKeyMARK,
|
||||
Register: 1,
|
||||
@@ -149,6 +161,40 @@ func (r *autoRedirect) setupNFTables() error {
|
||||
&expr.Counter{},
|
||||
},
|
||||
})
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chainPreRoutingUDP,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Ct{
|
||||
Key: expr.CtKeyMARK,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpNeq,
|
||||
Register: 1,
|
||||
Data: binaryutil.NativeEndian.PutUint32(r.tunOptions.AutoRedirectInputMark),
|
||||
},
|
||||
&expr.Immediate{
|
||||
Register: 1,
|
||||
Data: binaryutil.NativeEndian.PutUint32(r.tunOptions.AutoRedirectOutputMark),
|
||||
},
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyMARK,
|
||||
Register: 1,
|
||||
SourceRegister: true,
|
||||
},
|
||||
&expr.Meta{
|
||||
Key: expr.MetaKeyMARK,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Ct{
|
||||
Key: expr.CtKeyMARK,
|
||||
Register: 1,
|
||||
SourceRegister: true,
|
||||
},
|
||||
&expr.Counter{},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
err = r.configureOpenWRTFirewall4(nft, false)
|
||||
|
||||
@@ -138,6 +138,27 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
|
||||
},
|
||||
},
|
||||
})
|
||||
if chain.Type == nftables.ChainTypeRoute {
|
||||
nft.AddRule(&nftables.Rule{
|
||||
Table: table,
|
||||
Chain: chain,
|
||||
Exprs: []expr.Any{
|
||||
&expr.Ct{
|
||||
Key: expr.CtKeyMARK,
|
||||
Register: 1,
|
||||
},
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: binaryutil.NativeEndian.PutUint32(r.tunOptions.AutoRedirectOutputMark),
|
||||
},
|
||||
&expr.Counter{},
|
||||
&expr.Verdict{
|
||||
Kind: expr.VerdictReturn,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if chain.Hooknum == nftables.ChainHookPrerouting {
|
||||
if len(r.tunOptions.IncludeInterface) > 0 {
|
||||
|
||||
@@ -107,10 +107,7 @@ func (s *System) Start() error {
|
||||
}
|
||||
|
||||
func (s *System) start() error {
|
||||
err := fixWindowsFirewall()
|
||||
if err != nil {
|
||||
return E.Cause(err, "fix windows firewall for system stack")
|
||||
}
|
||||
_ = fixWindowsFirewall()
|
||||
var listener net.ListenConfig
|
||||
if s.bindInterface {
|
||||
listener.Control = control.Append(listener.Control, func(network, address string, conn syscall.RawConn) error {
|
||||
@@ -122,6 +119,7 @@ func (s *System) start() error {
|
||||
})
|
||||
}
|
||||
var tcpListener net.Listener
|
||||
var err error
|
||||
if s.inet4Address.IsValid() {
|
||||
for i := 0; i < 3; i++ {
|
||||
tcpListener, err = listener.Listen(s.ctx, "tcp4", net.JoinHostPort(s.inet4ServerAddress.String(), "0"))
|
||||
|
||||
@@ -22,6 +22,7 @@ func fixWindowsFirewall() error {
|
||||
Protocol: winfw.NET_FW_IP_PROTOCOL_TCP,
|
||||
Direction: winfw.NET_FW_RULE_DIR_IN,
|
||||
Action: winfw.NET_FW_ACTION_ALLOW,
|
||||
Profiles: winfw.NET_FW_PROFILE2_ALL,
|
||||
}
|
||||
_, err = winfw.FirewallRuleAddAdvanced(rule)
|
||||
return err
|
||||
|
||||
19
tun_linux.go
19
tun_linux.go
@@ -668,7 +668,7 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
}
|
||||
}
|
||||
if len(t.options.IncludeInterface) > 0 {
|
||||
matchPriority := priority + 2*len(t.options.IncludeInterface) + 1
|
||||
matchPriority := priority + 2
|
||||
for _, includeInterface := range t.options.IncludeInterface {
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
@@ -677,7 +677,6 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Goto = matchPriority
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
@@ -686,9 +685,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Goto = matchPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
if p4 {
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
priority6++
|
||||
}
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
@@ -726,7 +730,6 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
@@ -735,9 +738,15 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
|
||||
if p4 {
|
||||
priority++
|
||||
}
|
||||
if p6 {
|
||||
priority6++
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "android" && t.options.InterfaceMonitor.AndroidVPNEnabled() {
|
||||
|
||||
Reference in New Issue
Block a user