github.com/yaling888/clash@v1.53.0/component/iface/iface.go (about) 1 package iface 2 3 import ( 4 "errors" 5 "net" 6 "net/netip" 7 "time" 8 9 "github.com/yaling888/clash/common/singledo" 10 ) 11 12 type Interface struct { 13 Index int 14 Name string 15 Addrs []*netip.Prefix 16 HardwareAddr net.HardwareAddr 17 } 18 19 var ( 20 ErrIfaceNotFound = errors.New("interface not found") 21 ErrAddrNotFound = errors.New("addr not found") 22 ) 23 24 var interfaces = singledo.NewSingle[map[string]*Interface](time.Second * 20) 25 26 func ResolveInterface(name string) (*Interface, error) { 27 value, err, _ := interfaces.Do(func() (map[string]*Interface, error) { 28 ifaces, err := net.Interfaces() 29 if err != nil { 30 return nil, err 31 } 32 33 r := map[string]*Interface{} 34 35 for _, iface := range ifaces { 36 addrs, err := iface.Addrs() 37 if err != nil { 38 continue 39 } 40 41 ipNets := make([]*netip.Prefix, 0, len(addrs)) 42 for _, addr := range addrs { 43 ipNet := addr.(*net.IPNet) 44 ip, _ := netip.AddrFromSlice(ipNet.IP) 45 46 ones, bits := ipNet.Mask.Size() 47 if bits == 32 { 48 ip = ip.Unmap() 49 } 50 51 pf := netip.PrefixFrom(ip, ones) 52 ipNets = append(ipNets, &pf) 53 } 54 55 r[iface.Name] = &Interface{ 56 Index: iface.Index, 57 Name: iface.Name, 58 Addrs: ipNets, 59 HardwareAddr: iface.HardwareAddr, 60 } 61 } 62 63 return r, nil 64 }) 65 if err != nil { 66 return nil, err 67 } 68 69 ifaces := value 70 iface, ok := ifaces[name] 71 if !ok { 72 return nil, ErrIfaceNotFound 73 } 74 75 return iface, nil 76 } 77 78 func FlushCache() { 79 interfaces.Reset() 80 } 81 82 func (iface *Interface) PickIPv4Addr(destination netip.Addr) (*netip.Prefix, error) { 83 return iface.pickIPAddr(destination, func(addr *netip.Prefix) bool { 84 return addr.Addr().Is4() 85 }) 86 } 87 88 func (iface *Interface) PickIPv6Addr(destination netip.Addr) (*netip.Prefix, error) { 89 return iface.pickIPAddr(destination, func(addr *netip.Prefix) bool { 90 return addr.Addr().Is6() 91 }) 92 } 93 94 func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr *netip.Prefix) bool) (*netip.Prefix, error) { 95 var fallback *netip.Prefix 96 97 for _, addr := range iface.Addrs { 98 if !accept(addr) { 99 continue 100 } 101 102 if fallback == nil && !addr.Addr().IsLinkLocalUnicast() { 103 fallback = addr 104 105 if !destination.IsValid() { 106 break 107 } 108 } 109 110 if destination.IsValid() && addr.Contains(destination) { 111 return addr, nil 112 } 113 } 114 115 if fallback == nil { 116 return nil, ErrAddrNotFound 117 } 118 119 return fallback, nil 120 }