123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- package iface
- import (
- "errors"
- "net"
- "net/netip"
- "time"
- "github.com/metacubex/mihomo/common/singledo"
- )
- type Interface struct {
- Index int
- MTU int
- Name string
- Addresses []netip.Prefix
- HardwareAddr net.HardwareAddr
- }
- var (
- ErrIfaceNotFound = errors.New("interface not found")
- ErrAddrNotFound = errors.New("addr not found")
- )
- var interfaces = singledo.NewSingle[map[string]*Interface](time.Second * 20)
- func Interfaces() (map[string]*Interface, error) {
- 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
- 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)
- ip = ip.Unmap()
- pf = netip.PrefixFrom(ip, ip.BitLen())
- }
- if pf.IsValid() {
- ipNets = append(ipNets, pf)
- }
- }
- r[iface.Name] = &Interface{
- Index: iface.Index,
- MTU: iface.MTU,
- Name: iface.Name,
- Addresses: ipNets,
- HardwareAddr: iface.HardwareAddr,
- }
- }
- return r, nil
- })
- return value, err
- }
- func ResolveInterface(name string) (*Interface, error) {
- ifaces, err := Interfaces()
- if err != nil {
- return nil, err
- }
- iface, ok := ifaces[name]
- if !ok {
- return nil, ErrIfaceNotFound
- }
- return iface, nil
- }
- func IsLocalIp(ip netip.Addr) (bool, error) {
- ifaces, err := Interfaces()
- 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
- }
- func FlushCache() {
- interfaces.Reset()
- }
- func (iface *Interface) PickIPv4Addr(destination netip.Addr) (netip.Prefix, error) {
- return iface.pickIPAddr(destination, func(addr netip.Prefix) bool {
- return addr.Addr().Is4()
- })
- }
- func (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, error) {
- return iface.pickIPAddr(destination, func(addr netip.Prefix) bool {
- 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.Addresses {
- if !accept(addr) {
- continue
- }
- if !fallback.IsValid() && !addr.Addr().IsLinkLocalUnicast() {
- fallback = addr
- if !destination.IsValid() {
- break
- }
- }
- if destination.IsValid() && addr.Contains(destination) {
- return addr, nil
- }
- }
- if !fallback.IsValid() {
- return netip.Prefix{}, ErrAddrNotFound
- }
- return fallback, nil
- }
|