Ver código fonte

Add `wifi_ssid` and `wifi_bssid` route and DNS rules

世界 1 ano atrás
pai
commit
253976d6c0

+ 6 - 0
adapter/router.go

@@ -41,6 +41,7 @@ type Router interface {
 	NetworkMonitor() tun.NetworkUpdateMonitor
 	InterfaceMonitor() tun.DefaultInterfaceMonitor
 	PackageManager() tun.PackageManager
+	WIFIState() WIFIState
 	Rules() []Rule
 
 	ClashServer() ClashServer
@@ -78,3 +79,8 @@ type DNSRule interface {
 type InterfaceUpdateListener interface {
 	InterfaceUpdated()
 }
+
+type WIFIState struct {
+	SSID  string
+	BSSID string
+}

+ 10 - 0
experimental/libbox/platform.go

@@ -19,6 +19,7 @@ type PlatformInterface interface {
 	UsePlatformInterfaceGetter() bool
 	GetInterfaces() (NetworkInterfaceIterator, error)
 	UnderNetworkExtension() bool
+	ReadWIFIState() *WIFIState
 	ClearDNSCache()
 }
 
@@ -38,6 +39,15 @@ type NetworkInterface struct {
 	Addresses StringIterator
 }
 
+type WIFIState struct {
+	SSID  string
+	BSSID string
+}
+
+func NewWIFIState(wifiSSID string, wifiBSSID string) *WIFIState {
+	return &WIFIState{wifiSSID, wifiBSSID}
+}
+
 type NetworkInterfaceIterator interface {
 	Next() *NetworkInterface
 	HasNext() bool

+ 1 - 0
experimental/libbox/platform/interface.go

@@ -23,6 +23,7 @@ type Interface interface {
 	Interfaces() ([]NetworkInterface, error)
 	UnderNetworkExtension() bool
 	ClearDNSCache()
+	ReadWIFIState() adapter.WIFIState
 	process.Searcher
 }
 

+ 8 - 0
experimental/libbox/service.go

@@ -210,6 +210,14 @@ func (w *platformInterfaceWrapper) ClearDNSCache() {
 	w.iif.ClearDNSCache()
 }
 
+func (w *platformInterfaceWrapper) ReadWIFIState() adapter.WIFIState {
+	wifiState := w.iif.ReadWIFIState()
+	if wifiState == nil {
+		return adapter.WIFIState{}
+	}
+	return (adapter.WIFIState)(*wifiState)
+}
+
 func (w *platformInterfaceWrapper) DisableColors() bool {
 	return runtime.GOOS != "android"
 }

+ 2 - 0
option/rule.go

@@ -78,6 +78,8 @@ type DefaultRule struct {
 	User            Listable[string] `json:"user,omitempty"`
 	UserID          Listable[int32]  `json:"user_id,omitempty"`
 	ClashMode       string           `json:"clash_mode,omitempty"`
+	WIFISSID        Listable[string] `json:"wifi_ssid,omitempty"`
+	WIFIBSSID       Listable[string] `json:"wifi_bssid,omitempty"`
 	Invert          bool             `json:"invert,omitempty"`
 	Outbound        string           `json:"outbound,omitempty"`
 }

+ 2 - 0
option/rule_dns.go

@@ -78,6 +78,8 @@ type DefaultDNSRule struct {
 	UserID          Listable[int32]        `json:"user_id,omitempty"`
 	Outbound        Listable[string]       `json:"outbound,omitempty"`
 	ClashMode       string                 `json:"clash_mode,omitempty"`
+	WIFISSID        Listable[string]       `json:"wifi_ssid,omitempty"`
+	WIFIBSSID       Listable[string]       `json:"wifi_bssid,omitempty"`
 	Invert          bool                   `json:"invert,omitempty"`
 	Server          string                 `json:"server,omitempty"`
 	DisableCache    bool                   `json:"disable_cache,omitempty"`

+ 26 - 0
route/router.go

@@ -86,6 +86,8 @@ type Router struct {
 	clashServer                        adapter.ClashServer
 	v2rayServer                        adapter.V2RayServer
 	platformInterface                  platform.Interface
+	needWIFIState                      bool
+	wifiState                          adapter.WIFIState
 }
 
 func NewRouter(
@@ -116,6 +118,7 @@ func NewRouter(
 		defaultMark:           options.DefaultMark,
 		pauseManager:          pause.ManagerFromContext(ctx),
 		platformInterface:     platformInterface,
+		needWIFIState:         hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
 	}
 	router.dnsClient = dns.NewClient(dns.ClientOptions{
 		DisableCache:     dnsOptions.DNSClientOptions.DisableCache,
@@ -328,6 +331,11 @@ func NewRouter(
 		service.ContextWith[serviceNTP.TimeService](ctx, timeService)
 		router.timeService = timeService
 	}
+	if platformInterface != nil && router.interfaceMonitor != nil && router.needWIFIState {
+		router.interfaceMonitor.RegisterCallback(func(_ int) {
+			router.updateWIFIState()
+		})
+	}
 	return router, nil
 }
 
@@ -468,6 +476,9 @@ func (r *Router) Start() error {
 		r.geositeCache = nil
 		r.geositeReader = nil
 	}
+	if r.needWIFIState {
+		r.updateWIFIState()
+	}
 	for i, rule := range r.rules {
 		err := rule.Start()
 		if err != nil {
@@ -940,6 +951,10 @@ func (r *Router) Rules() []adapter.Rule {
 	return r.rules
 }
 
+func (r *Router) WIFIState() adapter.WIFIState {
+	return r.wifiState
+}
+
 func (r *Router) NetworkMonitor() tun.NetworkUpdateMonitor {
 	return r.networkMonitor
 }
@@ -1019,3 +1034,14 @@ func (r *Router) ResetNetwork() error {
 	}
 	return nil
 }
+
+func (r *Router) updateWIFIState() {
+	if r.platformInterface == nil {
+		return
+	}
+	state := r.platformInterface.ReadWIFIState()
+	if state != r.wifiState {
+		r.wifiState = state
+		r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
+	}
+}

+ 8 - 0
route/router_geo_resources.go

@@ -307,3 +307,11 @@ func isProcessDNSRule(rule option.DefaultDNSRule) bool {
 func notPrivateNode(code string) bool {
 	return code != "private"
 }
+
+func isWIFIRule(rule option.DefaultRule) bool {
+	return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
+}
+
+func isWIFIDNSRule(rule option.DefaultDNSRule) bool {
+	return len(rule.WIFISSID) > 0 || len(rule.WIFIBSSID) > 0
+}

+ 10 - 0
route/rule_default.go

@@ -184,6 +184,16 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
 		rule.items = append(rule.items, item)
 		rule.allItems = append(rule.allItems, item)
 	}
+	if len(options.WIFISSID) > 0 {
+		item := NewWIFISSIDItem(router, options.WIFISSID)
+		rule.items = append(rule.items, item)
+		rule.allItems = append(rule.allItems, item)
+	}
+	if len(options.WIFIBSSID) > 0 {
+		item := NewWIFIBSSIDItem(router, options.WIFIBSSID)
+		rule.items = append(rule.items, item)
+		rule.allItems = append(rule.allItems, item)
+	}
 	return rule, nil
 }
 

+ 10 - 0
route/rule_dns.go

@@ -180,6 +180,16 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
 		rule.items = append(rule.items, item)
 		rule.allItems = append(rule.allItems, item)
 	}
+	if len(options.WIFISSID) > 0 {
+		item := NewWIFISSIDItem(router, options.WIFISSID)
+		rule.items = append(rule.items, item)
+		rule.allItems = append(rule.allItems, item)
+	}
+	if len(options.WIFIBSSID) > 0 {
+		item := NewWIFIBSSIDItem(router, options.WIFIBSSID)
+		rule.items = append(rule.items, item)
+		rule.allItems = append(rule.allItems, item)
+	}
 	return rule, nil
 }
 

+ 39 - 0
route/rule_item_wifi_bssid.go

@@ -0,0 +1,39 @@
+package route
+
+import (
+	"strings"
+
+	"github.com/sagernet/sing-box/adapter"
+	F "github.com/sagernet/sing/common/format"
+)
+
+var _ RuleItem = (*WIFIBSSIDItem)(nil)
+
+type WIFIBSSIDItem struct {
+	bssidList []string
+	bssidMap  map[string]bool
+	router    adapter.Router
+}
+
+func NewWIFIBSSIDItem(router adapter.Router, bssidList []string) *WIFIBSSIDItem {
+	bssidMap := make(map[string]bool)
+	for _, bssid := range bssidList {
+		bssidMap[bssid] = true
+	}
+	return &WIFIBSSIDItem{
+		bssidList,
+		bssidMap,
+		router,
+	}
+}
+
+func (r *WIFIBSSIDItem) Match(metadata *adapter.InboundContext) bool {
+	return r.bssidMap[r.router.WIFIState().BSSID]
+}
+
+func (r *WIFIBSSIDItem) String() string {
+	if len(r.bssidList) == 1 {
+		return F.ToString("wifi_bssid=", r.bssidList[0])
+	}
+	return F.ToString("wifi_bssid=[", strings.Join(r.bssidList, " "), "]")
+}

+ 39 - 0
route/rule_item_wifi_ssid.go

@@ -0,0 +1,39 @@
+package route
+
+import (
+	"strings"
+
+	"github.com/sagernet/sing-box/adapter"
+	F "github.com/sagernet/sing/common/format"
+)
+
+var _ RuleItem = (*WIFISSIDItem)(nil)
+
+type WIFISSIDItem struct {
+	ssidList []string
+	ssidMap  map[string]bool
+	router   adapter.Router
+}
+
+func NewWIFISSIDItem(router adapter.Router, ssidList []string) *WIFISSIDItem {
+	ssidMap := make(map[string]bool)
+	for _, ssid := range ssidList {
+		ssidMap[ssid] = true
+	}
+	return &WIFISSIDItem{
+		ssidList,
+		ssidMap,
+		router,
+	}
+}
+
+func (r *WIFISSIDItem) Match(metadata *adapter.InboundContext) bool {
+	return r.ssidMap[r.router.WIFIState().SSID]
+}
+
+func (r *WIFISSIDItem) String() string {
+	if len(r.ssidList) == 1 {
+		return F.ToString("wifi_ssid=", r.ssidList[0])
+	}
+	return F.ToString("wifi_ssid=[", strings.Join(r.ssidList, " "), "]")
+}