libdoip  0.1.0
DoIP (Diagnostics over Internet Protocol) ISO 13400 C++17 Library
MacAddress.cpp
Go to the documentation of this file.
1 #include "MacAddress.h"
2 #include <cstring>
3 
4 // Platform detection
5 #if defined(_WIN32) || defined(_WIN64)
6  #define PLATFORM_WINDOWS
7  #include <winsock2.h>
8  #include <iphlpapi.h>
9  #pragma comment(lib, "iphlpapi.lib")
10 #elif defined(__APPLE__) && defined(__MACH__)
11  #define PLATFORM_MACOS
12  #include <sys/socket.h>
13  #include <sys/sysctl.h>
14  #include <net/if.h>
15  #include <net/if_dl.h>
16  #include <ifaddrs.h>
17  #include <arpa/inet.h>
18 #elif defined(__linux__)
19  #define PLATFORM_LINUX
20  #include <sys/ioctl.h>
21  #include <sys/socket.h>
22  #include <netinet/in.h>
23  #include <net/if.h>
24  #include <unistd.h>
25  #include <cstring>
26  #include <ifaddrs.h>
27 #else
28  #error "Unsupported platform"
29 #endif
30 
31 namespace doip {
32 
33 #ifdef PLATFORM_LINUX
34 
35 /**
36  * @brief Linux implementation using ioctl with SIOCGIFHWADDR
37  */
38 bool getMacAddress(const char* ifname, MacAddress& mac) {
39  if (ifname == nullptr) {
40  return getFirstMacAddress(mac);
41  }
42 
43  int sock = socket(AF_INET, SOCK_DGRAM, 0);
44  if (sock < 0) {
45  return false;
46  }
47 
48  struct ifreq ifr;
49  std::memset(&ifr, 0, sizeof(ifr));
50  std::strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
51  ifr.ifr_name[IFNAMSIZ - 1] = '\0';
52 
53  if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
54  close(sock);
55  return false;
56  }
57 
58  close(sock);
59 
60  // Copy MAC address from ifr_hwaddr.sa_data
61  std::memcpy(mac.data(), ifr.ifr_hwaddr.sa_data, 6);
62 
63  return true;
64 }
65 
66 bool getFirstMacAddress(MacAddress& mac) {
67  struct ifaddrs* ifaddr = nullptr;
68  struct ifaddrs* ifa = nullptr;
69 
70  if (getifaddrs(&ifaddr) == -1) {
71  return false;
72  }
73 
74  bool found = false;
75 
76  // Iterate through all interfaces
77  for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
78  if (ifa->ifa_addr == nullptr) {
79  continue;
80  }
81 
82  // Skip loopback interface
83  if (std::strcmp(ifa->ifa_name, "lo") == 0) {
84  continue;
85  }
86 
87  // Try to get MAC address for this interface
88  if (getMacAddress(ifa->ifa_name, mac)) {
89  // Check if it's not all zeros
90  bool isZero = true;
91  for (size_t i = 0; i < 6; ++i) {
92  if (mac[i] != 0) {
93  isZero = false;
94  break;
95  }
96  }
97 
98  if (!isZero) {
99  found = true;
100  break;
101  }
102  }
103  }
104 
105  freeifaddrs(ifaddr);
106  return found;
107 }
108 
109 #elif defined(PLATFORM_MACOS)
110 
111 /**
112  * @brief macOS implementation using getifaddrs and AF_LINK
113  */
114 bool getMacAddress(const char* ifname, MacAddress& mac) {
115  if (ifname == nullptr) {
116  return getFirstMacAddress(mac);
117  }
118 
119  struct ifaddrs* ifaddr = nullptr;
120  struct ifaddrs* ifa = nullptr;
121 
122  if (getifaddrs(&ifaddr) == -1) {
123  return false;
124  }
125 
126  bool found = false;
127 
128  for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
129  if (ifa->ifa_addr == nullptr) {
130  continue;
131  }
132 
133  // Check if this is the interface we're looking for
134  if (std::strcmp(ifa->ifa_name, ifname) != 0) {
135  continue;
136  }
137 
138  // Check if this is a link-layer address (AF_LINK for macOS)
139  if (ifa->ifa_addr->sa_family == AF_LINK) {
140  struct sockaddr_dl* sdl = reinterpret_cast<struct sockaddr_dl*>(ifa->ifa_addr);
141 
142  if (sdl->sdl_alen == 6) {
143  unsigned char* ptr = reinterpret_cast<unsigned char*>(LLADDR(sdl));
144  std::memcpy(mac.data(), ptr, 6);
145  found = true;
146  break;
147  }
148  }
149  }
150 
151  freeifaddrs(ifaddr);
152  return found;
153 }
154 
155 bool getFirstMacAddress(MacAddress& mac) {
156  struct ifaddrs* ifaddr = nullptr;
157  struct ifaddrs* ifa = nullptr;
158 
159  if (getifaddrs(&ifaddr) == -1) {
160  return false;
161  }
162 
163  bool found = false;
164 
165  for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
166  if (ifa->ifa_addr == nullptr) {
167  continue;
168  }
169 
170  // Skip loopback interface
171  if (std::strcmp(ifa->ifa_name, "lo0") == 0) {
172  continue;
173  }
174 
175  // Check if this is a link-layer address
176  if (ifa->ifa_addr->sa_family == AF_LINK) {
177  struct sockaddr_dl* sdl = reinterpret_cast<struct sockaddr_dl*>(ifa->ifa_addr);
178 
179  if (sdl->sdl_alen == 6) {
180  unsigned char* ptr = reinterpret_cast<unsigned char*>(LLADDR(sdl));
181 
182  // Check if it's not all zeros
183  bool isZero = true;
184  for (int i = 0; i < 6; ++i) {
185  if (ptr[i] != 0) {
186  isZero = false;
187  break;
188  }
189  }
190 
191  if (!isZero) {
192  std::memcpy(mac.data(), ptr, 6);
193  found = true;
194  break;
195  }
196  }
197  }
198  }
199 
200  freeifaddrs(ifaddr);
201  return found;
202 }
203 
204 #elif defined(PLATFORM_WINDOWS)
205 
206 /**
207  * @brief Windows implementation using IP Helper API
208  */
209 bool getMacAddress(const char* ifname, MacAddress& mac) {
210  if (ifname == nullptr) {
211  return getFirstMacAddress(mac);
212  }
213 
214  ULONG bufferSize = 15000;
215  PIP_ADAPTER_INFO adapterInfo = nullptr;
216  PIP_ADAPTER_INFO adapter = nullptr;
217 
218  adapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new BYTE[bufferSize]);
219 
220  if (GetAdaptersInfo(adapterInfo, &bufferSize) == ERROR_BUFFER_OVERFLOW) {
221  delete[] reinterpret_cast<BYTE*>(adapterInfo);
222  adapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new BYTE[bufferSize]);
223  }
224 
225  bool found = false;
226 
227  if (GetAdaptersInfo(adapterInfo, &bufferSize) == NO_ERROR) {
228  adapter = adapterInfo;
229 
230  while (adapter) {
231  // Compare adapter name or description
232  if (std::strcmp(adapter->AdapterName, ifname) == 0 ||
233  std::strcmp(adapter->Description, ifname) == 0) {
234 
235  if (adapter->AddressLength == 6) {
236  std::memcpy(mac.data(), adapter->Address, 6);
237  found = true;
238  break;
239  }
240  }
241  adapter = adapter->Next;
242  }
243  }
244 
245  delete[] reinterpret_cast<BYTE*>(adapterInfo);
246  return found;
247 }
248 
249 bool getFirstMacAddress(MacAddress& mac) {
250  ULONG bufferSize = 15000;
251  PIP_ADAPTER_INFO adapterInfo = nullptr;
252  PIP_ADAPTER_INFO adapter = nullptr;
253 
254  adapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new BYTE[bufferSize]);
255 
256  if (GetAdaptersInfo(adapterInfo, &bufferSize) == ERROR_BUFFER_OVERFLOW) {
257  delete[] reinterpret_cast<BYTE*>(adapterInfo);
258  adapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new BYTE[bufferSize]);
259  }
260 
261  bool found = false;
262 
263  if (GetAdaptersInfo(adapterInfo, &bufferSize) == NO_ERROR) {
264  adapter = adapterInfo;
265 
266  // Find first non-loopback, non-zero MAC address
267  while (adapter) {
268  if (adapter->AddressLength == 6) {
269  // Check if it's not all zeros
270  bool isZero = true;
271  for (UINT i = 0; i < 6; ++i) {
272  if (adapter->Address[i] != 0) {
273  isZero = false;
274  break;
275  }
276  }
277 
278  if (!isZero && adapter->Type == MIB_IF_TYPE_ETHERNET) {
279  std::memcpy(mac.data(), adapter->Address, 6);
280  found = true;
281  break;
282  }
283  }
284  adapter = adapter->Next;
285  }
286  }
287 
288  delete[] reinterpret_cast<BYTE*>(adapterInfo);
289  return found;
290 }
291 
292 #endif
293 
294 } // namespace doip
Definition: AnsiColors.h:3
bool getFirstMacAddress(MacAddress &mac)
Retrieves the MAC address of the first available network interface.
bool getMacAddress(const char *ifname, MacAddress &mac)
std::array< uint8_t, 6 > MacAddress
Type alias for MAC address (6 bytes)
Definition: MacAddress.h:12