github.com/haraldrudell/parl@v0.4.176/pnet/linkaddr.go (about) 1 /* 2 © 2020–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 "strconv" 12 "strings" 13 14 "github.com/haraldrudell/parl" 15 "github.com/haraldrudell/parl/perrors" 16 "golang.org/x/exp/slices" 17 ) 18 19 const ( 20 HardwareAddrMac48 = 6 21 HardwareAddrEui64 = 8 22 HardwareAddrInfini = 20 23 ) 24 25 // ErrNoSuchInterface is the value net.errNoSuchInterface 26 // 27 // Usage: 28 // 29 // if errors.Is(err, pnet.ErrNoSuchInterface) { … 30 var ErrNoSuchInterface = func() (err error) { 31 for _, e := net.InterfaceByName("a b"); e != nil; e = errors.Unwrap(e) { 32 err = e 33 } 34 if err == nil { 35 panic(perrors.NewPF("failed to obtain NoSuchInterface from InterfaceByName")) 36 } 37 return 38 }() 39 40 var HardwareAddrLengths = []int{HardwareAddrMac48, HardwareAddrEui64, HardwareAddrInfini} 41 var HardwareAddrLengthsWithZero = append([]int{0}, HardwareAddrLengths...) 42 43 func IsHardwareAddrLength(byts []byte) (isHardwareAddrLength bool) { 44 return slices.Contains(HardwareAddrLengths, len(byts)) 45 } 46 47 // LinkAddr contains an Ethernet mac address, its interface name and interface index 48 type LinkAddr struct { 49 IfIndex // 0 is none 50 Name string // "" none 51 net.HardwareAddr // []byte 52 } 53 54 // NewLinkAddr instantiates LinkAddr 55 func NewLinkAddr(index IfIndex, name string) (linkAddr *LinkAddr) { 56 return &LinkAddr{ 57 IfIndex: index, 58 Name: name, 59 } 60 } 61 62 func (a *LinkAddr) UpdateFrom(b *LinkAddr) (isComplete bool) { 63 if !a.IfIndex.IsValid() && b.IfIndex.IsValid() { 64 a.IfIndex = b.IfIndex 65 } 66 if a.Name == "" && b.Name != "" { 67 a.Name = b.Name 68 } 69 if len(a.HardwareAddr) == 0 && len(b.HardwareAddr) > 0 { 70 a.HardwareAddr = b.HardwareAddr 71 } 72 return a.IsComplete() 73 } 74 75 func (a *LinkAddr) SetHw(hw net.HardwareAddr) (err error) { 76 if !slices.Contains(HardwareAddrLengthsWithZero, len(hw)) { 77 err = perrors.ErrorfPF("hardware address bad length: %d allowed: [%v]", hw) 78 return 79 } else if len(hw) > 0 { 80 a.HardwareAddr = hw 81 } 82 return 83 } 84 85 func (a *LinkAddr) SetName(name string) { 86 a.Name = name 87 } 88 89 // UpdateName attempts to populate interface name if not already present 90 func (a *LinkAddr) UpdateName() (linkAddr *LinkAddr, err error) { 91 linkAddr = a 92 if a.Name != "" { 93 return // name already present return 94 } 95 var name string 96 if name, _, err = a.IfIndex.Zone(); err != nil { 97 return // error while getting interface data return 98 } 99 if name == "" { 100 return // no new name obtained return 101 } 102 linkAddr = &LinkAddr{ 103 IfIndex: a.IfIndex, 104 Name: name, 105 HardwareAddr: a.HardwareAddr, 106 } 107 return // name updated return 108 } 109 110 // Interface returns net.Interface associated with LinkAddr 111 // - order is index, name, mac 112 // - if LinkAddr is zero-value, nil is returned 113 func (a *LinkAddr) Interface() (netInterface *net.Interface, isNoSuchInterface bool, err error) { 114 if a.IfIndex.IsValid() { 115 return a.IfIndex.Interface() 116 } else if a.Name != "" { 117 if netInterface, err = net.InterfaceByName(a.Name); err != nil { 118 isNoSuchInterface = errors.Is(err, ErrNoSuchInterface) 119 err = perrors.Errorf("net.InterfaceByName %w", err) 120 } 121 return 122 } else if len(a.HardwareAddr) > 0 { 123 return HardwareAddrInterface(a.HardwareAddr) 124 } 125 return // zero-value: netInterface nil return 126 } 127 128 // ZoneID is the IPv6 ZoneID for this interface 129 func (a *LinkAddr) ZoneID() string { 130 if a != nil { 131 if a.Name != "" { 132 return a.Name 133 } else if a.IfIndex > 0 { 134 return strconv.Itoa(int(a.IfIndex)) 135 } 136 } 137 return "0" 138 } 139 140 // OneString picks the most meaningful value 141 // - interface name or hardware address or #interface index or "0" 142 func (a *LinkAddr) OneString() string { 143 if a != nil { 144 if a.Name != "" { 145 return a.Name 146 } else if len(a.HardwareAddr) > 0 { 147 return a.HardwareAddr.String() 148 } else if a.IfIndex > 0 { 149 return "#" + strconv.Itoa(int(a.IfIndex)) 150 } 151 } 152 return "0" 153 } 154 155 func (a *LinkAddr) IsValid() (isValid bool) { 156 return a.IfIndex != 0 || 157 a.Name != "" || 158 len(a.HardwareAddr) > 0 159 } 160 161 func (a *LinkAddr) IsZeroValue() (isZeroValue bool) { 162 return !a.IfIndex.IsValid() && 163 a.Name == "" && 164 a.HardwareAddr == nil 165 } 166 167 func (a *LinkAddr) IsComplete() (isComplete bool) { 168 return a.IfIndex.IsValid() && 169 a.Name != "" && 170 len(a.HardwareAddr) > 0 171 } 172 173 // "#13_en5_00:00:5e:00:53:01" 174 // - zero-values are skipped 175 // - zero-value: "zero-value" 176 func (a *LinkAddr) NonZero() (s string) { 177 var sL []string 178 if a.IfIndex != 0 { 179 sL = append(sL, a.IfIndex.String()) // "#13" 180 } 181 if a.Name != "" { 182 sL = append(sL, a.Name) 183 } 184 if len(a.HardwareAddr) > 0 { 185 sL = append(sL, a.HardwareAddr.String()) // "00:00:5e:00:53:01" 186 } 187 if len(sL) > 0 { 188 s = strings.Join(sL, "_") 189 } else { 190 s = "zero-value" 191 } 192 return 193 } 194 195 func (a *LinkAddr) Dump() (s string) { 196 return parl.Sprintf("linkAddr#%d%q_hw%s", 197 a.IfIndex, 198 a.Name, 199 a.HardwareAddr.String(), 200 ) 201 } 202 203 // "en8(28)00:11:22:33:44:55:66" 204 func (a *LinkAddr) String() (s string) { 205 if len(a.Name) > 0 { 206 s += a.Name 207 } 208 if a.IfIndex > 0 { 209 s += "(" + strconv.Itoa(int(a.IfIndex)) + ")" 210 } 211 s += a.HardwareAddr.String() 212 return 213 }