github.com/igoogolx/clash@v1.19.8/component/iface/iface.go (about)

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