Having a problem figuring out how to get Ethernet interface info on Mac OS X using ioctl/SIOCGIFADDR/SIOCGIFCONF?

爷,独闯天下 提交于 2019-12-09 12:55:48

问题


Are you having a problem figuring out how to get interface info on Mac OS X using ioctl/SIOCGIFADDR/SIOCGIFCONF?

I had a lot of trouble getting code that worked fine on Linux to work on Mac OS X today.


回答1:


Copy-paste to main.c and gcc main.c && ./a.out should work (lists all network interfaces, their ipv4/6 address, netmask and MAC address if associated):

Works fine on Mac OSX and 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;
  }
}



回答2:


The mechanism to get MAC addresses is entirely different on BSD-derived OSes than on Linux. This includes OS X.

Here's code I use that works on Linux and OS X, and probably on the BSDs, too:

#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

It's up to you to work out how to get the right HAVE_* macro defined for the platform. I happen to use autoconf for this, but you may have another way of dealing with platform differences.

Notice that the default interface name parameter for these functions is the default for the first Ethernet interface on Linux and OS X boxes. You may need to override this for other OSes, or pass another value if you're interested in the MAC address for a different interface.




回答3:


This thread matched my problem somewhat:

http://discussions.apple.com/thread.jspa?messageID=10935410&tstart=0

This thread helped a lot:

https://lists.isc.org/pipermail/dhcp-hackers/2007-September/000767.html

because that thread eventually mentions that getifaddrs() should be used instead. The man page on Ubuntu 10.04 had a great example of how to use getifaddrs and using it as a reference helped me figure out code that worked on both Mac and Linux. I don't want anyone else to waste time on something so simple, so I'm posting and answering myself here. Hoping that my post helps you...



来源:https://stackoverflow.com/questions/3964494/having-a-problem-figuring-out-how-to-get-ethernet-interface-info-on-mac-os-x-usi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!