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 }