chore: support longest-prefix matches in local interface finding

This commit is contained in:
wwqgtxx
2025-03-17 10:42:13 +08:00
parent dee5898e36
commit 68abb1348a
4 changed files with 58 additions and 42 deletions

View File

@@ -7,6 +7,8 @@ import (
"time"
"github.com/metacubex/mihomo/common/singledo"
"github.com/metacubex/bart"
)
type Interface struct {
@@ -23,16 +25,23 @@ var (
ErrAddrNotFound = errors.New("addr not found")
)
var interfaces = singledo.NewSingle[map[string]*Interface](time.Second * 20)
type ifaceCache struct {
ifMap map[string]*Interface
ifTable bart.Table[*Interface]
}
func Interfaces() (map[string]*Interface, error) {
value, err, _ := interfaces.Do(func() (map[string]*Interface, error) {
var caches = singledo.NewSingle[*ifaceCache](time.Second * 20)
func getCache() (*ifaceCache, error) {
value, err, _ := caches.Do(func() (*ifaceCache, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
r := map[string]*Interface{}
cache := &ifaceCache{
ifMap: make(map[string]*Interface),
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
@@ -61,7 +70,7 @@ func Interfaces() (map[string]*Interface, error) {
}
}
r[iface.Name] = &Interface{
ifaceObj := &Interface{
Index: iface.Index,
MTU: iface.MTU,
Name: iface.Name,
@@ -69,13 +78,26 @@ func Interfaces() (map[string]*Interface, error) {
Flags: iface.Flags,
Addresses: ipNets,
}
cache.ifMap[iface.Name] = ifaceObj
for _, prefix := range ipNets {
cache.ifTable.Insert(prefix, ifaceObj)
}
}
return r, nil
return cache, nil
})
return value, err
}
func Interfaces() (map[string]*Interface, error) {
cache, err := getCache()
if err != nil {
return nil, err
}
return cache.ifMap, nil
}
func ResolveInterface(name string) (*Interface, error) {
ifaces, err := Interfaces()
if err != nil {
@@ -90,23 +112,29 @@ func ResolveInterface(name string) (*Interface, error) {
return iface, nil
}
func IsLocalIp(ip netip.Addr) (bool, error) {
ifaces, err := Interfaces()
func ResolveInterfaceByAddr(addr netip.Addr) (*Interface, error) {
cache, err := getCache()
if err != nil {
return nil, err
}
iface, ok := cache.ifTable.Lookup(addr)
if !ok {
return nil, ErrIfaceNotFound
}
return iface, nil
}
func IsLocalIp(addr netip.Addr) (bool, error) {
cache, err := getCache()
if err != nil {
return false, err
}
for _, iface := range ifaces {
for _, addr := range iface.Addresses {
if addr.Contains(ip) {
return true, nil
}
}
}
return false, nil
return cache.ifTable.Contains(addr), nil
}
func FlushCache() {
interfaces.Reset()
caches.Reset()
}
func (iface *Interface) PickIPv4Addr(destination netip.Addr) (netip.Prefix, error) {