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

1
go.mod
View File

@ -18,6 +18,7 @@ require (
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/mdlayher/netlink v1.7.2 github.com/mdlayher/netlink v1.7.2
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
github.com/metacubex/bart v0.19.0
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399
github.com/metacubex/chacha v0.1.1 github.com/metacubex/chacha v0.1.1
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759

2
go.sum
View File

@ -97,6 +97,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4= github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab h1:Chbw+/31UC14YFNr78pESt5Vowlc62zziw05JCUqoL4=
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI= github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab/go.mod h1:xVKK8jC5Sd3hfh7WjmCq+HorehIbrBijaUWmcuKjPcI=
github.com/metacubex/bart v0.19.0 h1:XQ9AJeI+WO+phRPkUOoflAFwlqDJnm5BPQpixciJQBY=
github.com/metacubex/bart v0.19.0/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig= github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro= github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c= github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c=

View File

@ -1,7 +1,6 @@
package sing_tun package sing_tun
import ( import (
"errors"
"net" "net"
"net/netip" "net/netip"
@ -16,7 +15,8 @@ var DefaultInterfaceFinder control.InterfaceFinder = (*defaultInterfaceFinder)(n
func (f *defaultInterfaceFinder) Update() error { func (f *defaultInterfaceFinder) Update() error {
iface.FlushCache() iface.FlushCache()
return nil _, err := iface.Interfaces()
return err
} }
func (f *defaultInterfaceFinder) Interfaces() []control.Interface { func (f *defaultInterfaceFinder) Interfaces() []control.Interface {
@ -32,27 +32,19 @@ func (f *defaultInterfaceFinder) Interfaces() []control.Interface {
return interfaces return interfaces
} }
var errNoSuchInterface = errors.New("no such network interface")
func (f *defaultInterfaceFinder) ByName(name string) (*control.Interface, error) { func (f *defaultInterfaceFinder) ByName(name string) (*control.Interface, error) {
ifaces, err := iface.Interfaces() netInterface, err := iface.ResolveInterface(name)
if err != nil {
return nil, err
}
for _, netInterface := range ifaces {
if netInterface.Name == name {
return (*control.Interface)(netInterface), nil
}
}
_, err = net.InterfaceByName(name)
if err == nil { if err == nil {
return (*control.Interface)(netInterface), nil
}
if _, err := net.InterfaceByName(name); err == nil {
err = f.Update() err = f.Update()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return f.ByName(name) return f.ByName(name)
} }
return nil, errNoSuchInterface return nil, err
} }
func (f *defaultInterfaceFinder) ByIndex(index int) (*control.Interface, error) { func (f *defaultInterfaceFinder) ByIndex(index int) (*control.Interface, error) {
@ -73,20 +65,13 @@ func (f *defaultInterfaceFinder) ByIndex(index int) (*control.Interface, error)
} }
return f.ByIndex(index) return f.ByIndex(index)
} }
return nil, errNoSuchInterface return nil, iface.ErrIfaceNotFound
} }
func (f *defaultInterfaceFinder) ByAddr(addr netip.Addr) (*control.Interface, error) { func (f *defaultInterfaceFinder) ByAddr(addr netip.Addr) (*control.Interface, error) {
ifaces, err := iface.Interfaces() netInterface, err := iface.ResolveInterfaceByAddr(addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, netInterface := range ifaces { return (*control.Interface)(netInterface), nil
for _, prefix := range netInterface.Addresses {
if prefix.Contains(addr) {
return (*control.Interface)(netInterface), nil
}
}
}
return nil, errNoSuchInterface
} }