github.com/haraldrudell/parl@v0.4.176/pnet/interface.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pnet
     7  
     8  import (
     9  	"errors"
    10  	"net"
    11  	"net/netip"
    12  
    13  	"github.com/haraldrudell/parl/perrors"
    14  )
    15  
    16  func Interfaces() (interfaces []net.Interface, err error) {
    17  	if interfaces, err = net.Interfaces(); err != nil {
    18  		perrors.ErrorfPF("net.Interfaces %w", err)
    19  	}
    20  	return
    21  }
    22  
    23  // InterfaceAddrs gets Addresses for interface
    24  //   - netInterface.Name is interface name "eth0"
    25  //   - netInterface.Addr() returns assigned IP addresses
    26  func InterfaceAddrs(netInterface *net.Interface) (i4, i6 []netip.Prefix, err error) {
    27  
    28  	// get assigned IPv4 and IPv6 addresses
    29  	var netAddrSlice []net.Addr
    30  	if netAddrSlice, err = netInterface.Addrs(); perrors.IsPF(&err, "netInterface.Addrs %w", err) {
    31  		return
    32  	}
    33  	// netAddr is interface with Network() String()
    34  	//	- go1.20.3: type is *net.IPNet
    35  	//	- Network is "ip+net"
    36  	//	- String is "127.0.0.1/8" "fe80::1/64"
    37  	var netAddr net.Addr
    38  	for _, netAddr = range netAddrSlice {
    39  		var netIPNet *net.IPNet
    40  		var ok bool
    41  		if netIPNet, ok = netAddr.(*net.IPNet); !ok {
    42  			err = perrors.ErrorfPF("type assertion failed actual: %T expected: %T", netAddr, netIPNet)
    43  			return
    44  		}
    45  		var prefix netip.Prefix
    46  		if prefix, err = IPNetToPrefix(netIPNet); err != nil {
    47  			return
    48  		}
    49  		if prefix.Addr().Is4() {
    50  			i4 = append(i4, prefix)
    51  		} else {
    52  			i6 = append(i6, prefix)
    53  		}
    54  	}
    55  
    56  	return
    57  }
    58  
    59  // InterfaceFromAddr finds network interface and prefix for addr
    60  //   - returns interface and prefix or error
    61  //   - first uses Zone, then scans all interfaces for prefixes
    62  //   - addr must be valid
    63  func InterfaceFromAddr(addr netip.Addr) (netInterface *net.Interface, prefix netip.Prefix, isNoSuchInterface bool, err error) {
    64  
    65  	// ensure there is IP
    66  	if !addr.IsValid() {
    67  		err = perrors.NewPF("addr cannot be invalid")
    68  		return
    69  	}
    70  
    71  	// try zone
    72  	zone, znum, hasZone, isNumeric := Zone(addr)
    73  	if hasZone {
    74  		if !isNumeric {
    75  			if netInterface, err = net.InterfaceByName(zone); perrors.IsPF(&err, "net.InterfaceByName zone: %q %w", zone, err) {
    76  				isNoSuchInterface = errors.Is(err, ErrNoSuchInterface)
    77  				return
    78  			}
    79  		} else {
    80  			if netInterface, err = net.InterfaceByIndex(znum); perrors.IsPF(&err, "net.InterfaceByName zone-numeric: %d %w", znum, err) {
    81  				isNoSuchInterface = errors.Is(err, ErrNoSuchInterface)
    82  				return
    83  			}
    84  		}
    85  		if prefix, err = InterfacePrefix(netInterface, addr); err != nil {
    86  			return
    87  		}
    88  	} else {
    89  
    90  		// scan interfaces to find a network prefix
    91  		var interfaces []net.Interface
    92  		if interfaces, err = net.Interfaces(); perrors.IsPF(&err, "net.Interfaces %w", err) {
    93  			return
    94  		}
    95  		for i := 0; i < len(interfaces); i++ {
    96  			var ifp = &interfaces[i]
    97  			if prefix, err = InterfacePrefix(ifp, addr); err != nil {
    98  				return
    99  			} else if prefix.IsValid() {
   100  				netInterface = ifp
   101  				break
   102  			}
   103  		}
   104  	}
   105  
   106  	if isNoSuchInterface = netInterface == nil; isNoSuchInterface {
   107  		err = perrors.ErrorfPF("no network interface has address: %s", addr)
   108  		return
   109  	}
   110  
   111  	if !prefix.IsValid() {
   112  		err = perrors.ErrorfPF("network interface %s does not have address: %s", netInterface.Name, addr)
   113  		return
   114  	}
   115  
   116  	return
   117  }
   118  
   119  // InterfacePrefix returns the network prefix assigned to netInterface that contains addr
   120  //   - if addr is not part of any prefix, returned prefix is invalid
   121  func InterfacePrefix(netInterface *net.Interface, addr netip.Addr) (prefix netip.Prefix, err error) {
   122  	var i4, i6 []netip.Prefix
   123  	if i4, i6, err = InterfaceAddrs(netInterface); err != nil {
   124  		return
   125  	}
   126  	if addr.Is4() {
   127  		for _, i4p := range i4 {
   128  			if i4p.Contains(addr) {
   129  				prefix = i4p
   130  				return // interface name by finding assigned IP
   131  			}
   132  		}
   133  		var a6 = netip.AddrFrom16(addr.As16())
   134  		for _, i6p := range i6 {
   135  			if i6p.Contains(a6) {
   136  				prefix = i6p
   137  				return // interface name by finding assigned IP
   138  			}
   139  		}
   140  	} else {
   141  		for _, i6p := range i6 {
   142  			if i6p.Contains(addr) {
   143  				prefix = i6p
   144  				return // interface name by finding assigned IP
   145  			}
   146  		}
   147  	}
   148  	return
   149  }