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  }