router: ability to add/remove outbounds

This commit is contained in:
jebbs 2022-10-14 14:17:47 +08:00
parent 6591dd58ca
commit 385475a6f6
3 changed files with 102 additions and 18 deletions

View File

@ -6,8 +6,8 @@ import (
"net/netip" "net/netip"
"github.com/sagernet/sing-box/common/geoip" "github.com/sagernet/sing-box/common/geoip"
"github.com/sagernet/sing-dns" dns "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun" tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
N "github.com/sagernet/sing/common/network" N "github.com/sagernet/sing/common/network"
@ -19,6 +19,8 @@ type Router interface {
Outbounds() []Outbound Outbounds() []Outbound
Outbound(tag string) (Outbound, bool) Outbound(tag string) (Outbound, bool)
AddOutbound(Outbound)
RemoveOutbound(string)
DefaultOutbound(network string) Outbound DefaultOutbound(network string) Outbound
RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error

View File

@ -24,9 +24,9 @@ import (
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"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-dns" dns "github.com/sagernet/sing-dns"
"github.com/sagernet/sing-tun" tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing-vmess" 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"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
@ -67,8 +67,7 @@ type Router struct {
logger log.ContextLogger logger log.ContextLogger
dnsLogger log.ContextLogger dnsLogger log.ContextLogger
inboundByTag map[string]adapter.Inbound inboundByTag map[string]adapter.Inbound
outbounds []adapter.Outbound outbounds *outboundsManager
outboundByTag map[string]adapter.Outbound
rules []adapter.Rule rules []adapter.Rule
defaultDetour string defaultDetour string
defaultOutboundForConnection adapter.Outbound defaultOutboundForConnection adapter.Outbound
@ -114,7 +113,7 @@ func NewRouter(ctx context.Context, logger log.ContextLogger, dnsLogger log.Cont
ctx: ctx, ctx: ctx,
logger: logger, logger: logger,
dnsLogger: dnsLogger, dnsLogger: dnsLogger,
outboundByTag: make(map[string]adapter.Outbound), outbounds: newOutboundsManager(),
rules: make([]adapter.Rule, 0, len(options.Rules)), rules: make([]adapter.Rule, 0, len(options.Rules)),
dnsRules: make([]adapter.DNSRule, 0, len(dnsOptions.Rules)), dnsRules: make([]adapter.DNSRule, 0, len(dnsOptions.Rules)),
needGeoIPDatabase: hasRule(options.Rules, isGeoIPRule) || hasDNSRule(dnsOptions.Rules, isGeoIPDNSRule), needGeoIPDatabase: hasRule(options.Rules, isGeoIPRule) || hasDNSRule(dnsOptions.Rules, isGeoIPDNSRule),
@ -311,14 +310,13 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
for _, inbound := range inbounds { for _, inbound := range inbounds {
inboundByTag[inbound.Tag()] = inbound inboundByTag[inbound.Tag()] = inbound
} }
outboundByTag := make(map[string]adapter.Outbound)
for _, detour := range outbounds { for _, detour := range outbounds {
outboundByTag[detour.Tag()] = detour r.outbounds.Add(detour)
} }
var defaultOutboundForConnection adapter.Outbound var defaultOutboundForConnection adapter.Outbound
var defaultOutboundForPacketConnection adapter.Outbound var defaultOutboundForPacketConnection adapter.Outbound
if r.defaultDetour != "" { if r.defaultDetour != "" {
detour, loaded := outboundByTag[r.defaultDetour] detour, loaded := r.outbounds.Get(r.defaultDetour)
if !loaded { if !loaded {
return E.New("default detour not found: ", r.defaultDetour) return E.New("default detour not found: ", r.defaultDetour)
} }
@ -357,7 +355,7 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
defaultOutboundForPacketConnection = detour defaultOutboundForPacketConnection = detour
} }
outbounds = append(outbounds, detour) outbounds = append(outbounds, detour)
outboundByTag[detour.Tag()] = detour r.outbounds.Add(detour)
} }
if defaultOutboundForConnection != defaultOutboundForPacketConnection { if defaultOutboundForConnection != defaultOutboundForPacketConnection {
var description string var description string
@ -376,12 +374,10 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
r.logger.Info("using ", defaultOutboundForPacketConnection.Type(), "[", packetDescription, "] as default outbound for packet connection") r.logger.Info("using ", defaultOutboundForPacketConnection.Type(), "[", packetDescription, "] as default outbound for packet connection")
} }
r.inboundByTag = inboundByTag r.inboundByTag = inboundByTag
r.outbounds = outbounds
r.defaultOutboundForConnection = defaultOutboundForConnection r.defaultOutboundForConnection = defaultOutboundForConnection
r.defaultOutboundForPacketConnection = defaultOutboundForPacketConnection r.defaultOutboundForPacketConnection = defaultOutboundForPacketConnection
r.outboundByTag = outboundByTag
for i, rule := range r.rules { for i, rule := range r.rules {
if _, loaded := outboundByTag[rule.Outbound()]; !loaded { if _, loaded := r.outbounds.Get(rule.Outbound()); !loaded {
return E.New("outbound not found for rule[", i, "]: ", rule.Outbound()) return E.New("outbound not found for rule[", i, "]: ", rule.Outbound())
} }
} }
@ -389,7 +385,7 @@ func (r *Router) Initialize(inbounds []adapter.Inbound, outbounds []adapter.Outb
} }
func (r *Router) Outbounds() []adapter.Outbound { func (r *Router) Outbounds() []adapter.Outbound {
return r.outbounds return r.outbounds.All()
} }
func (r *Router) Start() error { func (r *Router) Start() error {
@ -499,10 +495,15 @@ func (r *Router) LoadGeosite(code string) (adapter.Rule, error) {
r.geositeCache[code] = rule r.geositeCache[code] = rule
return rule, nil return rule, nil
} }
func (r *Router) AddOutbound(o adapter.Outbound) {
r.outbounds.Add(o)
}
func (r *Router) RemoveOutbound(tag string) {
r.outbounds.Remove(tag)
}
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) { func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
outbound, loaded := r.outboundByTag[tag] return r.outbounds.Get(tag)
return outbound, loaded
} }
func (r *Router) DefaultOutbound(network string) adapter.Outbound { func (r *Router) DefaultOutbound(network string) adapter.Outbound {

81
route/router_outbounds.go Normal file
View File

@ -0,0 +1,81 @@
package route
import (
"sync"
"github.com/sagernet/sing-box/adapter"
)
// outboundsManager is the thread-safe outbound manager.
type outboundsManager struct {
sync.RWMutex
tags []string // tags keeps the order of outbounds
all map[string]adapter.Outbound
}
func newOutboundsManager() *outboundsManager {
return &outboundsManager{
all: make(map[string]adapter.Outbound),
}
}
func (o *outboundsManager) Add(outbound adapter.Outbound) {
o.Lock()
defer o.Unlock()
tag := outbound.Tag()
if _, ok := o.all[tag]; ok {
// update and return
o.all[tag] = outbound
return
}
o.all[tag] = outbound
o.tags = append(o.tags, tag)
}
func (o *outboundsManager) Remove(tag string) {
o.Lock()
defer o.Unlock()
if _, ok := o.all[tag]; !ok {
return
}
delete(o.all, tag)
o.tags = findDeleteElement(o.tags, tag)
}
func (o *outboundsManager) Get(tag string) (adapter.Outbound, bool) {
o.RLock()
defer o.RUnlock()
outbound, ok := o.all[tag]
return outbound, ok
}
func (o *outboundsManager) All() []adapter.Outbound {
o.RLock()
defer o.RUnlock()
all := make([]adapter.Outbound, 0, len(o.tags))
for _, tag := range o.tags {
all = append(all, o.all[tag])
}
return all
}
func findDeleteElement[T comparable](slice []T, element T) []T {
idx := -1
for i := 0; i < len(slice); i++ {
if slice[i] == element {
idx = i
break
}
}
if idx < 0 {
return slice
}
copy(slice[idx:], slice[idx+1:])
return slice[:len(slice)-1]
}