Add preferred_by route rule item

This commit is contained in:
世界
2025-08-15 12:45:06 +08:00
parent 5be1887f92
commit 239e6ec701
9 changed files with 194 additions and 19 deletions

View File

@@ -7,7 +7,6 @@ import (
"net/netip"
"net/url"
"os"
"reflect"
"strings"
"sync"
@@ -47,8 +46,6 @@ type DNSTransport struct {
acceptDefaultResolvers bool
dnsRouter adapter.DNSRouter
endpointManager adapter.EndpointManager
cfg *wgcfg.Config
dnsCfg *nDNS.Config
endpoint *Endpoint
routePrefixes []netip.Prefix
routes map[string][]adapter.DNSTransport
@@ -83,10 +80,10 @@ func (t *DNSTransport) Start(stage adapter.StartStage) error {
if !isTailscale {
return E.New("endpoint is not Tailscale: ", t.endpointTag)
}
if ep.onReconfig != nil {
if ep.onReconfigHook != nil {
return E.New("only one Tailscale DNS server is allowed for single endpoint")
}
ep.onReconfig = t.onReconfig
ep.onReconfigHook = t.onReconfig
t.endpoint = ep
return nil
}
@@ -95,14 +92,6 @@ func (t *DNSTransport) Reset() {
}
func (t *DNSTransport) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *nDNS.Config) {
if cfg == nil || dnsCfg == nil {
return
}
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
return
}
t.cfg = cfg
t.dnsCfg = dnsCfg
err := t.updateDNSServers(routerCfg, dnsCfg)
if err != nil {
t.logger.Error(E.Cause(err, "update DNS servers"))

View File

@@ -10,6 +10,7 @@ import (
"net/url"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"sync/atomic"
@@ -50,8 +51,14 @@ import (
"github.com/sagernet/tailscale/version"
"github.com/sagernet/tailscale/wgengine"
"github.com/sagernet/tailscale/wgengine/filter"
"github.com/sagernet/tailscale/wgengine/router"
"github.com/sagernet/tailscale/wgengine/wgcfg"
"go4.org/netipx"
)
var _ adapter.OutboundWithPreferredRoutes = (*Endpoint)(nil)
func init() {
version.SetVersion("sing-box " + C.Version)
}
@@ -71,7 +78,12 @@ type Endpoint struct {
server *tsnet.Server
stack *stack.Stack
filter *atomic.Pointer[filter.Filter]
onReconfig wgengine.ReconfigListener
onReconfigHook wgengine.ReconfigListener
cfg *wgcfg.Config
dnsCfg *tsDNS.Config
routeDomains common.TypedValue[map[string]bool]
routePrefixes atomic.Pointer[netipx.IPSet]
acceptRoutes bool
exitNode string
@@ -218,9 +230,7 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
if err != nil {
return err
}
if t.onReconfig != nil {
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
}
t.server.ExportLocalBackend().ExportEngine().(wgengine.ExportedUserspaceEngine).SetOnReconfigListener(t.onReconfig)
ipStack := t.server.ExportNetstack().ExportIPStack()
gErr := ipStack.SetSpoofing(tun.DefaultNIC, true)
@@ -256,7 +266,6 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
return E.Cause(err, "update prefs")
}
t.filter = localBackend.ExportFilter()
go t.watchState()
return nil
}
@@ -491,10 +500,58 @@ func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
t.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
}
func (t *Endpoint) PreferredDomain(domain string) bool {
routeDomains := t.routeDomains.Load()
if routeDomains == nil {
return false
}
return routeDomains[strings.ToLower(domain)]
}
func (t *Endpoint) PreferredAddress(address netip.Addr) bool {
routePrefixes := t.routePrefixes.Load()
if routePrefixes == nil {
return false
}
return routePrefixes.Contains(address)
}
func (t *Endpoint) Server() *tsnet.Server {
return t.server
}
func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCfg *tsDNS.Config) {
if cfg == nil || dnsCfg == nil {
return
}
if (t.cfg != nil && reflect.DeepEqual(t.cfg, cfg)) && (t.dnsCfg != nil && reflect.DeepEqual(t.dnsCfg, dnsCfg)) {
return
}
t.cfg = cfg
t.dnsCfg = dnsCfg
routeDomains := make(map[string]bool)
for fqdn := range dnsCfg.Routes {
routeDomains[fqdn.WithoutTrailingDot()] = true
}
for _, fqdn := range dnsCfg.SearchDomains {
routeDomains[fqdn.WithoutTrailingDot()] = true
}
t.routeDomains.Store(routeDomains)
var builder netipx.IPSetBuilder
for _, peer := range cfg.Peers {
for _, allowedIP := range peer.AllowedIPs {
builder.AddPrefix(allowedIP)
}
}
t.routePrefixes.Store(common.Must1(builder.IPSet()))
if t.onReconfigHook != nil {
t.onReconfigHook(cfg, routerCfg, dnsCfg)
}
}
func addressFromAddr(destination netip.Addr) tcpip.Address {
if destination.Is6() {
return tcpip.AddrFrom16(destination.As16())

View File

@@ -22,6 +22,8 @@ import (
"github.com/sagernet/sing/service"
)
var _ adapter.OutboundWithPreferredRoutes = (*Endpoint)(nil)
func RegisterEndpoint(registry *endpoint.Registry) {
endpoint.Register[option.WireGuardEndpointOptions](registry, C.TypeWireGuard, NewEndpoint)
}
@@ -210,3 +212,11 @@ func (w *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
}
return w.endpoint.ListenPacket(ctx, destination)
}
func (w *Endpoint) PreferredDomain(domain string) bool {
return false
}
func (w *Endpoint) PreferredAddress(address netip.Addr) bool {
return w.endpoint.Lookup(address) != nil
}

View File

@@ -21,6 +21,8 @@ import (
"github.com/sagernet/sing/service"
)
var _ adapter.OutboundWithPreferredRoutes = (*Outbound)(nil)
func RegisterOutbound(registry *outbound.Registry) {
outbound.Register[option.LegacyWireGuardOutboundOptions](registry, C.TypeWireGuard, NewOutbound)
}
@@ -158,3 +160,11 @@ func (o *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
}
return o.endpoint.ListenPacket(ctx, destination)
}
func (o *Outbound) PreferredDomain(domain string) bool {
return false
}
func (o *Outbound) PreferredAddress(address netip.Addr) bool {
return o.endpoint.Lookup(address) != nil
}