From 68abb1348acee9379778e23fccaa395fe9dde79f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 17 Mar 2025 10:42:13 +0800 Subject: [PATCH] chore: support longest-prefix matches in local interface finding --- component/iface/iface.go | 62 +++++++++++++++++++++++++++----------- go.mod | 1 + go.sum | 2 ++ listener/sing_tun/iface.go | 35 ++++++--------------- 4 files changed, 58 insertions(+), 42 deletions(-) diff --git a/component/iface/iface.go b/component/iface/iface.go index a56b3432..62a46f1f 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -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) { diff --git a/go.mod b/go.mod index ffc2685d..f1ba0436 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2 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/chacha v0.1.1 github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 diff --git a/go.sum b/go.sum index feacad2d..c74b82fb 100644 --- a/go.sum +++ b/go.sum @@ -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/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/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/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro= github.com/metacubex/chacha v0.1.1 h1:OHIv11Nd9CISAIzegpjfupIoZp9DYm6uQw41RxvmU/c= diff --git a/listener/sing_tun/iface.go b/listener/sing_tun/iface.go index ed152b69..3551a853 100644 --- a/listener/sing_tun/iface.go +++ b/listener/sing_tun/iface.go @@ -1,7 +1,6 @@ package sing_tun import ( - "errors" "net" "net/netip" @@ -16,7 +15,8 @@ var DefaultInterfaceFinder control.InterfaceFinder = (*defaultInterfaceFinder)(n func (f *defaultInterfaceFinder) Update() error { iface.FlushCache() - return nil + _, err := iface.Interfaces() + return err } func (f *defaultInterfaceFinder) Interfaces() []control.Interface { @@ -32,27 +32,19 @@ func (f *defaultInterfaceFinder) Interfaces() []control.Interface { return interfaces } -var errNoSuchInterface = errors.New("no such network interface") - func (f *defaultInterfaceFinder) ByName(name string) (*control.Interface, error) { - ifaces, err := iface.Interfaces() - if err != nil { - return nil, err - } - for _, netInterface := range ifaces { - if netInterface.Name == name { - return (*control.Interface)(netInterface), nil - } - } - _, err = net.InterfaceByName(name) + netInterface, err := iface.ResolveInterface(name) if err == nil { + return (*control.Interface)(netInterface), nil + } + if _, err := net.InterfaceByName(name); err == nil { err = f.Update() if err != nil { return nil, err } return f.ByName(name) } - return nil, errNoSuchInterface + return nil, err } 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 nil, errNoSuchInterface + return nil, iface.ErrIfaceNotFound } func (f *defaultInterfaceFinder) ByAddr(addr netip.Addr) (*control.Interface, error) { - ifaces, err := iface.Interfaces() + netInterface, err := iface.ResolveInterfaceByAddr(addr) if err != nil { return nil, err } - for _, netInterface := range ifaces { - for _, prefix := range netInterface.Addresses { - if prefix.Contains(addr) { - return (*control.Interface)(netInterface), nil - } - } - } - return nil, errNoSuchInterface + return (*control.Interface)(netInterface), nil }