mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-08-15 14:57:36 +08:00
Add interface address rule items
This commit is contained in:
parent
37b4f48a74
commit
aabc60a7ed
@ -6,8 +6,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/route/rule"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@ -69,7 +71,7 @@ func compileRuleSet(sourcePath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = srs.Write(outputFile, plainRuleSet.Options, plainRuleSet.Version)
|
||||
err = srs.Write(outputFile, plainRuleSet.Options, downgradeRuleSetVersion(plainRuleSet.Version, plainRuleSet.Options))
|
||||
if err != nil {
|
||||
outputFile.Close()
|
||||
os.Remove(outputPath)
|
||||
@ -78,3 +80,18 @@ func compileRuleSet(sourcePath string) error {
|
||||
outputFile.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func downgradeRuleSetVersion(version uint8, options option.PlainRuleSet) uint8 {
|
||||
if version == C.RuleSetVersion4 && !rule.HasHeadlessRule(options.Rules, func(rule option.DefaultHeadlessRule) bool {
|
||||
return rule.NetworkInterfaceAddress != nil && rule.NetworkInterfaceAddress.Size() > 0 ||
|
||||
len(rule.DefaultInterfaceAddress) > 0
|
||||
}) {
|
||||
version = C.RuleSetVersion3
|
||||
}
|
||||
if version == C.RuleSetVersion3 && !rule.HasHeadlessRule(options.Rules, func(rule option.DefaultHeadlessRule) bool {
|
||||
return len(rule.NetworkType) > 0 || rule.NetworkIsExpensive || rule.NetworkIsConstrained
|
||||
}) {
|
||||
version = C.RuleSetVersion2
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/domain"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
|
||||
"go4.org/netipx"
|
||||
@ -41,6 +43,8 @@ const (
|
||||
ruleItemNetworkType
|
||||
ruleItemNetworkIsExpensive
|
||||
ruleItemNetworkIsConstrained
|
||||
ruleItemNetworkInterfaceAddress
|
||||
ruleItemDefaultInterfaceAddress
|
||||
ruleItemFinal uint8 = 0xFF
|
||||
)
|
||||
|
||||
@ -230,6 +234,51 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
|
||||
rule.NetworkIsExpensive = true
|
||||
case ruleItemNetworkIsConstrained:
|
||||
rule.NetworkIsConstrained = true
|
||||
case ruleItemNetworkInterfaceAddress:
|
||||
rule.NetworkInterfaceAddress = new(badjson.TypedMap[option.InterfaceType, badoption.Listable[badoption.Prefixable]])
|
||||
var size uint64
|
||||
size, err = binary.ReadUvarint(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := uint64(0); i < size; i++ {
|
||||
var key uint8
|
||||
err = binary.Read(reader, binary.BigEndian, &key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var value []badoption.Prefixable
|
||||
var prefixCount uint64
|
||||
prefixCount, err = binary.ReadUvarint(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for j := uint64(0); j < prefixCount; j++ {
|
||||
var prefix netip.Prefix
|
||||
prefix, err = readPrefix(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
value = append(value, badoption.Prefixable(prefix))
|
||||
}
|
||||
rule.NetworkInterfaceAddress.Put(option.InterfaceType(key), value)
|
||||
}
|
||||
case ruleItemDefaultInterfaceAddress:
|
||||
var value []badoption.Prefixable
|
||||
var prefixCount uint64
|
||||
prefixCount, err = binary.ReadUvarint(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for j := uint64(0); j < prefixCount; j++ {
|
||||
var prefix netip.Prefix
|
||||
prefix, err = readPrefix(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
value = append(value, badoption.Prefixable(prefix))
|
||||
}
|
||||
rule.DefaultInterfaceAddress = value
|
||||
case ruleItemFinal:
|
||||
err = binary.Read(reader, binary.BigEndian, &rule.Invert)
|
||||
return
|
||||
@ -346,7 +395,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
||||
}
|
||||
if len(rule.NetworkType) > 0 {
|
||||
if generateVersion < C.RuleSetVersion3 {
|
||||
return E.New("network_type rule item is only supported in version 3 or later")
|
||||
return E.New("`network_type` rule item is only supported in version 3 or later")
|
||||
}
|
||||
err = writeRuleItemUint8(writer, ruleItemNetworkType, rule.NetworkType)
|
||||
if err != nil {
|
||||
@ -354,17 +403,67 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
|
||||
}
|
||||
}
|
||||
if rule.NetworkIsExpensive {
|
||||
if generateVersion < C.RuleSetVersion3 {
|
||||
return E.New("`network_is_expensive` rule item is only supported in version 3 or later")
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsExpensive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if rule.NetworkIsConstrained {
|
||||
if generateVersion < C.RuleSetVersion3 {
|
||||
return E.New("`network_is_constrained` rule item is only supported in version 3 or later")
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsConstrained)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if rule.NetworkInterfaceAddress != nil && rule.NetworkInterfaceAddress.Size() > 0 {
|
||||
if generateVersion < C.RuleSetVersion4 {
|
||||
return E.New("`network_interface_address` rule item is only supported in version 4 or later")
|
||||
}
|
||||
err = writer.WriteByte(ruleItemNetworkInterfaceAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = varbin.WriteUvarint(writer, uint64(rule.NetworkInterfaceAddress.Size()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, entry := range rule.NetworkInterfaceAddress.Entries() {
|
||||
err = binary.Write(writer, binary.BigEndian, uint8(entry.Key.Build()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rawPrefix := range entry.Value {
|
||||
err = writePrefix(writer, rawPrefix.Build(netip.Prefix{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(rule.DefaultInterfaceAddress) > 0 {
|
||||
if generateVersion < C.RuleSetVersion4 {
|
||||
return E.New("`default_interface_address` rule item is only supported in version 4 or later")
|
||||
}
|
||||
err = writer.WriteByte(ruleItemDefaultInterfaceAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = varbin.WriteUvarint(writer, uint64(len(rule.DefaultInterfaceAddress)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rawPrefix := range rule.DefaultInterfaceAddress {
|
||||
err = writePrefix(writer, rawPrefix.Build(netip.Prefix{}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(rule.WIFISSID) > 0 {
|
||||
err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID)
|
||||
if err != nil {
|
||||
|
33
common/srs/ip_cidr.go
Normal file
33
common/srs/ip_cidr.go
Normal file
@ -0,0 +1,33 @@
|
||||
package srs
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net/netip"
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/varbin"
|
||||
)
|
||||
|
||||
func readPrefix(reader varbin.Reader) (netip.Prefix, error) {
|
||||
addrSlice, err := varbin.ReadValue[[]byte](reader, binary.BigEndian)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, err
|
||||
}
|
||||
prefixBits, err := varbin.ReadValue[uint8](reader, binary.BigEndian)
|
||||
if err != nil {
|
||||
return netip.Prefix{}, err
|
||||
}
|
||||
return netip.PrefixFrom(M.AddrFromIP(addrSlice), int(prefixBits)), nil
|
||||
}
|
||||
|
||||
func writePrefix(writer varbin.Writer, prefix netip.Prefix) error {
|
||||
err := varbin.Write(writer, binary.BigEndian, prefix.Addr().AsSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = binary.Write(writer, binary.BigEndian, uint8(prefix.Bits()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -22,7 +22,8 @@ const (
|
||||
RuleSetVersion1 = 1 + iota
|
||||
RuleSetVersion2
|
||||
RuleSetVersion3
|
||||
RuleSetVersionCurrent = RuleSetVersion3
|
||||
RuleSetVersion4
|
||||
RuleSetVersionCurrent = RuleSetVersion4
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -100,6 +100,9 @@ type RawDefaultRule struct {
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
InterfaceAddress *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]] `json:"interface_address,omitempty"`
|
||||
NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"`
|
||||
DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
@ -103,6 +103,9 @@ type RawDefaultDNSRule struct {
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
InterfaceAddress *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]] `json:"interface_address,omitempty"`
|
||||
NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"`
|
||||
DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"`
|
||||
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||
RuleSetIPCIDRMatchSource bool `json:"rule_set_ip_cidr_match_source,omitempty"`
|
||||
RuleSetIPCIDRAcceptEmpty bool `json:"rule_set_ip_cidr_accept_empty,omitempty"`
|
||||
|
@ -203,6 +203,9 @@ type DefaultHeadlessRule struct {
|
||||
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
|
||||
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
|
||||
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
|
||||
NetworkInterfaceAddress *badjson.TypedMap[InterfaceType, badoption.Listable[badoption.Prefixable]] `json:"network_interface_address,omitempty"`
|
||||
DefaultInterfaceAddress badoption.Listable[badoption.Prefixable] `json:"default_interface_address,omitempty"`
|
||||
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
|
||||
DomainMatcher *domain.Matcher `json:"-"`
|
||||
@ -240,7 +243,7 @@ type PlainRuleSetCompat _PlainRuleSetCompat
|
||||
func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
|
||||
var v any
|
||||
switch r.Version {
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
|
||||
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3, C.RuleSetVersion4:
|
||||
v = r.Options
|
||||
default:
|
||||
return nil, E.New("unknown rule-set version: ", r.Version)
|
||||
|
@ -246,6 +246,21 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.InterfaceAddress != nil && options.InterfaceAddress.Size() > 0 {
|
||||
item := NewInterfaceAddressItem(networkManager, options.InterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 {
|
||||
item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DefaultInterfaceAddress) > 0 {
|
||||
item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.RuleSet) > 0 {
|
||||
var matchSource bool
|
||||
if options.RuleSetIPCIDRMatchSource {
|
||||
|
56
route/rule/rule_default_interface_address.go
Normal file
56
route/rule/rule_default_interface_address.go
Normal file
@ -0,0 +1,56 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*DefaultInterfaceAddressItem)(nil)
|
||||
|
||||
type DefaultInterfaceAddressItem struct {
|
||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||
interfaceAddresses []netip.Prefix
|
||||
}
|
||||
|
||||
func NewDefaultInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses badoption.Listable[badoption.Prefixable]) *DefaultInterfaceAddressItem {
|
||||
item := &DefaultInterfaceAddressItem{
|
||||
interfaceMonitor: networkManager.InterfaceMonitor(),
|
||||
interfaceAddresses: make([]netip.Prefix, 0, len(interfaceAddresses)),
|
||||
}
|
||||
for _, prefixable := range interfaceAddresses {
|
||||
item.interfaceAddresses = append(item.interfaceAddresses, prefixable.Build(netip.Prefix{}))
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func (r *DefaultInterfaceAddressItem) Match(metadata *adapter.InboundContext) bool {
|
||||
defaultInterface := r.interfaceMonitor.DefaultInterface()
|
||||
if defaultInterface == nil {
|
||||
return false
|
||||
}
|
||||
for _, address := range r.interfaceAddresses {
|
||||
if common.All(defaultInterface.Addresses, func(it netip.Prefix) bool {
|
||||
return !address.Overlaps(it)
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *DefaultInterfaceAddressItem) String() string {
|
||||
addressLen := len(r.interfaceAddresses)
|
||||
switch {
|
||||
case addressLen == 1:
|
||||
return "default_interface_address=" + r.interfaceAddresses[0].String()
|
||||
case addressLen > 3:
|
||||
return "default_interface_address=[" + strings.Join(common.Map(r.interfaceAddresses[:3], netip.Prefix.String), " ") + "...]"
|
||||
default:
|
||||
return "default_interface_address=[" + strings.Join(common.Map(r.interfaceAddresses, netip.Prefix.String), " ") + "]"
|
||||
}
|
||||
}
|
@ -247,6 +247,21 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.InterfaceAddress != nil && options.InterfaceAddress.Size() > 0 {
|
||||
item := NewInterfaceAddressItem(networkManager, options.InterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 {
|
||||
item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DefaultInterfaceAddress) > 0 {
|
||||
item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.RuleSet) > 0 {
|
||||
var matchSource bool
|
||||
if options.RuleSetIPCIDRMatchSource {
|
||||
|
@ -164,13 +164,21 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
|
||||
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
}
|
||||
if len(options.WIFIBSSID) > 0 {
|
||||
item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
|
||||
}
|
||||
if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 {
|
||||
item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if len(options.DefaultInterfaceAddress) > 0 {
|
||||
item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
}
|
||||
if len(options.AdGuardDomain) > 0 {
|
||||
|
62
route/rule/rule_interface_address.go
Normal file
62
route/rule/rule_interface_address.go
Normal file
@ -0,0 +1,62 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*InterfaceAddressItem)(nil)
|
||||
|
||||
type InterfaceAddressItem struct {
|
||||
networkManager adapter.NetworkManager
|
||||
interfaceAddresses map[string][]netip.Prefix
|
||||
description string
|
||||
}
|
||||
|
||||
func NewInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses *badjson.TypedMap[string, badoption.Listable[badoption.Prefixable]]) *InterfaceAddressItem {
|
||||
item := &InterfaceAddressItem{
|
||||
networkManager: networkManager,
|
||||
interfaceAddresses: make(map[string][]netip.Prefix, interfaceAddresses.Size()),
|
||||
}
|
||||
var entryDescriptions []string
|
||||
for _, entry := range interfaceAddresses.Entries() {
|
||||
prefixes := make([]netip.Prefix, 0, len(entry.Value))
|
||||
for _, prefixable := range entry.Value {
|
||||
prefixes = append(prefixes, prefixable.Build(netip.Prefix{}))
|
||||
}
|
||||
item.interfaceAddresses[entry.Key] = prefixes
|
||||
entryDescriptions = append(entryDescriptions, entry.Key+"="+strings.Join(common.Map(prefixes, netip.Prefix.String), ","))
|
||||
}
|
||||
item.description = "interface_address=[" + strings.Join(entryDescriptions, " ") + "]"
|
||||
return item
|
||||
}
|
||||
|
||||
func (r *InterfaceAddressItem) Match(metadata *adapter.InboundContext) bool {
|
||||
interfaces := r.networkManager.InterfaceFinder().Interfaces()
|
||||
for ifName, addresses := range r.interfaceAddresses {
|
||||
iface := common.Find(interfaces, func(it control.Interface) bool {
|
||||
return it.Name == ifName
|
||||
})
|
||||
if iface.Name == "" {
|
||||
return false
|
||||
}
|
||||
if common.All(addresses, func(address netip.Prefix) bool {
|
||||
return common.All(iface.Addresses, func(it netip.Prefix) bool {
|
||||
return !address.Overlaps(it)
|
||||
})
|
||||
}) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *InterfaceAddressItem) String() string {
|
||||
return r.description
|
||||
}
|
64
route/rule/rule_network_interface_address.go
Normal file
64
route/rule/rule_network_interface_address.go
Normal file
@ -0,0 +1,64 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/json/badoption"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*NetworkInterfaceAddressItem)(nil)
|
||||
|
||||
type NetworkInterfaceAddressItem struct {
|
||||
networkManager adapter.NetworkManager
|
||||
interfaceAddresses map[C.InterfaceType][]netip.Prefix
|
||||
description string
|
||||
}
|
||||
|
||||
func NewNetworkInterfaceAddressItem(networkManager adapter.NetworkManager, interfaceAddresses *badjson.TypedMap[option.InterfaceType, badoption.Listable[badoption.Prefixable]]) *NetworkInterfaceAddressItem {
|
||||
item := &NetworkInterfaceAddressItem{
|
||||
networkManager: networkManager,
|
||||
interfaceAddresses: make(map[C.InterfaceType][]netip.Prefix, interfaceAddresses.Size()),
|
||||
}
|
||||
var entryDescriptions []string
|
||||
for _, entry := range interfaceAddresses.Entries() {
|
||||
prefixes := make([]netip.Prefix, 0, len(entry.Value))
|
||||
for _, prefixable := range entry.Value {
|
||||
prefixes = append(prefixes, prefixable.Build(netip.Prefix{}))
|
||||
}
|
||||
item.interfaceAddresses[entry.Key.Build()] = prefixes
|
||||
entryDescriptions = append(entryDescriptions, entry.Key.Build().String()+"="+strings.Join(common.Map(prefixes, netip.Prefix.String), ","))
|
||||
}
|
||||
item.description = "network_interface_address=[" + strings.Join(entryDescriptions, " ") + "]"
|
||||
return item
|
||||
}
|
||||
|
||||
func (r *NetworkInterfaceAddressItem) Match(metadata *adapter.InboundContext) bool {
|
||||
interfaces := r.networkManager.NetworkInterfaces()
|
||||
match:
|
||||
for ifType, addresses := range r.interfaceAddresses {
|
||||
for _, networkInterface := range interfaces {
|
||||
if networkInterface.Type != ifType {
|
||||
continue
|
||||
}
|
||||
if common.Any(networkInterface.Addresses, func(it netip.Prefix) bool {
|
||||
return common.Any(addresses, func(prefix netip.Prefix) bool {
|
||||
return prefix.Overlaps(it)
|
||||
})
|
||||
}) {
|
||||
continue match
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *NetworkInterfaceAddressItem) String() string {
|
||||
return r.description
|
||||
}
|
@ -42,7 +42,7 @@ func extractIPSetFromRule(rawRule adapter.HeadlessRule) []*netipx.IPSet {
|
||||
}
|
||||
}
|
||||
|
||||
func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultHeadlessRule) bool) bool {
|
||||
func HasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultHeadlessRule) bool) bool {
|
||||
for _, rule := range rules {
|
||||
switch rule.Type {
|
||||
case C.RuleTypeDefault:
|
||||
@ -50,7 +50,7 @@ func hasHeadlessRule(rules []option.HeadlessRule, cond func(rule option.DefaultH
|
||||
return true
|
||||
}
|
||||
case C.RuleTypeLogical:
|
||||
if hasHeadlessRule(rule.LogicalOptions.Rules, cond) {
|
||||
if HasHeadlessRule(rule.LogicalOptions.Rules, cond) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -138,9 +138,9 @@ func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error {
|
||||
}
|
||||
}
|
||||
var metadata adapter.RuleSetMetadata
|
||||
metadata.ContainsProcessRule = hasHeadlessRule(headlessRules, isProcessHeadlessRule)
|
||||
metadata.ContainsWIFIRule = hasHeadlessRule(headlessRules, isWIFIHeadlessRule)
|
||||
metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
|
||||
metadata.ContainsProcessRule = HasHeadlessRule(headlessRules, isProcessHeadlessRule)
|
||||
metadata.ContainsWIFIRule = HasHeadlessRule(headlessRules, isWIFIHeadlessRule)
|
||||
metadata.ContainsIPCIDRRule = HasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
|
||||
s.rules = rules
|
||||
s.metadata = metadata
|
||||
s.callbackAccess.Lock()
|
||||
|
@ -185,9 +185,9 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
|
||||
return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
||||
}
|
||||
}
|
||||
s.metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
||||
s.metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
||||
s.metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
|
||||
s.metadata.ContainsProcessRule = HasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
||||
s.metadata.ContainsWIFIRule = HasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
||||
s.metadata.ContainsIPCIDRRule = HasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
|
||||
s.rules = rules
|
||||
s.callbackAccess.Lock()
|
||||
callbacks := s.callbacks.Array()
|
||||
|
Loading…
x
Reference in New Issue
Block a user