mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Migrate auto-redirect to library
This commit is contained in:
parent
888487021d
commit
d528cfcb5c
8
go.mod
8
go.mod
@ -24,17 +24,16 @@ require (
|
|||||||
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
|
||||||
github.com/sagernet/gomobile v0.1.3
|
github.com/sagernet/gomobile v0.1.3
|
||||||
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
|
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
|
||||||
github.com/sagernet/nftables v0.3.0-beta.2
|
|
||||||
github.com/sagernet/quic-go v0.43.1-beta.1
|
github.com/sagernet/quic-go v0.43.1-beta.1
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.5.0-alpha.7
|
github.com/sagernet/sing v0.5.0-alpha.8
|
||||||
github.com/sagernet/sing-dns v0.2.0-beta.18
|
github.com/sagernet/sing-dns v0.2.0-beta.18
|
||||||
github.com/sagernet/sing-mux v0.2.0
|
github.com/sagernet/sing-mux v0.2.0
|
||||||
github.com/sagernet/sing-quic v0.2.0-beta.5
|
github.com/sagernet/sing-quic v0.2.0-beta.5
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6
|
github.com/sagernet/sing-shadowsocks v0.2.6
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4
|
github.com/sagernet/sing-shadowtls v0.1.4
|
||||||
github.com/sagernet/sing-tun v0.4.0-beta.4
|
github.com/sagernet/sing-tun v0.4.0-beta.6
|
||||||
github.com/sagernet/sing-vmess v0.1.8
|
github.com/sagernet/sing-vmess v0.1.8
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||||
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
|
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
|
||||||
@ -46,7 +45,6 @@ require (
|
|||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.23.0
|
golang.org/x/crypto v0.23.0
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
|
||||||
golang.org/x/net v0.25.0
|
golang.org/x/net v0.25.0
|
||||||
golang.org/x/sys v0.20.0
|
golang.org/x/sys v0.20.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
@ -83,11 +81,13 @@ require (
|
|||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
|
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
|
||||||
|
github.com/sagernet/nftables v0.3.0-beta.2 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||||
golang.org/x/mod v0.17.0 // indirect
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/text v0.15.0 // indirect
|
||||||
|
8
go.sum
8
go.sum
@ -108,8 +108,8 @@ github.com/sagernet/quic-go v0.43.1-beta.1/go.mod h1:BkrQYeop7Jx3hN3TW8/76CXcdhY
|
|||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.5.0-alpha.7 h1:yxjiH0vQAotu87JNJ9B0BGO0OJqsFjug84xcYdwyDm4=
|
github.com/sagernet/sing v0.5.0-alpha.8 h1:2KtzBvKP6hwknsi/G6H4vRgR4it31HQ6quLb0Woze7c=
|
||||||
github.com/sagernet/sing v0.5.0-alpha.7/go.mod h1:Xh4KO9nGdvm4K/LVg9Xn9jSxJdqe9KcXbAzNC1S2qfw=
|
github.com/sagernet/sing v0.5.0-alpha.8/go.mod h1:Xh4KO9nGdvm4K/LVg9Xn9jSxJdqe9KcXbAzNC1S2qfw=
|
||||||
github.com/sagernet/sing-dns v0.2.0-beta.18 h1:6vzXZThRdA7YUzBOpSbUT48XRumtl/KIpIHFSOP0za8=
|
github.com/sagernet/sing-dns v0.2.0-beta.18 h1:6vzXZThRdA7YUzBOpSbUT48XRumtl/KIpIHFSOP0za8=
|
||||||
github.com/sagernet/sing-dns v0.2.0-beta.18/go.mod h1:k/dmFcQpg6+m08gC1yQBy+13+QkuLqpKr4bIreq4U24=
|
github.com/sagernet/sing-dns v0.2.0-beta.18/go.mod h1:k/dmFcQpg6+m08gC1yQBy+13+QkuLqpKr4bIreq4U24=
|
||||||
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
|
||||||
@ -122,8 +122,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
|
|||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||||
github.com/sagernet/sing-tun v0.4.0-beta.4 h1:94OB9fQupn0qBPvlDzKRAq1lSIPHAvVBaw69/jEZfgQ=
|
github.com/sagernet/sing-tun v0.4.0-beta.6 h1:Ybp1Yr2iNuh9iI/+f8L6R5MkhMJEMNtRHNBdmvVq9P0=
|
||||||
github.com/sagernet/sing-tun v0.4.0-beta.4/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
|
github.com/sagernet/sing-tun v0.4.0-beta.6/go.mod h1:X9taVYeJXEtK9v6BpS1V/gUXTwY4nQKH4iYDLX1JUXY=
|
||||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||||
|
@ -3,6 +3,7 @@ package inbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -37,7 +38,7 @@ type Tun struct {
|
|||||||
tunStack tun.Stack
|
tunStack tun.Stack
|
||||||
platformInterface platform.Interface
|
platformInterface platform.Interface
|
||||||
platformOptions option.TunPlatformOptions
|
platformOptions option.TunPlatformOptions
|
||||||
autoRedirect *tunAutoRedirect
|
autoRedirect tun.AutoRedirect
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
|
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
|
||||||
@ -105,7 +106,15 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
|||||||
if !options.AutoRoute {
|
if !options.AutoRoute {
|
||||||
return nil, E.New("`auto_route` is required by `auto_redirect`")
|
return nil, E.New("`auto_route` is required by `auto_redirect`")
|
||||||
}
|
}
|
||||||
inbound.autoRedirect, err = newAutoRedirect(inbound)
|
disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES"))
|
||||||
|
inbound.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{
|
||||||
|
TunOptions: &inbound.tunOptions,
|
||||||
|
Context: ctx,
|
||||||
|
Handler: inbound,
|
||||||
|
Logger: logger,
|
||||||
|
TableName: "sing-box",
|
||||||
|
DisableNFTables: dErr == nil && disableNFTables,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize auto redirect")
|
return nil, E.Cause(err, "initialize auto redirect")
|
||||||
}
|
}
|
||||||
@ -222,7 +231,7 @@ func (t *Tun) Close() error {
|
|||||||
return common.Close(
|
return common.Close(
|
||||||
t.tunStack,
|
t.tunStack,
|
||||||
t.tunIf,
|
t.tunIf,
|
||||||
common.PtrOrNil(t.autoRedirect),
|
t.autoRedirect,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +243,11 @@ func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, upstreamMetadata
|
|||||||
metadata.Source = upstreamMetadata.Source
|
metadata.Source = upstreamMetadata.Source
|
||||||
metadata.Destination = upstreamMetadata.Destination
|
metadata.Destination = upstreamMetadata.Destination
|
||||||
metadata.InboundOptions = t.inboundOptions
|
metadata.InboundOptions = t.inboundOptions
|
||||||
|
if upstreamMetadata.Protocol != "" {
|
||||||
|
t.logger.InfoContext(ctx, "inbound ", upstreamMetadata.Protocol, " connection from ", metadata.Source)
|
||||||
|
} else {
|
||||||
t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||||||
|
}
|
||||||
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
err := t.router.RouteConnection(ctx, conn, metadata)
|
err := t.router.RouteConnection(ctx, conn, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,200 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package inbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/sagernet/nftables"
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/common/redir"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
"github.com/sagernet/sing-tun"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tunAutoRedirect struct {
|
|
||||||
myInboundAdapter
|
|
||||||
tunOptions *tun.Options
|
|
||||||
enableIPv4 bool
|
|
||||||
enableIPv6 bool
|
|
||||||
iptablesPath string
|
|
||||||
ip6tablesPath string
|
|
||||||
useNfTables bool
|
|
||||||
androidSu bool
|
|
||||||
suPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAutoRedirect(t *Tun) (*tunAutoRedirect, error) {
|
|
||||||
s := &tunAutoRedirect{
|
|
||||||
myInboundAdapter: myInboundAdapter{
|
|
||||||
protocol: C.TypeRedirect,
|
|
||||||
network: []string{N.NetworkTCP},
|
|
||||||
ctx: t.ctx,
|
|
||||||
router: t.router,
|
|
||||||
logger: t.logger,
|
|
||||||
tag: t.tag,
|
|
||||||
listenOptions: option.ListenOptions{
|
|
||||||
InboundOptions: t.inboundOptions,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tunOptions: &t.tunOptions,
|
|
||||||
}
|
|
||||||
s.connHandler = s
|
|
||||||
|
|
||||||
if C.IsAndroid {
|
|
||||||
s.enableIPv4 = true
|
|
||||||
s.iptablesPath = "/system/bin/iptables"
|
|
||||||
userId := os.Getuid()
|
|
||||||
if userId != 0 {
|
|
||||||
var (
|
|
||||||
suPath string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
for _, suPath = range []string{
|
|
||||||
"su",
|
|
||||||
"/system/bin/su",
|
|
||||||
} {
|
|
||||||
suPath, err = exec.LookPath(suPath)
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Extend(E.Cause(err, "root permission is required for auto redirect"), os.Getenv("PATH"))
|
|
||||||
}
|
|
||||||
s.androidSu = true
|
|
||||||
s.suPath = suPath
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := s.initializeNfTables()
|
|
||||||
if err != nil && err != os.ErrInvalid {
|
|
||||||
t.logger.Debug("device has no nftables support: ", err)
|
|
||||||
}
|
|
||||||
if len(t.tunOptions.Inet4Address) > 0 {
|
|
||||||
s.enableIPv4 = true
|
|
||||||
if !s.useNfTables {
|
|
||||||
s.iptablesPath, err = exec.LookPath("iptables")
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "iptables is required")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(t.tunOptions.Inet6Address) > 0 {
|
|
||||||
s.enableIPv6 = true
|
|
||||||
if !s.useNfTables {
|
|
||||||
s.ip6tablesPath, err = exec.LookPath("ip6tables")
|
|
||||||
if err != nil {
|
|
||||||
if !s.enableIPv4 {
|
|
||||||
return nil, E.Cause(err, "ip6tables is required")
|
|
||||||
} else {
|
|
||||||
s.enableIPv6 = false
|
|
||||||
t.logger.Error("device has no ip6tables nat support: ", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var listenAddr netip.Addr
|
|
||||||
if C.IsAndroid {
|
|
||||||
listenAddr = netip.AddrFrom4([4]byte{127, 0, 0, 1})
|
|
||||||
} else if s.enableIPv6 {
|
|
||||||
listenAddr = netip.IPv6Unspecified()
|
|
||||||
} else {
|
|
||||||
listenAddr = netip.IPv4Unspecified()
|
|
||||||
}
|
|
||||||
s.listenOptions.Listen = option.NewListenAddress(listenAddr)
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) initializeNfTables() error {
|
|
||||||
disabled, err := strconv.ParseBool(os.Getenv("AUTO_REDIRECT_DISABLE_NFTABLES"))
|
|
||||||
if err == nil && disabled {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
nft, err := nftables.New()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer nft.CloseLasting()
|
|
||||||
_, err = nft.ListTablesOfFamily(unix.AF_INET)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
t.useNfTables = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) Start() error {
|
|
||||||
err := t.myInboundAdapter.Start()
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "start redirect server")
|
|
||||||
}
|
|
||||||
t.cleanupTables()
|
|
||||||
err = t.setupTables()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) Close() error {
|
|
||||||
t.cleanupTables()
|
|
||||||
return t.myInboundAdapter.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
destination, err := redir.GetOriginalDestination(conn)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "get redirect destination")
|
|
||||||
}
|
|
||||||
metadata.Destination = M.SocksaddrFromNetIP(destination)
|
|
||||||
return t.newConnection(ctx, conn, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) setupTables() error {
|
|
||||||
var setupTables func(int) error
|
|
||||||
if t.useNfTables {
|
|
||||||
setupTables = t.setupNfTables
|
|
||||||
} else {
|
|
||||||
setupTables = t.setupIPTables
|
|
||||||
}
|
|
||||||
if t.enableIPv4 {
|
|
||||||
err := setupTables(unix.AF_INET)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t.enableIPv6 {
|
|
||||||
err := setupTables(unix.AF_INET6)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) cleanupTables() {
|
|
||||||
var cleanupTables func(int)
|
|
||||||
if t.useNfTables {
|
|
||||||
cleanupTables = t.cleanupNfTables
|
|
||||||
} else {
|
|
||||||
cleanupTables = t.cleanupIPTables
|
|
||||||
}
|
|
||||||
if t.enableIPv4 {
|
|
||||||
cleanupTables(unix.AF_INET)
|
|
||||||
}
|
|
||||||
if t.enableIPv6 {
|
|
||||||
cleanupTables(unix.AF_INET6)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,235 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package inbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
iptablesTableNameOutput = "sing-box-output"
|
|
||||||
iptablesTableNameForward = "sing-box-forward"
|
|
||||||
iptablesTableNamePreRouteing = "sing-box-prerouting"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) iptablesPathForFamily(family int) string {
|
|
||||||
if family == unix.AF_INET {
|
|
||||||
return t.iptablesPath
|
|
||||||
} else {
|
|
||||||
return t.ip6tablesPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) setupIPTables(family int) error {
|
|
||||||
iptablesPath := t.iptablesPathForFamily(family)
|
|
||||||
// OUTPUT
|
|
||||||
err := t.runShell(iptablesPath, "-t nat -N", iptablesTableNameOutput)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNameOutput,
|
|
||||||
"-p tcp -o", t.tunOptions.Name,
|
|
||||||
"-j REDIRECT --to-ports", M.AddrPortFromNet(t.tcpListener.Addr()).Port())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -I OUTPUT -j", iptablesTableNameOutput)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !t.androidSu {
|
|
||||||
// FORWARD
|
|
||||||
err = t.runShell(iptablesPath, "-N", iptablesTableNameForward)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = t.runShell(iptablesPath, "-A", iptablesTableNameForward,
|
|
||||||
"-i", t.tunOptions.Name, "-j", "ACCEPT")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = t.runShell(iptablesPath, "-A", iptablesTableNameForward,
|
|
||||||
"-o", t.tunOptions.Name, "-j", "ACCEPT")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = t.runShell(iptablesPath, "-I FORWARD -j", iptablesTableNameForward)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// PREROUTING
|
|
||||||
err = t.setupIPTablesPreRouting(family)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) setupIPTablesPreRouting(family int) error {
|
|
||||||
iptablesPath := t.iptablesPathForFamily(family)
|
|
||||||
err := t.runShell(iptablesPath, "-t nat -N", iptablesTableNamePreRouteing)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
routeAddress []netip.Prefix
|
|
||||||
routeExcludeAddress []netip.Prefix
|
|
||||||
)
|
|
||||||
if family == unix.AF_INET {
|
|
||||||
routeAddress = t.tunOptions.Inet4RouteAddress
|
|
||||||
routeExcludeAddress = t.tunOptions.Inet4RouteExcludeAddress
|
|
||||||
} else {
|
|
||||||
routeAddress = t.tunOptions.Inet6RouteAddress
|
|
||||||
routeExcludeAddress = t.tunOptions.Inet6RouteExcludeAddress
|
|
||||||
}
|
|
||||||
if len(routeAddress) > 0 && (len(t.tunOptions.IncludeInterface) > 0 || len(t.tunOptions.IncludeUID) > 0) {
|
|
||||||
return E.New("`*_route_address` is conflict with `include_interface` or `include_uid`")
|
|
||||||
}
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-i", t.tunOptions.Name, "-j RETURN")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, address := range routeExcludeAddress {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-d", address.String(), "-j RETURN")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, name := range t.tunOptions.ExcludeInterface {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-i", name, "-j RETURN")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, uid := range t.tunOptions.ExcludeUID {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-m owner --uid-owner", uid, "-j RETURN")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var dnsServerAddress netip.Addr
|
|
||||||
if family == unix.AF_INET {
|
|
||||||
dnsServerAddress = t.tunOptions.Inet4Address[0].Addr().Next()
|
|
||||||
} else {
|
|
||||||
dnsServerAddress = t.tunOptions.Inet6Address[0].Addr().Next()
|
|
||||||
}
|
|
||||||
if len(routeAddress) > 0 {
|
|
||||||
for _, address := range routeAddress {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-d", address.String(), "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if len(t.tunOptions.IncludeInterface) > 0 || len(t.tunOptions.IncludeUID) > 0 {
|
|
||||||
for _, name := range t.tunOptions.IncludeInterface {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-i", name, "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, uidRange := range t.tunOptions.IncludeUID {
|
|
||||||
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-m owner --uid-owner", uid, "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-p udp --dport 53 -j DNAT --to", dnsServerAddress)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing, "-m addrtype --dst-type LOCAL -j RETURN")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(routeAddress) > 0 {
|
|
||||||
for _, address := range routeAddress {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-d", address.String(), "-p tcp -j REDIRECT --to-ports", M.AddrPortFromNet(t.tcpListener.Addr()).Port())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if len(t.tunOptions.IncludeInterface) > 0 || len(t.tunOptions.IncludeUID) > 0 {
|
|
||||||
for _, name := range t.tunOptions.IncludeInterface {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-i", name, "-p tcp -j REDIRECT --to-ports", M.AddrPortFromNet(t.tcpListener.Addr()).Port())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, uidRange := range t.tunOptions.IncludeUID {
|
|
||||||
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-m owner --uid-owner", uid, "-p tcp -j REDIRECT --to-ports", M.AddrPortFromNet(t.tcpListener.Addr()).Port())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -A", iptablesTableNamePreRouteing,
|
|
||||||
"-p tcp -j REDIRECT --to-ports", M.AddrPortFromNet(t.tcpListener.Addr()).Port())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = t.runShell(iptablesPath, "-t nat -I PREROUTING -j", iptablesTableNamePreRouteing)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) cleanupIPTables(family int) {
|
|
||||||
iptablesPath := t.iptablesPathForFamily(family)
|
|
||||||
_ = t.runShell(iptablesPath, "-t nat -D OUTPUT -j", iptablesTableNameOutput)
|
|
||||||
_ = t.runShell(iptablesPath, "-t nat -F", iptablesTableNameOutput)
|
|
||||||
_ = t.runShell(iptablesPath, "-t nat -X", iptablesTableNameOutput)
|
|
||||||
if !t.androidSu {
|
|
||||||
_ = t.runShell(iptablesPath, "-D FORWARD -j", iptablesTableNameForward)
|
|
||||||
_ = t.runShell(iptablesPath, "-F", iptablesTableNameForward)
|
|
||||||
_ = t.runShell(iptablesPath, "-X", iptablesTableNameForward)
|
|
||||||
_ = t.runShell(iptablesPath, "-t nat -D PREROUTING -j", iptablesTableNamePreRouteing)
|
|
||||||
_ = t.runShell(iptablesPath, "-t nat -F", iptablesTableNamePreRouteing)
|
|
||||||
_ = t.runShell(iptablesPath, "-t nat -X", iptablesTableNamePreRouteing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) runShell(commands ...any) error {
|
|
||||||
commandStr := strings.Join(F.MapToString(commands), " ")
|
|
||||||
var command *exec.Cmd
|
|
||||||
if t.androidSu {
|
|
||||||
command = exec.Command(t.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
|
|
||||||
}
|
|
@ -1,231 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package inbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/sagernet/nftables"
|
|
||||||
"github.com/sagernet/nftables/binaryutil"
|
|
||||||
"github.com/sagernet/nftables/expr"
|
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
nftablesTableName = "sing-box"
|
|
||||||
nftablesChainOutput = "output"
|
|
||||||
nftablesChainForward = "forward"
|
|
||||||
nftablesChainPreRouting = "prerouting"
|
|
||||||
)
|
|
||||||
|
|
||||||
func nftablesFamily(family int) nftables.TableFamily {
|
|
||||||
switch family {
|
|
||||||
case unix.AF_INET:
|
|
||||||
return nftables.TableFamilyIPv4
|
|
||||||
case unix.AF_INET6:
|
|
||||||
return nftables.TableFamilyIPv6
|
|
||||||
default:
|
|
||||||
panic(F.ToString("unknown family ", family))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) setupNfTables(family int) error {
|
|
||||||
nft, err := nftables.New()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer nft.CloseLasting()
|
|
||||||
table := nft.AddTable(&nftables.Table{
|
|
||||||
Name: nftablesTableName,
|
|
||||||
Family: nftablesFamily(family),
|
|
||||||
})
|
|
||||||
chainOutput := nft.AddChain(&nftables.Chain{
|
|
||||||
Name: nftablesChainOutput,
|
|
||||||
Table: table,
|
|
||||||
Hooknum: nftables.ChainHookOutput,
|
|
||||||
Priority: nftables.ChainPriorityMangle,
|
|
||||||
Type: nftables.ChainTypeNAT,
|
|
||||||
})
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainOutput,
|
|
||||||
Exprs: nftablesRuleIfName(expr.MetaKeyOIFNAME, t.tunOptions.Name, nftablesRuleRedirectToPorts(M.AddrPortFromNet(t.tcpListener.Addr()).Port())...),
|
|
||||||
})
|
|
||||||
chainForward := nft.AddChain(&nftables.Chain{
|
|
||||||
Name: nftablesChainForward,
|
|
||||||
Table: table,
|
|
||||||
Hooknum: nftables.ChainHookForward,
|
|
||||||
Priority: nftables.ChainPriorityMangle,
|
|
||||||
})
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainForward,
|
|
||||||
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, t.tunOptions.Name, &expr.Verdict{
|
|
||||||
Kind: expr.VerdictAccept,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainForward,
|
|
||||||
Exprs: nftablesRuleIfName(expr.MetaKeyOIFNAME, t.tunOptions.Name, &expr.Verdict{
|
|
||||||
Kind: expr.VerdictAccept,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
t.setupNfTablesPreRouting(nft, table)
|
|
||||||
return nft.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) setupNfTablesPreRouting(nft *nftables.Conn, table *nftables.Table) {
|
|
||||||
chainPreRouting := nft.AddChain(&nftables.Chain{
|
|
||||||
Name: nftablesChainPreRouting,
|
|
||||||
Table: table,
|
|
||||||
Hooknum: nftables.ChainHookPrerouting,
|
|
||||||
Priority: nftables.ChainPriorityMangle,
|
|
||||||
Type: nftables.ChainTypeNAT,
|
|
||||||
})
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, t.tunOptions.Name, &expr.Verdict{
|
|
||||||
Kind: expr.VerdictReturn,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
var (
|
|
||||||
routeAddress []netip.Prefix
|
|
||||||
routeExcludeAddress []netip.Prefix
|
|
||||||
)
|
|
||||||
if table.Family == nftables.TableFamilyIPv4 {
|
|
||||||
routeAddress = t.tunOptions.Inet4RouteAddress
|
|
||||||
routeExcludeAddress = t.tunOptions.Inet4RouteExcludeAddress
|
|
||||||
} else {
|
|
||||||
routeAddress = t.tunOptions.Inet6RouteAddress
|
|
||||||
routeExcludeAddress = t.tunOptions.Inet6RouteExcludeAddress
|
|
||||||
}
|
|
||||||
for _, address := range routeExcludeAddress {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: nftablesRuleDestinationAddress(address, &expr.Verdict{
|
|
||||||
Kind: expr.VerdictReturn,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, name := range t.tunOptions.ExcludeInterface {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, name, &expr.Verdict{
|
|
||||||
Kind: expr.VerdictReturn,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, uidRange := range t.tunOptions.ExcludeUID {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: nftablesRuleMetaUInt32Range(expr.MetaKeySKUID, uidRange, &expr.Verdict{
|
|
||||||
Kind: expr.VerdictReturn,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var routeExprs []expr.Any
|
|
||||||
if len(routeAddress) > 0 {
|
|
||||||
for _, address := range routeAddress {
|
|
||||||
routeExprs = append(routeExprs, nftablesRuleDestinationAddress(address)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
redirectPort := M.AddrPortFromNet(t.tcpListener.Addr()).Port()
|
|
||||||
var dnsServerAddress netip.Addr
|
|
||||||
if table.Family == nftables.TableFamilyIPv4 {
|
|
||||||
dnsServerAddress = t.tunOptions.Inet4Address[0].Addr().Next()
|
|
||||||
} else {
|
|
||||||
dnsServerAddress = t.tunOptions.Inet6Address[0].Addr().Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(t.tunOptions.IncludeInterface) > 0 || len(t.tunOptions.IncludeUID) > 0 {
|
|
||||||
for _, name := range t.tunOptions.IncludeInterface {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, name, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...)...),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, uidRange := range t.tunOptions.IncludeUID {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: nftablesRuleMetaUInt32Range(expr.MetaKeySKUID, uidRange, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...)...),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: []expr.Any{
|
|
||||||
&expr.Fib{
|
|
||||||
Register: 1,
|
|
||||||
FlagDADDR: true,
|
|
||||||
ResultADDRTYPE: true,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: binaryutil.NativeEndian.PutUint32(unix.RTN_LOCAL),
|
|
||||||
},
|
|
||||||
&expr.Verdict{
|
|
||||||
Kind: expr.VerdictReturn,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(t.tunOptions.IncludeInterface) > 0 || len(t.tunOptions.IncludeUID) > 0 {
|
|
||||||
for _, name := range t.tunOptions.IncludeInterface {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, name, append(routeExprs, nftablesRuleRedirectToPorts(redirectPort)...)...),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, uidRange := range t.tunOptions.IncludeUID {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: nftablesRuleMetaUInt32Range(expr.MetaKeySKUID, uidRange, append(routeExprs, nftablesRuleRedirectToPorts(redirectPort)...)...),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nft.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chainPreRouting,
|
|
||||||
Exprs: append(routeExprs, nftablesRuleRedirectToPorts(redirectPort)...),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) cleanupNfTables(family int) {
|
|
||||||
conn, err := nftables.New()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.CloseLasting()
|
|
||||||
conn.FlushTable(&nftables.Table{
|
|
||||||
Name: nftablesTableName,
|
|
||||||
Family: nftablesFamily(family),
|
|
||||||
})
|
|
||||||
conn.DelTable(&nftables.Table{
|
|
||||||
Name: nftablesTableName,
|
|
||||||
Family: nftablesFamily(family),
|
|
||||||
})
|
|
||||||
_ = conn.Flush()
|
|
||||||
}
|
|
@ -1,153 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package inbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/sagernet/nftables"
|
|
||||||
"github.com/sagernet/nftables/binaryutil"
|
|
||||||
"github.com/sagernet/nftables/expr"
|
|
||||||
"github.com/sagernet/sing/common/ranges"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func nftablesIfname(n string) []byte {
|
|
||||||
b := make([]byte, 16)
|
|
||||||
copy(b, n+"\x00")
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func nftablesRuleIfName(key expr.MetaKey, value string, exprs ...expr.Any) []expr.Any {
|
|
||||||
newExprs := []expr.Any{
|
|
||||||
&expr.Meta{Key: key, Register: 1},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: nftablesIfname(value),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
newExprs = append(newExprs, exprs...)
|
|
||||||
return newExprs
|
|
||||||
}
|
|
||||||
|
|
||||||
func nftablesRuleMetaUInt32Range(key expr.MetaKey, uidRange ranges.Range[uint32], exprs ...expr.Any) []expr.Any {
|
|
||||||
newExprs := []expr.Any{
|
|
||||||
&expr.Meta{Key: key, Register: 1},
|
|
||||||
&expr.Range{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
FromData: binaryutil.BigEndian.PutUint32(uidRange.Start),
|
|
||||||
ToData: binaryutil.BigEndian.PutUint32(uidRange.End),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
newExprs = append(newExprs, exprs...)
|
|
||||||
return newExprs
|
|
||||||
}
|
|
||||||
|
|
||||||
func nftablesRuleDestinationAddress(address netip.Prefix, exprs ...expr.Any) []expr.Any {
|
|
||||||
var newExprs []expr.Any
|
|
||||||
if address.Addr().Is4() {
|
|
||||||
newExprs = append(newExprs, &expr.Payload{
|
|
||||||
OperationType: expr.PayloadLoad,
|
|
||||||
DestRegister: 1,
|
|
||||||
SourceRegister: 0,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: 16,
|
|
||||||
Len: 4,
|
|
||||||
}, &expr.Bitwise{
|
|
||||||
SourceRegister: 1,
|
|
||||||
DestRegister: 1,
|
|
||||||
Len: 4,
|
|
||||||
Xor: make([]byte, 4),
|
|
||||||
Mask: net.CIDRMask(address.Bits(), 32),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
newExprs = append(newExprs, &expr.Payload{
|
|
||||||
OperationType: expr.PayloadLoad,
|
|
||||||
DestRegister: 1,
|
|
||||||
SourceRegister: 0,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: 24,
|
|
||||||
Len: 16,
|
|
||||||
}, &expr.Bitwise{
|
|
||||||
SourceRegister: 1,
|
|
||||||
DestRegister: 1,
|
|
||||||
Len: 16,
|
|
||||||
Xor: make([]byte, 16),
|
|
||||||
Mask: net.CIDRMask(address.Bits(), 128),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
newExprs = append(newExprs, &expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: address.Masked().Addr().AsSlice(),
|
|
||||||
})
|
|
||||||
newExprs = append(newExprs, exprs...)
|
|
||||||
return newExprs
|
|
||||||
}
|
|
||||||
|
|
||||||
func nftablesRuleHijackDNS(family nftables.TableFamily, dnsServerAddress netip.Addr) []expr.Any {
|
|
||||||
return []expr.Any{
|
|
||||||
&expr.Meta{
|
|
||||||
Key: expr.MetaKeyL4PROTO,
|
|
||||||
Register: 1,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: []byte{unix.IPPROTO_UDP},
|
|
||||||
},
|
|
||||||
&expr.Payload{
|
|
||||||
OperationType: expr.PayloadLoad,
|
|
||||||
DestRegister: 1,
|
|
||||||
SourceRegister: 0,
|
|
||||||
Base: expr.PayloadBaseTransportHeader,
|
|
||||||
Offset: 2,
|
|
||||||
Len: 2,
|
|
||||||
}, &expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: binaryutil.BigEndian.PutUint16(53),
|
|
||||||
}, &expr.Immediate{
|
|
||||||
Register: 1,
|
|
||||||
Data: dnsServerAddress.AsSlice(),
|
|
||||||
}, &expr.NAT{
|
|
||||||
Type: expr.NATTypeDestNAT,
|
|
||||||
Family: uint32(family),
|
|
||||||
RegAddrMin: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
NF_NAT_RANGE_MAP_IPS = 1 << iota
|
|
||||||
NF_NAT_RANGE_PROTO_SPECIFIED
|
|
||||||
NF_NAT_RANGE_PROTO_RANDOM
|
|
||||||
NF_NAT_RANGE_PERSISTENT
|
|
||||||
NF_NAT_RANGE_PROTO_RANDOM_FULLY
|
|
||||||
NF_NAT_RANGE_PROTO_OFFSET
|
|
||||||
)
|
|
||||||
|
|
||||||
func nftablesRuleRedirectToPorts(redirectPort uint16) []expr.Any {
|
|
||||||
return []expr.Any{
|
|
||||||
&expr.Meta{
|
|
||||||
Key: expr.MetaKeyL4PROTO,
|
|
||||||
Register: 1,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: []byte{unix.IPPROTO_TCP},
|
|
||||||
},
|
|
||||||
&expr.Immediate{
|
|
||||||
Register: 1,
|
|
||||||
Data: binaryutil.BigEndian.PutUint16(redirectPort),
|
|
||||||
}, &expr.Redir{
|
|
||||||
RegisterProtoMin: 1,
|
|
||||||
Flags: NF_NAT_RANGE_PROTO_SPECIFIED,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
//go:build !linux
|
|
||||||
|
|
||||||
package inbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tunAutoRedirect struct{}
|
|
||||||
|
|
||||||
func newAutoRedirect(t *Tun) (*tunAutoRedirect, error) {
|
|
||||||
return nil, E.New("only supported on linux")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) Start() error {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunAutoRedirect) Close() error {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user