Customize Consent Preferences

We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.

The cookies that are categorized as "Necessary" are stored on your browser as they are essential for enabling the basic functionalities of the site. ... 

Always Active

Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.

No cookies to display.

Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.

No cookies to display.

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.

No cookies to display.

Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.

No cookies to display.

Advertisement cookies are used to provide visitors with customized advertisements based on the pages you visited previously and to analyze the effectiveness of the ad campaigns.

No cookies to display.

Skip to content Skip to footer

Cross-Platform Way of Obtaining MAC Address of Your Machine

In one of my current projects I had to implement client-server communication and protection by MAC address when client machine can’t connect to server if its MAC address is not allowed, regardless of network or broadband connection. But what was a surprise that wxWidgets does not have API which allows obtaining MAC address in cross-platform way. So, I decided to write a small class which allows obtainig MAC address for Windows, Linux, Mac OS and Windows Mobile. Here it is:

MACAddressUtility.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef _MACADDRESS_UTILITY_H
#define _MACADDRESS_UTILITY_H
 
class MACAddressUtility
{
public:
    static long GetMACAddress(unsigned char * result);
private:
#if defined(WIN32) || defined(UNDER_CE)
    static long GetMACAddressMSW(unsigned char * result);
#elif defined(__APPLE__)
    static long GetMACAddressMAC(unsigned char * result);
#elif defined(LINUX) || defined(linux)
    static long GetMACAddressLinux(unsigned char * result);
#endif
};
 
#endif

MACAddressUtility.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#include "MACAddressUtility.h"
 
#include <stdio.h>
 
#if defined(WIN32) || defined(UNDER_CE)
#   include <windows.h>
#   if defined(UNDER_CE)
#       include <Iphlpapi.h>
#   endif
#elif defined(__APPLE__)
#   include <CoreFoundation/CoreFoundation.h>
#   include <IOKit/IOKitLib.h>
#   include <IOKit/network/IOEthernetInterface.h>
#   include <IOKit/network/IONetworkInterface.h>
#   include <IOKit/network/IOEthernetController.h>
#elif defined(LINUX) || defined(linux)
#   include <string.h>
#   include <net/if.h>
#   include <sys/ioctl.h>
#   include <sys/socket.h>
#   include <arpa/inet.h>
#endif
 
long MACAddressUtility::GetMACAddress(unsigned char * result)
{
    // Fill result with zeroes
    memset(result, 0, 6);
    // Call appropriate function for each platform
#if defined(WIN32) || defined(UNDER_CE)
    return GetMACAddressMSW(result);
#elif defined(__APPLE__)
    return GetMACAddressMAC(result);
#elif defined(LINUX) || defined(linux)
    return GetMACAddressLinux(result);
#endif
    // If platform is not supported then return error code
    return -1;
}
 
#if defined(WIN32) || defined(UNDER_CE)
 
inline long MACAddressUtility::GetMACAddressMSW(unsigned char * result)
{
 
#if defined(UNDER_CE)
    IP_ADAPTER_INFO AdapterInfo[16]; // Allocate information
    DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
    if(GetAdaptersInfo(AdapterInfo, &dwBufLen) == ERROR_SUCCESS)
    {
        memcpy(result, AdapterInfo->Address, 6);
    }
    else return -1;
#else
    UUID uuid;
    if(UuidCreateSequential(&uuid) == RPC_S_UUID_NO_ADDRESS) return -1;
    memcpy(result, (char*)(uuid.Data4+2), 6);
#endif
    return 0;
}
 
#elif defined(__APPLE__)
 
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices)
{
    kern_return_t       kernResult;
    CFMutableDictionaryRef  matchingDict;
    CFMutableDictionaryRef  propertyMatchDict;
 
    matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
 
    if (NULL != matchingDict)
    {
        propertyMatchDict =
            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                &kCFTypeDictionaryKeyCallBacks,
                &kCFTypeDictionaryValueCallBacks);
 
        if (NULL != propertyMatchDict)
        {
            CFDictionarySetValue(propertyMatchDict,
                CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
            CFDictionarySetValue(matchingDict,
                CFSTR(kIOPropertyMatchKey), propertyMatchDict);
            CFRelease(propertyMatchDict);
        }
    }
    kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault,
        matchingDict, matchingServices);
    return kernResult;
}
 
static kern_return_t GetMACAddress(io_iterator_t intfIterator,
                                   UInt8 *MACAddress, UInt8 bufferSize)
{
    io_object_t     intfService;
    io_object_t     controllerService;
    kern_return_t   kernResult = KERN_FAILURE;
 
    if (bufferSize < kIOEthernetAddressSize) {
        return kernResult;
    }
 
    bzero(MACAddress, bufferSize);
 
    while (intfService = IOIteratorNext(intfIterator))
    {
        CFTypeRef   MACAddressAsCFData;
 
        // IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call,
        // since they are hardware nubs and do not participate in driver matching. In other words,
        // registerService() is never called on them. So we've found the IONetworkInterface and will
        // get its parent controller by asking for it specifically.
 
        // IORegistryEntryGetParentEntry retains the returned object,
        // so release it when we're done with it.
        kernResult =
            IORegistryEntryGetParentEntry(intfService,
                kIOServicePlane,
                &controllerService);
 
        if (KERN_SUCCESS != kernResult) {
            printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult);
        }
        else {
            // Retrieve the MAC address property from the I/O Registry in the form of a CFData
            MACAddressAsCFData =
                IORegistryEntryCreateCFProperty(controllerService,
                    CFSTR(kIOMACAddress),
                    kCFAllocatorDefault,
                    0);
            if (MACAddressAsCFData) {
                CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr
 
                // Get the raw bytes of the MAC address from the CFData
                CFDataGetBytes((CFDataRef)MACAddressAsCFData,
                    CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
                CFRelease(MACAddressAsCFData);
            }
 
            // Done with the parent Ethernet controller object so we release it.
            (void) IOObjectRelease(controllerService);
        }
 
        // Done with the Ethernet interface object so we release it.
        (void) IOObjectRelease(intfService);
    }
 
    return kernResult;
}
 
long MACAddressUtility::GetMACAddressMAC(unsigned char * result)
{
    io_iterator_t   intfIterator;
    kern_return_t   kernResult = KERN_FAILURE;
    do
    {
        kernResult = ::FindEthernetInterfaces(&intfIterator);
        if (KERN_SUCCESS != kernResult) break;
        kernResult = ::GetMACAddress(intfIterator, (UInt8*)result, 6);
    }
    while(false);
    (void) IOObjectRelease(intfIterator);
}
 
#elif defined(LINUX) || defined(linux)
 
long MACAddressUtility::GetMACAddressLinux(unsigned char * result)
{
    struct ifreq ifr;
    struct ifreq *IFR;
    struct ifconf ifc;
    char buf[1024];
    int s, i;
    int ok = 0;
 
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s == -1)
    {
        return -1;
    }
 
    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    ioctl(s, SIOCGIFCONF, &ifc);
 
    IFR = ifc.ifc_req;
    for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; IFR++)
    {
        strcpy(ifr.ifr_name, IFR->ifr_name);
        if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0)
        {
            if (! (ifr.ifr_flags & IFF_LOOPBACK))
            {
                if (ioctl(s, SIOCGIFHWADDR, &ifr) == 0)
                {
                    ok = 1;
                    break;
                }
            }
        }
    }
 
    shutdown(s, SHUT_RDWR);
    if (ok)
    {
        bcopy( ifr.ifr_hwaddr.sa_data, result, 6);
    }
    else
    {
        return -1;
    }
    return 0;
}
 
#endif

wxMACAddressUtility.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef _WX_MACADDRESS_UTILITY_H
#define _WX_MACADDRESS_UTILITY_H
 
#include "MACAddressUtility.h"
#include <wx/wx.h>
 
class wxMACAddressUtility
{
public:
    static wxString GetMACAddress()
    {
        unsigned char result[6];
        if(MACAddressUtility::GetMACAddress(result) == 0)
        {
            return wxString::Format(wxT("%02X:%02X:%02X:%02X:%02X:%02X"),
                (unsigned int)result[0], (unsigned int)result[1], (unsigned int)result[2],
                (unsigned int)result[3], (unsigned int)result[4], (unsigned int)result[5]);
        }
        return wxEmptyString;
    }
};
 
#endif

Sample of usage:

1
m_MACAddress = wxMACAddressUtility::GetMACAddress();

Get MAC address programmatically

As you can see, core class does not depend on wxWidgets and can be used in native applications written without wxWidgets.

Download sample application

3 Comments

  • Alatar
    Posted September 4, 2009 at 14:57

    А можно немного покритиковать?
    1) Если уж делаем кросплатформенно, то пусть будет кроссплатформенно, а то что это такое? Под виндой у меня не собирается, говорит, что не знает кто такой UuidCreateSequential, про мою любимую FreeBSD вообще забыли… Под Линуксом и Маком не проверял – под рукой не оказалось. Кстати, если не секрет, зачем так сложно под Mac?
    Короче, мой вариант см тут – http://paste.org/pastebin/view/10239
    источники, из которых собирал:
    http://support.microsoft.com/kb/118623
    http://www.cs.williams.edu/~morgan/code/C++/getip.cpp
    2) Что вообще такое MAC-адрес компьютера? MAC-адрес может быть сетевой карточки, а их в компе может быть несколько. Получается, адрес первой попавшейся карточки. А если я захочу ещё одну сетевушку воткнуть? Или VPN поднять? Или ещё чего? Аутентификация может начать обламывать, что не есть хорошо. =) Для идентификации лучше бы использовать что-нить типа идентификатора материнки, хотя это, конечно, уже сложнее. Что же касается этих функций – неплохо бы было в качестве параметра передовать интерфейс, для которого нужно получить MAC. Либо IP – по нему найти нужный интерфейс уже не сложно.
    Всё это, конечно, моё личное ИМХО =)

  • T-Rex
    Posted September 4, 2009 at 15:32

    Под винду не собирается? А какая у вас винда?
    http://msdn.microsoft.com/en-us/library/aa379322%28VS.85%29.aspx
    говорит что минимальное требование Windows 2000 Professional.

    FreeBSD вообще забыли…

    Ммм? Так это.. не надо было мне ФриБСД, только линух/мак/винда. Если есть вариант под BSD – ок. 🙂 я только за.

    MAC-адрес может быть сетевой карточки, а их в компе может быть несколько

    Угу.. может. Задача у меня стояла как “запретить повторный коннект с одного мак адреса”. Если два раза одним и тем же алгоритмом выбрать первый мак-адрес то не важно какой там интерфейс идет первым.
    Ну а так – код в public domain – каждый дорабатывает как хочет. 🙂

    то же касается этих функций – неплохо бы было в качестве параметра передовать интерфейс, для которого нужно получить MAC. Либо IP – по нему найти нужный интерфейс уже не сложно.

    Даете вариант с параметром? Согласен выложить для общего обозрения если есть 🙂

    И еще, на пасторге сорцы долго хранятся? А то если удалят вариант для FreeBSD будет печально. Может сюда запостите?

  • Alatar
    Posted September 4, 2009 at 18:47

    Я не знаю, сколько там хранят – первый раз пользуюсь, раньше не было нужды. Просто постить большие куски кода в комменты не красиво, да и не знаю я, как тут форматирование нормальное сделать, а для отдельного поста в своём ЖЖ материала маловато. Так что Вы уж сами запостите оттуда сюда, если не сложно.

    Винда у меня XP. Проверял на двух компах –
    g++ main.cc
    main.cc: In static member function `static long int MACAddressUtility::GetMACAddressMSW(unsigned char*)’:
    main.cc:67: error: `UuidCreateSequential’ was not declared in this scope
    При этом если заменить UuidCreateSequential на UuidCreate – собирается, но, естественно, выдаёт ерунду. По всей видимости, надо ставить VS со свежим SDK. (у меня стоит только MinGW)
    По поводу BSD – это я просто справедливости ради – всё время про неё забывают. Тем более, обратите внимание, в моём варианте для *BSD/MacOS/Linux используется одна и таже функция с небольшим ifdef`ом.
    Вариант с параметрами? Был бы – выложил бы его, чего мудрить =) Я этот-то на скорую руку слепил из Вашего кода. Будет свободное время, сделаю, интереса ради =) Только у меня возможности по тестированию ограничены – Windows, FreeBSD и, в ограниченных количествах, MacOS.

Leave a comment

0.0/5