Refactor endpoint listen
This commit is contained in:
@@ -381,8 +381,8 @@ func (d *DefaultDialer) ListenSerialInterfacePacket(ctx context.Context, destina
|
||||
return d.trackPacketConn(packetConn, nil)
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) WireGuardControl() control.Func {
|
||||
return d.udpListener.Control
|
||||
func (d *DefaultDialer) WireGuardControl() (control.Func, bool) {
|
||||
return d.udpListener.Control, d.autoDetectBindFunc != nil && d.netns == ""
|
||||
}
|
||||
|
||||
func (d *DefaultDialer) trackConn(conn net.Conn, err error) (net.Conn, error) {
|
||||
|
||||
@@ -5,5 +5,5 @@ import (
|
||||
)
|
||||
|
||||
type WireGuardListener interface {
|
||||
WireGuardControl() control.Func
|
||||
WireGuardControl() (control.Func, bool)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ require (
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
|
||||
github.com/sagernet/nftables v0.3.0-mod.4
|
||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4
|
||||
github.com/sagernet/sing v0.8.12-0.20260716111929-074fc9988b34
|
||||
github.com/sagernet/sing v0.8.12-0.20260717023913-84ab32b56cb8
|
||||
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3
|
||||
github.com/sagernet/sing-mux v0.3.5
|
||||
github.com/sagernet/sing-quic v0.6.4-0.20260709034545-e23afe1172dc
|
||||
@@ -51,12 +51,12 @@ require (
|
||||
github.com/sagernet/sing-shadowsocks2 v0.2.1
|
||||
github.com/sagernet/sing-shadowtls v0.2.1
|
||||
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260716125804-79ea1ac88855
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d
|
||||
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb
|
||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1
|
||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1
|
||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7.0.20260711004045-7d3e867147ad
|
||||
github.com/sagernet/wireguard-go v0.0.5-0.20260706153856-2c27bbf4f97f
|
||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7.0.20260717024045-1edfbb9ee544
|
||||
github.com/sagernet/wireguard-go v0.0.5-0.20260717024847-6f5e8b1947ae
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
|
||||
@@ -270,8 +270,8 @@ github.com/sagernet/nftables v0.3.0-mod.4 h1:vnOtcDYeSXv2e5RoRuGH0lrpttQFJ8iC4IC
|
||||
github.com/sagernet/nftables v0.3.0-mod.4/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
|
||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o=
|
||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
||||
github.com/sagernet/sing v0.8.12-0.20260716111929-074fc9988b34 h1:rgSs2ttiz8EaubsOt0SkzsqciY0m0PRp3w/fOisPoNo=
|
||||
github.com/sagernet/sing v0.8.12-0.20260716111929-074fc9988b34/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA=
|
||||
github.com/sagernet/sing v0.8.12-0.20260717023913-84ab32b56cb8 h1:dyRIj+MZ2rc9JVzJoG04jxu+MpvHrLIZLJr0QjNAMGg=
|
||||
github.com/sagernet/sing v0.8.12-0.20260717023913-84ab32b56cb8/go.mod h1:olXxWQNqRW/l2Q6JI3b2Qmz8iQnIFlOeeH8bx6JhgUA=
|
||||
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3 h1:3y6++yIa8XlDhxPkpR4p+7RUHVY2KTP9CPIGnWmOlO8=
|
||||
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3/go.mod h1:XEqEDYRCAYLaoPjZ1ifVWJg5iWAJHL2gOAXe/PM28Cg=
|
||||
github.com/sagernet/sing-mux v0.3.5 h1:RHnhVEc+SFqkrK4xMygYjDwwLhzp2Bj3lztSukONfhI=
|
||||
@@ -286,18 +286,18 @@ github.com/sagernet/sing-shadowtls v0.2.1 h1:ZiHZdnEnP+YS73NMsxiZmIFCwNd0M4k7PkG
|
||||
github.com/sagernet/sing-shadowtls v0.2.1/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
|
||||
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb h1:VvU2/PZqP5tbKTDq0BxkhRO8ZnKI4UJzziakgBiP2Qg=
|
||||
github.com/sagernet/sing-snell v0.0.0-20260710094516-a4e97ee24beb/go.mod h1:PcwzX/Xvqky0EP3kGt8OCjYb3R1pydenPHNQZcPZmXY=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260716125804-79ea1ac88855 h1:zP2Bb6NLl3/zV24mmBjcpWIdvOKTiMo2SS0eJ8WpYFU=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260716125804-79ea1ac88855/go.mod h1:SF7OPVtqIYY14T4KmnoHND5CFiMSUCTYxzVOT3nZUuc=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d h1:UXUGxGTwotY+R+QkRGvhj/N148SDnwMh/d9Hefn5X7Y=
|
||||
github.com/sagernet/sing-tun v0.8.12-0.20260717024008-39eed1f6361d/go.mod h1:F/gRq5VX1WN/OZtsvbN2JjXXuNl2ATJglHMSk1/iN9U=
|
||||
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb h1:KEMbfexD4DvrQGYWwx6r+AwH9Veh8z6cnBZmtCS2G+0=
|
||||
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb/go.mod h1:D4CnJX3MNAAANhbQUxfIRgBdnvlTEaV7h6ojedcs+pw=
|
||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
|
||||
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
|
||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
|
||||
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
|
||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7.0.20260711004045-7d3e867147ad h1:WfqPcs43BJINn2IjQd10cLdd3lvJSanbk7MxGk6RTcA=
|
||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7.0.20260711004045-7d3e867147ad/go.mod h1:p8Ms8FbGlwQJyHb862XmdShTS50fFJ8C71VdO6xvWyk=
|
||||
github.com/sagernet/wireguard-go v0.0.5-0.20260706153856-2c27bbf4f97f h1:TzN97RL07xWb3gZtmqFhsdkud4f6G/pohiaOLiqSBj4=
|
||||
github.com/sagernet/wireguard-go v0.0.5-0.20260706153856-2c27bbf4f97f/go.mod h1:hEqi4y5czEg6LYtX2Bpjg+lV0b/J1n+5rA885Z66Mx0=
|
||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7.0.20260717024045-1edfbb9ee544 h1:j2tab0dGHutfclhwZxrkSDMXwGXtozIo5BV4DgwS+1Q=
|
||||
github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.7.0.20260717024045-1edfbb9ee544/go.mod h1:p8Ms8FbGlwQJyHb862XmdShTS50fFJ8C71VdO6xvWyk=
|
||||
github.com/sagernet/wireguard-go v0.0.5-0.20260717024847-6f5e8b1947ae h1:GmxlXWnRmeNfPE1tWXRZIFgKJd5BH5okoDHKZkkI5bw=
|
||||
github.com/sagernet/wireguard-go v0.0.5-0.20260717024847-6f5e8b1947ae/go.mod h1:hEqi4y5czEg6LYtX2Bpjg+lV0b/J1n+5rA885Z66Mx0=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
|
||||
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -339,22 +338,58 @@ func (t *Endpoint) start() error {
|
||||
t.systemDialer = systemDialer
|
||||
t.server.TunDevice = wgTunDevice
|
||||
}
|
||||
if mark := t.network.AutoRedirectOutputMark(); mark > 0 {
|
||||
controlFunc := t.network.AutoRedirectOutputMarkFunc()
|
||||
if bindFunc := t.network.AutoDetectInterfaceFunc(); bindFunc != nil {
|
||||
controlFunc = control.Append(controlFunc, bindFunc)
|
||||
}
|
||||
netns.SetControlFunc(controlFunc)
|
||||
} else if runtime.GOOS == "android" && t.platformInterface != nil && t.platformInterface.UsePlatformAutoDetectInterfaceControl() {
|
||||
netns.SetControlFunc(func(network, address string, c syscall.RawConn) error {
|
||||
return control.Raw(c, func(fd uintptr) error {
|
||||
return t.platformInterface.AutoDetectInterfaceControl(int(fd))
|
||||
if t.network.AutoRedirectOutputMark() != 0 {
|
||||
netns.SetControlFunc(t.network.AutoRedirectOutputMarkFunc())
|
||||
} else if t.platformInterface != nil && t.platformInterface.UsePlatformNetworkInterfaces() {
|
||||
if t.platformInterface.UsePlatformAutoDetectInterfaceControl() {
|
||||
netns.SetControlFunc(func(network, address string, conn syscall.RawConn) error {
|
||||
return control.Raw(conn, func(fileDescriptor uintptr) error {
|
||||
return t.platformInterface.AutoDetectInterfaceControl(int(fileDescriptor))
|
||||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// NEPacketTunnelProvider sockets are excluded from tunnel routes by
|
||||
// NECP; the empty override only suppresses tailscale's own
|
||||
// default-interface bind, which would select the sing-box utun.
|
||||
netns.SetControlFunc(func(string, string, syscall.RawConn) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
} else {
|
||||
bindFunc := t.network.AutoDetectInterfaceFunc()
|
||||
if bindFunc != nil {
|
||||
netns.SetControlFunc(bindFunc)
|
||||
netns.SetListenPacketFunc(t.listenPacket)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) listenPacket(ctx context.Context, network string, address string) (nettype.PacketConn, error) {
|
||||
listenConfig := net.ListenConfig{
|
||||
Control: control.Append(t.network.AutoDetectInterfaceFunc(), control.DisableUDPNetReset()),
|
||||
}
|
||||
packetConn, err := listenConfig.ListenPacket(ctx, network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
udpConn := packetConn.(*net.UDPConn)
|
||||
egressPool := tun.NewUDPEgressPool(tun.UDPEgressPoolOptions{
|
||||
Logger: t.logger,
|
||||
Network: network,
|
||||
InterfaceFinder: t.network.InterfaceFinder(),
|
||||
InterfaceMonitor: t.network.InterfaceMonitor(),
|
||||
IsExempt: func() bool {
|
||||
return t.network.AutoRedirectOutputMark() != 0
|
||||
},
|
||||
})
|
||||
if !egressPool.SetEgressPort(udpConn.LocalAddr().(*net.UDPAddr).AddrPort().Port()) {
|
||||
egressPool.Close()
|
||||
return udpConn, nil
|
||||
}
|
||||
return tun.NewUDPEgressConn(udpConn, egressPool), nil
|
||||
}
|
||||
|
||||
func (t *Endpoint) postStart() error {
|
||||
err := t.server.Start()
|
||||
if err != nil {
|
||||
@@ -649,6 +684,7 @@ func (t *Endpoint) Close() error {
|
||||
}
|
||||
netmon.RegisterInterfaceGetter(nil)
|
||||
netns.SetControlFunc(nil)
|
||||
netns.SetListenPacketFunc(nil)
|
||||
if t.fallbackTCPCloser != nil {
|
||||
t.fallbackTCPCloser()
|
||||
t.fallbackTCPCloser = nil
|
||||
|
||||
@@ -74,6 +74,23 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
udpTimeout = C.UDPTimeout
|
||||
}
|
||||
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
||||
var egressPool *tun.UDPEgressPool
|
||||
wireGuardListener, isWireGuardListener := common.Cast[dialer.WireGuardListener](outboundDialer)
|
||||
if isWireGuardListener {
|
||||
anchorControl, egressEnabled := wireGuardListener.WireGuardControl()
|
||||
if egressEnabled {
|
||||
egressPool = tun.NewUDPEgressPool(tun.UDPEgressPoolOptions{
|
||||
Logger: logger,
|
||||
Control: anchorControl,
|
||||
InterfaceFinder: networkManager.InterfaceFinder(),
|
||||
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
||||
ExcludeInterface: options.Name,
|
||||
IsExempt: func() bool {
|
||||
return networkManager.AutoRedirectOutputMark() != 0
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
wgEndpoint, err := wireguard.NewEndpoint(wireguard.EndpointOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
@@ -85,6 +102,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
|
||||
UDPFiltering: tun.NATFiltering(options.UDPFiltering),
|
||||
UDPNATMax: options.UDPNATMax,
|
||||
InterfaceFinder: networkManager.InterfaceFinder(),
|
||||
EgressPool: egressPool,
|
||||
Dialer: outboundDialer,
|
||||
CreateDialer: func(interfaceName string) N.Dialer {
|
||||
return common.Must1(dialer.NewDefault(ctx, option.DialerOptions{
|
||||
|
||||
+7
-1
@@ -353,7 +353,7 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
|
||||
if r.interfaceMonitor == nil {
|
||||
return nil
|
||||
}
|
||||
return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
||||
bindFunc := control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||
if remoteAddr.IsValid() {
|
||||
iif, err := r.interfaceFinder.ByAddr(remoteAddr)
|
||||
@@ -367,6 +367,12 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
|
||||
}
|
||||
return defaultInterface.Name, defaultInterface.Index, nil
|
||||
})
|
||||
return func(network, address string, conn syscall.RawConn) error {
|
||||
if r.autoRedirectOutputMark != 0 {
|
||||
return nil
|
||||
}
|
||||
return bindFunc(network, address, conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,13 +152,17 @@ func (e *Endpoint) Start(resolve bool) error {
|
||||
return nil
|
||||
}
|
||||
var bind conn.Bind
|
||||
wgListener, isWgListener := common.Cast[dialer.WireGuardListener](e.options.Dialer)
|
||||
if isWgListener {
|
||||
stdBind := conn.NewStdNetBind(wgListener.WireGuardControl())
|
||||
wireGuardListener, isWireGuardListener := common.Cast[dialer.WireGuardListener](e.options.Dialer)
|
||||
if isWireGuardListener {
|
||||
wireGuardControl, _ := wireGuardListener.WireGuardControl()
|
||||
standardBind := conn.NewStdNetBind(wireGuardControl).(*conn.StdNetBind)
|
||||
if e.options.ListenPort == 0 && len(e.peers) == 1 && e.peers[0].endpoint.IsValid() {
|
||||
stdBind.(*conn.StdNetBind).SetSinglePeerMode()
|
||||
standardBind.SetSinglePeerMode()
|
||||
}
|
||||
bind = stdBind
|
||||
if e.options.EgressPool != nil {
|
||||
standardBind.SetEgressProvider(e.options.EgressPool)
|
||||
}
|
||||
bind = standardBind
|
||||
} else {
|
||||
var (
|
||||
isConnect bool
|
||||
@@ -172,7 +176,7 @@ func (e *Endpoint) Start(resolve bool) error {
|
||||
}
|
||||
bind = NewClientBind(e.options.Context, e.options.Logger, e.options.Dialer, isConnect, connectAddr, reserved)
|
||||
}
|
||||
if isWgListener || len(e.peers) > 1 {
|
||||
if isWireGuardListener || len(e.peers) > 1 {
|
||||
for _, peer := range e.peers {
|
||||
if peer.reserved != [3]uint8{} {
|
||||
bind.SetReservedForEndpoint(peer.endpoint, peer.reserved)
|
||||
@@ -231,6 +235,9 @@ func (e *Endpoint) Close() error {
|
||||
e.pause.UnregisterCallback(e.pauseCallback)
|
||||
e.pauseCallback = nil
|
||||
}
|
||||
if e.options.EgressPool != nil {
|
||||
e.options.EgressPool.Close()
|
||||
}
|
||||
if e.device != nil {
|
||||
e.device.Down()
|
||||
e.device.Close()
|
||||
|
||||
@@ -24,6 +24,7 @@ type EndpointOptions struct {
|
||||
UDPNATMax uint32
|
||||
|
||||
InterfaceFinder control.InterfaceFinder
|
||||
EgressPool *tun.UDPEgressPool
|
||||
Dialer N.Dialer
|
||||
CreateDialer func(interfaceName string) N.Dialer
|
||||
Name string
|
||||
|
||||
Reference in New Issue
Block a user