Clash.Meta/component/iface/iface.go

128 lines
2.5 KiB
Go
Raw Normal View History

package iface
import (
"errors"
"net"
2022-04-20 01:52:51 +08:00
"net/netip"
"time"
2023-11-03 21:01:45 +08:00
"github.com/metacubex/mihomo/common/singledo"
)
type Interface struct {
Index int
Name string
Addrs []netip.Prefix
HardwareAddr net.HardwareAddr
}
2021-10-10 23:44:09 +08:00
var (
ErrIfaceNotFound = errors.New("interface not found")
ErrAddrNotFound = errors.New("addr not found")
)
2022-04-24 02:07:57 +08:00
var interfaces = singledo.NewSingle[map[string]*Interface](time.Second * 20)
func ResolveInterface(name string) (*Interface, error) {
2022-04-24 02:07:57 +08:00
value, err, _ := interfaces.Do(func() (map[string]*Interface, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
r := map[string]*Interface{}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
ipNets := make([]netip.Prefix, 0, len(addrs))
for _, addr := range addrs {
var pf netip.Prefix
2024-03-24 21:41:05 +08:00
switch ipNet := addr.(type) {
case *net.IPNet:
ip, _ := netip.AddrFromSlice(ipNet.IP)
ones, bits := ipNet.Mask.Size()
if bits == 32 {
ip = ip.Unmap()
}
pf = netip.PrefixFrom(ip, ones)
case *net.IPAddr:
ip, _ := netip.AddrFromSlice(ipNet.IP)
2022-04-20 01:52:51 +08:00
ip = ip.Unmap()
pf = netip.PrefixFrom(ip, ip.BitLen())
}
if pf.IsValid() {
ipNets = append(ipNets, pf)
}
}
r[iface.Name] = &Interface{
Index: iface.Index,
Name: iface.Name,
Addrs: ipNets,
HardwareAddr: iface.HardwareAddr,
}
}
return r, nil
})
if err != nil {
return nil, err
}
2022-04-24 02:07:57 +08:00
ifaces := value
iface, ok := ifaces[name]
if !ok {
return nil, ErrIfaceNotFound
}
return iface, nil
}
func FlushCache() {
interfaces.Reset()
}
func (iface *Interface) PickIPv4Addr(destination netip.Addr) (netip.Prefix, error) {
return iface.pickIPAddr(destination, func(addr netip.Prefix) bool {
2022-04-20 01:52:51 +08:00
return addr.Addr().Is4()
})
}
func (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, error) {
return iface.pickIPAddr(destination, func(addr netip.Prefix) bool {
2022-04-20 01:52:51 +08:00
return addr.Addr().Is6()
})
}
func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr netip.Prefix) bool) (netip.Prefix, error) {
var fallback netip.Prefix
for _, addr := range iface.Addrs {
if !accept(addr) {
continue
}
if !fallback.IsValid() && !addr.Addr().IsLinkLocalUnicast() {
fallback = addr
2022-04-20 01:52:51 +08:00
if !destination.IsValid() {
break
}
}
2022-04-20 01:52:51 +08:00
if destination.IsValid() && addr.Contains(destination) {
return addr, nil
}
}
if !fallback.IsValid() {
return netip.Prefix{}, ErrAddrNotFound
}
return fallback, nil
}