Mac OS X で ioctl/SIOCGIFADDR/SIOCGIFCONF を使用してインターフェイス情報を取得する方法を理解するのに問題がありますか?
Linux で正常に動作するコードを Mac OS X で動作させるのに、今日はかなり苦労しました。
コピーして貼り付けるmain.c
と機能するgcc main.c && ./a.out
はずです(すべてのネットワークインターフェイス、それらのipv4 / 6アドレス、ネットマスク、および関連付けられている場合はMACアドレスをリストします):
Mac OSXおよびiOS iPad/iPhoneで正常に動作します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <ifaddrs.h>
#include <errno.h>
int main() {
struct ifaddrs *if_addrs = NULL;
struct ifaddrs *if_addr = NULL;
void *tmp = NULL;
char buf[INET6_ADDRSTRLEN];
if (0 == getifaddrs(&if_addrs)) {
for (if_addr = if_addrs; if_addr != NULL; if_addr = if_addr->ifa_next) {
printf("name : %s\n", if_addr->ifa_name);
// Address
if (if_addr->ifa_addr->sa_family == AF_INET) {
tmp = &((struct sockaddr_in *)if_addr->ifa_addr)->sin_addr;
} else {
tmp = &((struct sockaddr_in6 *)if_addr->ifa_addr)->sin6_addr;
}
printf("addr : %s\n",
inet_ntop(if_addr->ifa_addr->sa_family,
tmp,
buf,
sizeof(buf)));
// Mask
if (if_addr->ifa_netmask != NULL) {
if (if_addr->ifa_netmask->sa_family == AF_INET) {
tmp = &((struct sockaddr_in *)if_addr->ifa_netmask)->sin_addr;
} else {
tmp = &((struct sockaddr_in6 *)if_addr->ifa_netmask)->sin6_addr;
}
printf("mask : %s\n",
inet_ntop(if_addr->ifa_netmask->sa_family,
tmp,
buf,
sizeof(buf)));
}
// MAC address
if (if_addr->ifa_addr != NULL && if_addr->ifa_addr->sa_family == AF_LINK) {
struct sockaddr_dl* sdl = (struct sockaddr_dl *)if_addr->ifa_addr;
unsigned char mac[6];
if (6 == sdl->sdl_alen) {
memcpy(mac, LLADDR(sdl), sdl->sdl_alen);
printf("mac : %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
}
printf("\n");
}
freeifaddrs(if_addrs);
if_addrs = NULL;
} else {
printf("getifaddrs() failed with errno = %i %s\n", errno, strerror(errno));
return -1;
}
}
MAC アドレスを取得するメカニズムは、BSD 由来の OS と Linux ではまったく異なります。これには OS X が含まれます。
Linux と OS X、そしておそらく BSD でも動作する、私が使用するコードを次に示します。
#if defined(HAVE_SIOCGIFHWADDR)
bool get_mac_address(char* mac_addr, const char* if_name = "eth0")
{
struct ifreq ifinfo;
strcpy(ifinfo.ifr_name, if_name);
int sd = socket(AF_INET, SOCK_DGRAM, 0);
int result = ioctl(sd, SIOCGIFHWADDR, &ifinfo);
close(sd);
if ((result == 0) && (ifinfo.ifr_hwaddr.sa_family == 1)) {
memcpy(mac_addr, ifinfo.ifr_hwaddr.sa_data, IFHWADDRLEN);
return true;
}
else {
return false;
}
}
#elif defined(HAVE_GETIFADDRS)
bool get_mac_address(char* mac_addr, const char* if_name = "en0")
{
ifaddrs* iflist;
bool found = false;
if (getifaddrs(&iflist) == 0) {
for (ifaddrs* cur = iflist; cur; cur = cur->ifa_next) {
if ((cur->ifa_addr->sa_family == AF_LINK) &&
(strcmp(cur->ifa_name, if_name) == 0) &&
cur->ifa_addr) {
sockaddr_dl* sdl = (sockaddr_dl*)cur->ifa_addr;
memcpy(mac_addr, LLADDR(sdl), sdl->sdl_alen);
found = true;
break;
}
}
freeifaddrs(iflist);
}
return found;
}
#else
# error no definition for get_mac_address() on this platform!
#endif
HAVE_*
プラットフォームに適切なマクロを定義する方法を理解するのはあなた次第です。私はたまたまこれに autoconf を使用していますが、プラットフォームの違いに対処する別の方法があるかもしれません。
これらの関数のデフォルトのインターフェイス名パラメーターは、Linux および OS X ボックスの最初のイーサネット インターフェイスのデフォルトであることに注意してください。他の OS ではこれをオーバーライドするか、別のインターフェイスの MAC アドレスに関心がある場合は別の値を渡す必要がある場合があります。
このスレッドは私の問題と多少一致しました:
http://discussions.apple.com/thread.jspa?messageID=10935410&tstart=0
このスレッドは大いに役立ちました:
https://lists.isc.org/pipermail/dhcp-hackers/2007-September/000767.html
そのスレッドは、代わりに getifaddrs() を使用する必要があると最終的に述べているためです。Ubuntu 10.04 のマニュアル ページには、getifaddrs の使用方法の優れた例があり、それを参照として使用することで、Mac と Linux の両方で機能するコードを理解することができました。他の誰かがこのような単純なことに時間を無駄にしたくないので、ここに投稿して自分自身に答えています. 私の投稿がお役に立てば幸いです...