mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-08 11:44:13 +08:00
Add ping support for WireGuard endpoint
This commit is contained in:
parent
baa68b5c6f
commit
f2d26f5842
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,6 +19,11 @@ type Outbound interface {
|
|||||||
N.Dialer
|
N.Dialer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DirectRouteOutbound interface {
|
||||||
|
Outbound
|
||||||
|
NewDirectRouteConnection(metadata InboundContext, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error)
|
||||||
|
}
|
||||||
|
|
||||||
type OutboundRegistry interface {
|
type OutboundRegistry interface {
|
||||||
option.OutboundOptionsRegistry
|
option.OutboundOptionsRegistry
|
||||||
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/ntp"
|
"github.com/sagernet/sing/common/ntp"
|
||||||
@ -19,7 +20,7 @@ import (
|
|||||||
type Router interface {
|
type Router interface {
|
||||||
Lifecycle
|
Lifecycle
|
||||||
ConnectionRouter
|
ConnectionRouter
|
||||||
PreMatch(metadata InboundContext) error
|
PreMatch(metadata InboundContext, context tun.DirectRouteContext) (tun.DirectRouteDestination, error)
|
||||||
ConnectionRouterEx
|
ConnectionRouterEx
|
||||||
RuleSet(tag string) (RuleSet, bool)
|
RuleSet(tag string) (RuleSet, bool)
|
||||||
NeedWIFIState() bool
|
NeedWIFIState() bool
|
||||||
|
4
go.mod
4
go.mod
@ -25,7 +25,7 @@ require (
|
|||||||
github.com/sagernet/cors v1.2.1
|
github.com/sagernet/cors v1.2.1
|
||||||
github.com/sagernet/fswatch v0.1.1
|
github.com/sagernet/fswatch v0.1.1
|
||||||
github.com/sagernet/gomobile v0.1.4
|
github.com/sagernet/gomobile v0.1.4
|
||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
github.com/sagernet/gvisor v0.0.0-20250217052116-ed66b6946f72
|
||||||
github.com/sagernet/quic-go v0.49.0-beta.1
|
github.com/sagernet/quic-go v0.49.0-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.6.2-0.20250210105917-3464ed3babc0
|
github.com/sagernet/sing v0.6.2-0.20250210105917-3464ed3babc0
|
||||||
@ -34,7 +34,7 @@ require (
|
|||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
github.com/sagernet/sing-shadowsocks2 v0.2.0
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0
|
github.com/sagernet/sing-shadowtls v0.2.0
|
||||||
github.com/sagernet/sing-tun v0.6.1
|
github.com/sagernet/sing-tun v0.6.2-0.20250217135654-784bb584392f
|
||||||
github.com/sagernet/sing-vmess v0.2.0
|
github.com/sagernet/sing-vmess v0.2.0
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||||
github.com/sagernet/tailscale v1.79.0-mod.1
|
github.com/sagernet/tailscale v1.79.0-mod.1
|
||||||
|
8
go.sum
8
go.sum
@ -171,8 +171,8 @@ github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQ
|
|||||||
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o=
|
||||||
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
github.com/sagernet/gomobile v0.1.4 h1:WzX9ka+iHdupMgy2Vdich+OAt7TM8C2cZbIbzNjBrJY=
|
||||||
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
github.com/sagernet/gomobile v0.1.4/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
|
||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff h1:mlohw3360Wg1BNGook/UHnISXhUx4Gd/3tVLs5T0nSs=
|
github.com/sagernet/gvisor v0.0.0-20250217052116-ed66b6946f72 h1:Jgv6N59yiVMEwimTcFV1EVcu2Aa7R2Wh1ZAYNzWP2qA=
|
||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
github.com/sagernet/gvisor v0.0.0-20250217052116-ed66b6946f72/go.mod h1:ehZwnT2UpmOWAHFL48XdBhnd4Qu4hN2O3Ji0us3ZHMw=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
|
||||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
|
||||||
@ -194,8 +194,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.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk=
|
github.com/sagernet/sing-shadowtls v0.2.0 h1:cLKe4OAOFwuhmAIuPLj//CIL7Q9js+pIDardhJ+/osk=
|
||||||
github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo=
|
github.com/sagernet/sing-shadowtls v0.2.0/go.mod h1:agU+Fw5X+xnWVyRHyFthoZCX3MfWKCFPm4JUf+1oaxo=
|
||||||
github.com/sagernet/sing-tun v0.6.1 h1:4l0+gnEKcGjlWfUVTD+W0BRApqIny/lU2ZliurE+VMo=
|
github.com/sagernet/sing-tun v0.6.2-0.20250217135654-784bb584392f h1:VEtmCNfk8RuZcYz/Xx63F2QjcgG3z/7Pa0uiJciQo+Y=
|
||||||
github.com/sagernet/sing-tun v0.6.1/go.mod h1:fisFCbC4Vfb6HqQNcwPJi2CDK2bf0Xapyz3j3t4cnHE=
|
github.com/sagernet/sing-tun v0.6.2-0.20250217135654-784bb584392f/go.mod h1:UiOi1ombGaAzWkGSgH4qcP7Zpq8FjWc1uQmleK8oPCE=
|
||||||
github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI=
|
github.com/sagernet/sing-vmess v0.2.0 h1:pCMGUXN2k7RpikQV65/rtXtDHzb190foTfF9IGTMZrI=
|
||||||
github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
github.com/sagernet/sing-vmess v0.2.0/go.mod h1:jDAZ0A0St1zVRkyvhAPRySOFfhC+4SQtO5VYyeFotgA=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||||
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@ -205,8 +206,10 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
|
|||||||
|
|
||||||
ipStack := t.server.ExportNetstack().ExportIPStack()
|
ipStack := t.server.ExportNetstack().ExportIPStack()
|
||||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket)
|
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(t.ctx, ipStack, t).HandlePacket)
|
||||||
udpForwarder := tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout)
|
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.NewUDPForwarder(t.ctx, ipStack, t, t.udpTimeout).HandlePacket)
|
||||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
icmpForwarder := tun.NewICMPForwarder(t.ctx, ipStack, t, t.udpTimeout)
|
||||||
|
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
|
||||||
|
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
|
||||||
t.stack = ipStack
|
t.stack = ipStack
|
||||||
|
|
||||||
localBackend := t.server.ExportLocalBackend()
|
localBackend := t.server.ExportLocalBackend()
|
||||||
@ -377,7 +380,7 @@ func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
|||||||
return udpConn, nil
|
return udpConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
||||||
tsFilter := t.filter.Load()
|
tsFilter := t.filter.Load()
|
||||||
if tsFilter != nil {
|
if tsFilter != nil {
|
||||||
var ipProto ipproto.Proto
|
var ipProto ipproto.Proto
|
||||||
@ -390,9 +393,9 @@ func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destina
|
|||||||
response := tsFilter.Check(source.Addr, destination.Addr, destination.Port, ipProto)
|
response := tsFilter.Check(source.Addr, destination.Addr, destination.Port, ipProto)
|
||||||
switch response {
|
switch response {
|
||||||
case filter.Drop:
|
case filter.Drop:
|
||||||
return syscall.ECONNRESET
|
return nil, syscall.ECONNREFUSED
|
||||||
case filter.DropSilently:
|
case filter.DropSilently:
|
||||||
return tun.ErrDrop
|
return nil, tun.ErrDrop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return t.router.PreMatch(adapter.InboundContext{
|
return t.router.PreMatch(adapter.InboundContext{
|
||||||
@ -401,7 +404,7 @@ func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destina
|
|||||||
Network: network,
|
Network: network,
|
||||||
Source: source,
|
Source: source,
|
||||||
Destination: destination,
|
Destination: destination,
|
||||||
})
|
}, routeContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@ -438,15 +439,21 @@ func (t *Inbound) Close() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Inbound) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
func (t *Inbound) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
||||||
return t.router.PreMatch(adapter.InboundContext{
|
routeDestination, err := t.router.PreMatch(adapter.InboundContext{
|
||||||
Inbound: t.tag,
|
Inbound: t.tag,
|
||||||
InboundType: C.TypeTun,
|
InboundType: C.TypeTun,
|
||||||
Network: network,
|
Network: network,
|
||||||
Source: source,
|
Source: source,
|
||||||
Destination: destination,
|
Destination: destination,
|
||||||
InboundOptions: t.inboundOptions,
|
InboundOptions: t.inboundOptions,
|
||||||
})
|
}, routeContext)
|
||||||
|
if err != nil {
|
||||||
|
if !E.IsMulti(err, tun.ErrDrop, syscall.ECONNREFUSED) {
|
||||||
|
t.logger.Warn(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return routeDestination, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/wireguard"
|
"github.com/sagernet/sing-box/transport/wireguard"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@ -43,7 +44,7 @@ type Endpoint struct {
|
|||||||
|
|
||||||
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardEndpointOptions) (adapter.Endpoint, error) {
|
func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardEndpointOptions) (adapter.Endpoint, error) {
|
||||||
ep := &Endpoint{
|
ep := &Endpoint{
|
||||||
Adapter: endpoint.NewAdapterWithDialerOptions(C.TypeWireGuard, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions),
|
Adapter: endpoint.NewAdapterWithDialerOptions(C.TypeWireGuard, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMPv4, N.NetworkICMPv6}, options.DialerOptions),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
|
||||||
@ -132,14 +133,14 @@ func (w *Endpoint) InterfaceUpdated() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr) error {
|
func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, context tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
||||||
return w.router.PreMatch(adapter.InboundContext{
|
return w.router.PreMatch(adapter.InboundContext{
|
||||||
Inbound: w.Tag(),
|
Inbound: w.Tag(),
|
||||||
InboundType: w.Type(),
|
InboundType: w.Type(),
|
||||||
Network: network,
|
Network: network,
|
||||||
Source: source,
|
Source: source,
|
||||||
Destination: destination,
|
Destination: destination,
|
||||||
})
|
}, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
func (w *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
|
||||||
@ -220,3 +221,12 @@ func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
|||||||
}
|
}
|
||||||
return w.endpoint.ListenPacket(ctx, destination)
|
return w.endpoint.ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Endpoint) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
||||||
|
destination, err := w.endpoint.NewDirectRouteConnection(metadata, routeContext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
w.logger.Info("linked ", metadata.Network, " connection to ", metadata.Destination.AddrString())
|
||||||
|
return destination, nil
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/route/rule"
|
"github.com/sagernet/sing-box/route/rule"
|
||||||
"github.com/sagernet/sing-mux"
|
"github.com/sagernet/sing-mux"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing-vmess"
|
"github.com/sagernet/sing-vmess"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
@ -271,19 +272,36 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) PreMatch(metadata adapter.InboundContext) error {
|
func (r *Router) PreMatch(metadata adapter.InboundContext, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
||||||
selectedRule, _, _, _, err := r.matchRule(r.ctx, &metadata, true, nil, nil)
|
selectedRule, _, _, _, err := r.matchRule(r.ctx, &metadata, true, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
if selectedRule == nil {
|
if selectedRule == nil {
|
||||||
return nil
|
defaultOutbound := r.outbound.Default()
|
||||||
|
if !common.Contains(defaultOutbound.Network(), metadata.Network) {
|
||||||
|
return nil, E.New(metadata.Network, " is not supported by default outbound: ", defaultOutbound.Tag())
|
||||||
}
|
}
|
||||||
rejectAction, isReject := selectedRule.Action().(*rule.RuleActionReject)
|
return defaultOutbound.(adapter.DirectRouteOutbound).NewDirectRouteConnection(metadata, routeContext)
|
||||||
if !isReject {
|
}
|
||||||
return nil
|
switch action := selectedRule.Action().(type) {
|
||||||
|
case *rule.RuleActionReject:
|
||||||
|
return nil, action.Error(context.Background())
|
||||||
|
case *rule.RuleActionRoute:
|
||||||
|
if routeContext == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
outbound, loaded := r.outbound.Outbound(action.Outbound)
|
||||||
|
if !loaded {
|
||||||
|
return nil, E.New("outbound not found: ", action.Outbound)
|
||||||
|
}
|
||||||
|
if !common.Contains(outbound.Network(), metadata.Network) {
|
||||||
|
return nil, E.New(metadata.Network, " is not supported by outbound: ", action.Outbound)
|
||||||
|
}
|
||||||
|
return outbound.(adapter.DirectRouteOutbound).NewDirectRouteConnection(metadata, routeContext)
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
return rejectAction.Error(context.Background())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) matchRule(
|
func (r *Router) matchRule(
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
@ -17,6 +18,8 @@ type Device interface {
|
|||||||
N.Dialer
|
N.Dialer
|
||||||
Start() error
|
Start() error
|
||||||
SetDevice(device *device.Device)
|
SetDevice(device *device.Device)
|
||||||
|
Inet4Address() netip.Addr
|
||||||
|
Inet6Address() netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeviceOptions struct {
|
type DeviceOptions struct {
|
||||||
@ -41,3 +44,8 @@ func NewDevice(options DeviceOptions) (Device, error) {
|
|||||||
return newSystemStackDevice(options)
|
return newSystemStackDevice(options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NatDevice interface {
|
||||||
|
Device
|
||||||
|
CreateDestination(metadata adapter.InboundContext, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error)
|
||||||
|
}
|
||||||
|
85
transport/wireguard/device_nat.go
Normal file
85
transport/wireguard/device_nat.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Device = (*natDeviceWrapper)(nil)
|
||||||
|
|
||||||
|
type natDeviceWrapper struct {
|
||||||
|
Device
|
||||||
|
gVisorOutbound
|
||||||
|
packetOutbound chan *buf.Buffer
|
||||||
|
mapping *tun.NatMapping
|
||||||
|
writer *tun.NatWriter
|
||||||
|
buffer [][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNATDevice(upstream Device, ipRewrite bool) NatDevice {
|
||||||
|
wrapper := &natDeviceWrapper{
|
||||||
|
Device: upstream,
|
||||||
|
gVisorOutbound: newGVisorOutbound(),
|
||||||
|
packetOutbound: make(chan *buf.Buffer, 256),
|
||||||
|
mapping: tun.NewNatMapping(ipRewrite),
|
||||||
|
}
|
||||||
|
if ipRewrite {
|
||||||
|
wrapper.writer = tun.NewNatWriter(upstream.Inet4Address(), upstream.Inet6Address())
|
||||||
|
}
|
||||||
|
return wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *natDeviceWrapper) Write(bufs [][]byte, offset int) (int, error) {
|
||||||
|
for _, buffer := range bufs {
|
||||||
|
handled, err := d.mapping.WritePacket(buffer[offset:])
|
||||||
|
if handled {
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d.buffer = append(d.buffer, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(d.buffer) > 0 {
|
||||||
|
_, err := d.Device.Write(d.buffer, offset)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
d.buffer = d.buffer[:0]
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *natDeviceWrapper) CreateDestination(metadata adapter.InboundContext, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
||||||
|
session := tun.DirectRouteSession{
|
||||||
|
Source: metadata.Source.Addr,
|
||||||
|
Destination: metadata.Destination.Addr,
|
||||||
|
}
|
||||||
|
d.mapping.CreateSession(session, routeContext)
|
||||||
|
return &natDestinationWrapper{d, session}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ tun.DirectRouteDestination = (*natDestinationWrapper)(nil)
|
||||||
|
|
||||||
|
type natDestinationWrapper struct {
|
||||||
|
device *natDeviceWrapper
|
||||||
|
session tun.DirectRouteSession
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *natDestinationWrapper) WritePacket(buffer *buf.Buffer) error {
|
||||||
|
if d.device.writer != nil {
|
||||||
|
d.device.writer.RewritePacket(buffer.Bytes())
|
||||||
|
}
|
||||||
|
d.device.packetOutbound <- buffer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *natDestinationWrapper) Close() error {
|
||||||
|
d.device.mapping.DeleteSession(d.session)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *natDestinationWrapper) Timeout() bool {
|
||||||
|
return false
|
||||||
|
}
|
48
transport/wireguard/device_nat_gvisor.go
Normal file
48
transport/wireguard/device_nat_gvisor.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
//go:build with_gvisor
|
||||||
|
|
||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gVisorOutbound struct {
|
||||||
|
outbound chan *stack.PacketBuffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGVisorOutbound() gVisorOutbound {
|
||||||
|
return gVisorOutbound{
|
||||||
|
outbound: make(chan *stack.PacketBuffer, 256),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *natDeviceWrapper) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
|
||||||
|
select {
|
||||||
|
case packet := <-d.outbound:
|
||||||
|
defer packet.DecRef()
|
||||||
|
var copyN int
|
||||||
|
/*rangeIterate(packet.Data().AsRange(), func(view *buffer.View) {
|
||||||
|
copyN += copy(bufs[0][offset+copyN:], view.AsSlice())
|
||||||
|
})*/
|
||||||
|
for _, view := range packet.AsSlices() {
|
||||||
|
copyN += copy(bufs[0][offset+copyN:], view)
|
||||||
|
}
|
||||||
|
sizes[0] = copyN
|
||||||
|
return 1, nil
|
||||||
|
case packet := <-d.packetOutbound:
|
||||||
|
defer packet.Release()
|
||||||
|
sizes[0] = copy(bufs[0][offset:], packet.Bytes())
|
||||||
|
return 1, nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return d.Device.Read(bufs, sizes, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *natDestinationWrapper) WritePacketBuffer(packetBuffer *stack.PacketBuffer) error {
|
||||||
|
println("read from wg")
|
||||||
|
if d.device.writer != nil {
|
||||||
|
d.device.writer.RewritePacketBuffer(packetBuffer)
|
||||||
|
}
|
||||||
|
d.device.outbound <- packetBuffer
|
||||||
|
return nil
|
||||||
|
}
|
20
transport/wireguard/device_nat_non_gvisor.go
Normal file
20
transport/wireguard/device_nat_non_gvisor.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//go:build !with_gvisor
|
||||||
|
|
||||||
|
package wireguard
|
||||||
|
|
||||||
|
type gVisorOutbound struct{}
|
||||||
|
|
||||||
|
func newGVisorOutbound() gVisorOutbound {
|
||||||
|
return gVisorOutbound{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *natDeviceWrapper) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
|
||||||
|
select {
|
||||||
|
case packet := <-d.packetOutbound:
|
||||||
|
defer packet.Release()
|
||||||
|
sizes[0] = copy(bufs[0][offset:], packet.Bytes())
|
||||||
|
return 1, nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return d.Device.Read(bufs, sizes, offset)
|
||||||
|
}
|
@ -5,6 +5,7 @@ package wireguard
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/gvisor/pkg/buffer"
|
"github.com/sagernet/gvisor/pkg/buffer"
|
||||||
@ -14,9 +15,12 @@ import (
|
|||||||
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv4"
|
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv4"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv6"
|
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv6"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||||
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
||||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
@ -24,17 +28,20 @@ import (
|
|||||||
wgTun "github.com/sagernet/wireguard-go/tun"
|
wgTun "github.com/sagernet/wireguard-go/tun"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Device = (*stackDevice)(nil)
|
var _ NatDevice = (*stackDevice)(nil)
|
||||||
|
|
||||||
type stackDevice struct {
|
type stackDevice struct {
|
||||||
stack *stack.Stack
|
stack *stack.Stack
|
||||||
mtu uint32
|
mtu uint32
|
||||||
events chan wgTun.Event
|
events chan wgTun.Event
|
||||||
outbound chan *stack.PacketBuffer
|
outbound chan *stack.PacketBuffer
|
||||||
|
packetOutbound chan *buf.Buffer
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
dispatcher stack.NetworkDispatcher
|
dispatcher stack.NetworkDispatcher
|
||||||
addr4 tcpip.Address
|
addr4 tcpip.Address
|
||||||
addr6 tcpip.Address
|
addr6 tcpip.Address
|
||||||
|
mapping *tun.NatMapping
|
||||||
|
writer *tun.NatWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStackDevice(options DeviceOptions) (*stackDevice, error) {
|
func newStackDevice(options DeviceOptions) (*stackDevice, error) {
|
||||||
@ -42,7 +49,9 @@ func newStackDevice(options DeviceOptions) (*stackDevice, error) {
|
|||||||
mtu: options.MTU,
|
mtu: options.MTU,
|
||||||
events: make(chan wgTun.Event, 1),
|
events: make(chan wgTun.Event, 1),
|
||||||
outbound: make(chan *stack.PacketBuffer, 256),
|
outbound: make(chan *stack.PacketBuffer, 256),
|
||||||
|
packetOutbound: make(chan *buf.Buffer, 256),
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
|
mapping: tun.NewNatMapping(true),
|
||||||
}
|
}
|
||||||
ipStack, err := tun.NewGVisorStack((*wireEndpoint)(tunDevice))
|
ipStack, err := tun.NewGVisorStack((*wireEndpoint)(tunDevice))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -68,10 +77,14 @@ func newStackDevice(options DeviceOptions) (*stackDevice, error) {
|
|||||||
return nil, E.New("parse local address ", protoAddr.AddressWithPrefix, ": ", gErr.String())
|
return nil, E.New("parse local address ", protoAddr.AddressWithPrefix, ": ", gErr.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tunDevice.writer = tun.NewNatWriter(tunDevice.Inet4Address(), tunDevice.Inet6Address())
|
||||||
tunDevice.stack = ipStack
|
tunDevice.stack = ipStack
|
||||||
if options.Handler != nil {
|
if options.Handler != nil {
|
||||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
|
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
|
||||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.NewUDPForwarder(options.Context, ipStack, options.Handler, options.UDPTimeout).HandlePacket)
|
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.NewUDPForwarder(options.Context, ipStack, options.Handler, options.UDPTimeout).HandlePacket)
|
||||||
|
icmpForwarder := tun.NewICMPForwarder(options.Context, ipStack, options.Handler, options.UDPTimeout)
|
||||||
|
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
|
||||||
|
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
|
||||||
}
|
}
|
||||||
return tunDevice, nil
|
return tunDevice, nil
|
||||||
}
|
}
|
||||||
@ -130,6 +143,14 @@ func (w *stackDevice) ListenPacket(ctx context.Context, destination M.Socksaddr)
|
|||||||
return udpConn, nil
|
return udpConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *stackDevice) Inet4Address() netip.Addr {
|
||||||
|
return netip.AddrFrom4(w.addr4.As4())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *stackDevice) Inet6Address() netip.Addr {
|
||||||
|
return netip.AddrFrom16(w.addr6.As16())
|
||||||
|
}
|
||||||
|
|
||||||
func (w *stackDevice) SetDevice(device *device.Device) {
|
func (w *stackDevice) SetDevice(device *device.Device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,20 +165,24 @@ func (w *stackDevice) File() *os.File {
|
|||||||
|
|
||||||
func (w *stackDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
|
func (w *stackDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
|
||||||
select {
|
select {
|
||||||
case packetBuffer, ok := <-w.outbound:
|
case packet, ok := <-w.outbound:
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, os.ErrClosed
|
return 0, os.ErrClosed
|
||||||
}
|
}
|
||||||
defer packetBuffer.DecRef()
|
defer packet.DecRef()
|
||||||
p := bufs[0]
|
var copyN int
|
||||||
p = p[offset:]
|
/*rangeIterate(packet.Data().AsRange(), func(view *buffer.View) {
|
||||||
n := 0
|
copyN += copy(bufs[0][offset+copyN:], view.AsSlice())
|
||||||
for _, slice := range packetBuffer.AsSlices() {
|
})*/
|
||||||
n += copy(p[n:], slice)
|
for _, view := range packet.AsSlices() {
|
||||||
|
copyN += copy(bufs[0][offset+copyN:], view)
|
||||||
}
|
}
|
||||||
sizes[0] = n
|
sizes[0] = copyN
|
||||||
count = 1
|
return 1, nil
|
||||||
return
|
case packet := <-w.packetOutbound:
|
||||||
|
defer packet.Release()
|
||||||
|
sizes[0] = copy(bufs[0][offset:], packet.Bytes())
|
||||||
|
return 1, nil
|
||||||
case <-w.done:
|
case <-w.done:
|
||||||
return 0, os.ErrClosed
|
return 0, os.ErrClosed
|
||||||
}
|
}
|
||||||
@ -169,6 +194,14 @@ func (w *stackDevice) Write(bufs [][]byte, offset int) (count int, err error) {
|
|||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
handled, err := w.mapping.WritePacket(b)
|
||||||
|
if handled {
|
||||||
|
if err != nil {
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
continue
|
||||||
|
}
|
||||||
var networkProtocol tcpip.NetworkProtocolNumber
|
var networkProtocol tcpip.NetworkProtocolNumber
|
||||||
switch header.IPVersion(b) {
|
switch header.IPVersion(b) {
|
||||||
case header.IPv4Version:
|
case header.IPv4Version:
|
||||||
@ -282,3 +315,157 @@ func (ep *wireEndpoint) Close() {
|
|||||||
|
|
||||||
func (ep *wireEndpoint) SetOnCloseAction(f func()) {
|
func (ep *wireEndpoint) SetOnCloseAction(f func()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *stackDevice) CreateDestination(metadata adapter.InboundContext, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
||||||
|
/* var wq waiter.Queue
|
||||||
|
ep, err := raw.NewEndpoint(w.stack, ipv4.ProtocolNumber, icmp.ProtocolNumber4, &wq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(gonet.TranslateNetstackError(err), "create endpoint")
|
||||||
|
}
|
||||||
|
err = ep.Connect(tcpip.FullAddress{
|
||||||
|
NIC: tun.DefaultNIC,
|
||||||
|
Port: metadata.Destination.Port,
|
||||||
|
Addr: tun.AddressFromAddr(metadata.Destination.Addr),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ep.Close()
|
||||||
|
return nil, E.Cause(gonet.TranslateNetstackError(err), "ICMP connect ", metadata.Destination)
|
||||||
|
}
|
||||||
|
fmt.Println("linked ", metadata.Network, " connection to ", metadata.Destination.AddrString())
|
||||||
|
destination := &endpointNatDestination{
|
||||||
|
ep: ep,
|
||||||
|
wq: &wq,
|
||||||
|
context: routeContext,
|
||||||
|
}
|
||||||
|
go destination.loopRead()
|
||||||
|
return destination, nil*/
|
||||||
|
session := tun.DirectRouteSession{
|
||||||
|
Source: metadata.Source.Addr,
|
||||||
|
Destination: metadata.Destination.Addr,
|
||||||
|
}
|
||||||
|
w.mapping.CreateSession(session, routeContext)
|
||||||
|
return &stackNatDestination{
|
||||||
|
device: w,
|
||||||
|
session: session,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type stackNatDestination struct {
|
||||||
|
device *stackDevice
|
||||||
|
session tun.DirectRouteSession
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *stackNatDestination) WritePacket(buffer *buf.Buffer) error {
|
||||||
|
if d.device.writer != nil {
|
||||||
|
d.device.writer.RewritePacket(buffer.Bytes())
|
||||||
|
}
|
||||||
|
d.device.packetOutbound <- buffer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *stackNatDestination) WritePacketBuffer(buffer *stack.PacketBuffer) error {
|
||||||
|
if d.device.writer != nil {
|
||||||
|
d.device.writer.RewritePacketBuffer(buffer)
|
||||||
|
}
|
||||||
|
d.device.outbound <- buffer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *stackNatDestination) Close() error {
|
||||||
|
d.device.mapping.DeleteSession(d.session)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *stackNatDestination) Timeout() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/*type endpointNatDestination struct {
|
||||||
|
ep tcpip.Endpoint
|
||||||
|
wq *waiter.Queue
|
||||||
|
networkProto tcpip.NetworkProtocolNumber
|
||||||
|
context tun.DirectRouteContext
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *endpointNatDestination) loopRead() {
|
||||||
|
for {
|
||||||
|
println("start read")
|
||||||
|
buffer, err := commonRead(d.ep, d.wq, d.done)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println("done read")
|
||||||
|
ipHdr := header.IPv4(buffer.Bytes())
|
||||||
|
if ipHdr.TransportProtocol() != header.ICMPv4ProtocolNumber {
|
||||||
|
buffer.Release()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
icmpHdr := header.ICMPv4(ipHdr.Payload())
|
||||||
|
if icmpHdr.Type() != header.ICMPv4EchoReply {
|
||||||
|
buffer.Release()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Println("read echo reply")
|
||||||
|
_ = d.context.WritePacket(ipHdr)
|
||||||
|
buffer.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func commonRead(ep tcpip.Endpoint, wq *waiter.Queue, done chan struct{}) (*buf.Buffer, error) {
|
||||||
|
buffer := buf.NewPacket()
|
||||||
|
result, err := ep.Read(buffer, tcpip.ReadOptions{})
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
||||||
|
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.ReadableEvents)
|
||||||
|
wq.EventRegister(&waitEntry)
|
||||||
|
defer wq.EventUnregister(&waitEntry)
|
||||||
|
for {
|
||||||
|
result, err = ep.Read(buffer, tcpip.ReadOptions{})
|
||||||
|
if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-notifyCh:
|
||||||
|
case <-done:
|
||||||
|
buffer.Release()
|
||||||
|
return nil, context.DeadlineExceeded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, gonet.TranslateNetstackError(err)
|
||||||
|
}
|
||||||
|
buffer.Truncate(result.Count)
|
||||||
|
return buffer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *endpointNatDestination) WritePacket(buffer *buf.Buffer) error {
|
||||||
|
_, err := d.ep.Write(buffer, tcpip.WriteOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return gonet.TranslateNetstackError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *endpointNatDestination) WritePacketBuffer(buffer *stack.PacketBuffer) error {
|
||||||
|
data := buffer.ToView().AsSlice()
|
||||||
|
println("write echo request buffer :" + fmt.Sprint(data))
|
||||||
|
_, err := d.ep.Write(bytes.NewReader(data), tcpip.WriteOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return gonet.TranslateNetstackError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *endpointNatDestination) Close() error {
|
||||||
|
d.ep.Abort()
|
||||||
|
close(d.done)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *endpointNatDestination) Timeout() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
@ -28,16 +28,36 @@ type systemDevice struct {
|
|||||||
batchDevice tun.LinuxTUN
|
batchDevice tun.LinuxTUN
|
||||||
events chan wgTun.Event
|
events chan wgTun.Event
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
|
addr4 netip.Addr
|
||||||
|
addr6 netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSystemDevice(options DeviceOptions) (*systemDevice, error) {
|
func newSystemDevice(options DeviceOptions) (*systemDevice, error) {
|
||||||
if options.Name == "" {
|
if options.Name == "" {
|
||||||
options.Name = tun.CalculateInterfaceName("wg")
|
options.Name = tun.CalculateInterfaceName("wg")
|
||||||
}
|
}
|
||||||
|
var inet4Address netip.Addr
|
||||||
|
var inet6Address netip.Addr
|
||||||
|
if len(options.Address) > 0 {
|
||||||
|
if prefix := common.Find(options.Address, func(it netip.Prefix) bool {
|
||||||
|
return it.Addr().Is4()
|
||||||
|
}); prefix.IsValid() {
|
||||||
|
inet4Address = prefix.Addr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(options.Address) > 0 {
|
||||||
|
if prefix := common.Find(options.Address, func(it netip.Prefix) bool {
|
||||||
|
return it.Addr().Is6()
|
||||||
|
}); prefix.IsValid() {
|
||||||
|
inet6Address = prefix.Addr()
|
||||||
|
}
|
||||||
|
}
|
||||||
return &systemDevice{
|
return &systemDevice{
|
||||||
options: options,
|
options: options,
|
||||||
dialer: options.CreateDialer(options.Name),
|
dialer: options.CreateDialer(options.Name),
|
||||||
events: make(chan wgTun.Event, 1),
|
events: make(chan wgTun.Event, 1),
|
||||||
|
addr4: inet4Address,
|
||||||
|
addr6: inet6Address,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +69,14 @@ func (w *systemDevice) ListenPacket(ctx context.Context, destination M.Socksaddr
|
|||||||
return w.dialer.ListenPacket(ctx, destination)
|
return w.dialer.ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *systemDevice) Inet4Address() netip.Addr {
|
||||||
|
return w.addr4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *systemDevice) Inet6Address() netip.Addr {
|
||||||
|
return w.addr6
|
||||||
|
}
|
||||||
|
|
||||||
func (w *systemDevice) SetDevice(device *device.Device) {
|
func (w *systemDevice) SetDevice(device *device.Device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
@ -29,6 +31,7 @@ type Endpoint struct {
|
|||||||
ipcConf string
|
ipcConf string
|
||||||
allowedAddress []netip.Prefix
|
allowedAddress []netip.Prefix
|
||||||
tunDevice Device
|
tunDevice Device
|
||||||
|
natDevice NatDevice
|
||||||
device *device.Device
|
device *device.Device
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
pauseCallback *list.Element[pause.Callback]
|
pauseCallback *list.Element[pause.Callback]
|
||||||
@ -111,12 +114,17 @@ func NewEndpoint(options EndpointOptions) (*Endpoint, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "create WireGuard device")
|
return nil, E.Cause(err, "create WireGuard device")
|
||||||
}
|
}
|
||||||
|
natDevice, isNatDevice := tunDevice.(NatDevice)
|
||||||
|
if !isNatDevice {
|
||||||
|
natDevice = NewNATDevice(tunDevice, true)
|
||||||
|
}
|
||||||
return &Endpoint{
|
return &Endpoint{
|
||||||
options: options,
|
options: options,
|
||||||
peers: peers,
|
peers: peers,
|
||||||
ipcConf: ipcConf,
|
ipcConf: ipcConf,
|
||||||
allowedAddress: allowedAddresses,
|
allowedAddress: allowedAddresses,
|
||||||
tunDevice: tunDevice,
|
tunDevice: tunDevice,
|
||||||
|
natDevice: natDevice,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +184,13 @@ func (e *Endpoint) Start(resolve bool) error {
|
|||||||
e.options.Logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
|
e.options.Logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
wgDevice := device.NewDevice(e.options.Context, e.tunDevice, bind, logger, e.options.Workers)
|
var deviceInput Device
|
||||||
|
if e.natDevice != nil {
|
||||||
|
deviceInput = e.natDevice
|
||||||
|
} else {
|
||||||
|
deviceInput = e.tunDevice
|
||||||
|
}
|
||||||
|
wgDevice := device.NewDevice(e.options.Context, deviceInput, bind, logger, e.options.Workers)
|
||||||
e.tunDevice.SetDevice(wgDevice)
|
e.tunDevice.SetDevice(wgDevice)
|
||||||
ipcConf := e.ipcConf
|
ipcConf := e.ipcConf
|
||||||
for _, peer := range e.peers {
|
for _, peer := range e.peers {
|
||||||
@ -194,6 +208,20 @@ func (e *Endpoint) Start(resolve bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Endpoint) Close() error {
|
||||||
|
if e.device != nil {
|
||||||
|
e.device.Close()
|
||||||
|
}
|
||||||
|
if e.pauseCallback != nil {
|
||||||
|
e.pauseManager.UnregisterCallback(e.pauseCallback)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Endpoint) BindUpdate() error {
|
||||||
|
return e.device.BindUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Endpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (e *Endpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
if !destination.Addr.IsValid() {
|
if !destination.Addr.IsValid() {
|
||||||
return nil, E.Cause(os.ErrInvalid, "invalid non-IP destination")
|
return nil, E.Cause(os.ErrInvalid, "invalid non-IP destination")
|
||||||
@ -208,18 +236,11 @@ func (e *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
|
|||||||
return e.tunDevice.ListenPacket(ctx, destination)
|
return e.tunDevice.ListenPacket(ctx, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) BindUpdate() error {
|
func (e *Endpoint) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext) (tun.DirectRouteDestination, error) {
|
||||||
return e.device.BindUpdate()
|
if e.natDevice == nil {
|
||||||
}
|
return nil, os.ErrInvalid
|
||||||
|
|
||||||
func (e *Endpoint) Close() error {
|
|
||||||
if e.device != nil {
|
|
||||||
e.device.Close()
|
|
||||||
}
|
}
|
||||||
if e.pauseCallback != nil {
|
return e.natDevice.CreateDestination(metadata, routeContext)
|
||||||
e.pauseManager.UnregisterCallback(e.pauseCallback)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) onPauseUpdated(event int) {
|
func (e *Endpoint) onPauseUpdated(event int) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user