github.com/kelleygo/clashcore@v1.0.2/component/iface/iface.go (about) 1 package iface 2 3 import ( 4 "errors" 5 "net" 6 "net/netip" 7 "time" 8 9 "github.com/kelleygo/clashcore/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 Interfaces() (map[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 var pf netip.Prefix 44 switch ipNet := addr.(type) { 45 case *net.IPNet: 46 ip, _ := netip.AddrFromSlice(ipNet.IP) 47 ones, bits := ipNet.Mask.Size() 48 if bits == 32 { 49 ip = ip.Unmap() 50 } 51 pf = netip.PrefixFrom(ip, ones) 52 case *net.IPAddr: 53 ip, _ := netip.AddrFromSlice(ipNet.IP) 54 ip = ip.Unmap() 55 pf = netip.PrefixFrom(ip, ip.BitLen()) 56 } 57 if pf.IsValid() { 58 ipNets = append(ipNets, pf) 59 } 60 } 61 62 r[iface.Name] = &Interface{ 63 Index: iface.Index, 64 Name: iface.Name, 65 Addrs: ipNets, 66 HardwareAddr: iface.HardwareAddr, 67 } 68 } 69 70 return r, nil 71 }) 72 return value, err 73 } 74 75 func ResolveInterface(name string) (*Interface, error) { 76 ifaces, err := Interfaces() 77 if err != nil { 78 return nil, err 79 } 80 81 iface, ok := ifaces[name] 82 if !ok { 83 return nil, ErrIfaceNotFound 84 } 85 86 return iface, nil 87 } 88 89 func IsLocalIp(ip netip.Addr) (bool, error) { 90 ifaces, err := Interfaces() 91 if err != nil { 92 return false, err 93 } 94 for _, iface := range ifaces { 95 for _, addr := range iface.Addrs { 96 if addr.Contains(ip) { 97 return true, nil 98 } 99 } 100 } 101 return false, nil 102 } 103 104 func FlushCache() { 105 interfaces.Reset() 106 } 107 108 func (iface *Interface) PickIPv4Addr(destination netip.Addr) (netip.Prefix, error) { 109 return iface.pickIPAddr(destination, func(addr netip.Prefix) bool { 110 return addr.Addr().Is4() 111 }) 112 } 113 114 func (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, error) { 115 return iface.pickIPAddr(destination, func(addr netip.Prefix) bool { 116 return addr.Addr().Is6() 117 }) 118 } 119 120 func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr netip.Prefix) bool) (netip.Prefix, error) { 121 var fallback netip.Prefix 122 123 for _, addr := range iface.Addrs { 124 if !accept(addr) { 125 continue 126 } 127 128 if !fallback.IsValid() && !addr.Addr().IsLinkLocalUnicast() { 129 fallback = addr 130 131 if !destination.IsValid() { 132 break 133 } 134 } 135 136 if destination.IsValid() && addr.Contains(destination) { 137 return addr, nil 138 } 139 } 140 141 if !fallback.IsValid() { 142 return netip.Prefix{}, ErrAddrNotFound 143 } 144 145 return fallback, nil 146 }