Wie erhalte ich die MAC-Adresse Ihres Computers mit einem C-Programm?

Antworten:

63

Sie müssen alle verfügbaren Schnittstellen auf Ihrem Computer durchlaufen und ioctlmit SIOCGIFHWADDRFlag verwenden, um die Mac-Adresse abzurufen. Die Mac-Adresse wird als 6-Oktett-Binärarray erhalten. Sie möchten auch die Loopback-Schnittstelle überspringen.

#include <sys/ioctl.h>
#include <net/if.h> 
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>

int main()
{
    struct ifreq ifr;
    struct ifconf ifc;
    char buf[1024];
    int success = 0;

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (sock == -1) { /* handle error*/ };

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ }

    struct ifreq* it = ifc.ifc_req;
    const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));

    for (; it != end; ++it) {
        strcpy(ifr.ifr_name, it->ifr_name);
        if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
            if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback
                if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
                    success = 1;
                    break;
                }
            }
        }
        else { /* handle error */ }
    }

    unsigned char mac_address[6];

    if (success) memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
}
Charles Salvia
quelle
1
Warum braucht dies keine Struktur vor ifreq und ifconf? Wurden Typedefs für diese Strukturen in neueren Kerneln entfernt, die 2009 existierten, als dies geschrieben wurde?
Karl P
Ich denke, es sollte beachtet werden, dass OpenVZ-Container keine MAC-Adresse haben und daher keine dieser Lösungen funktionieren würde. Die MAC-Adresse ist (angenommen)00:00:00:00:00:00
ub3rst4r
2
Es funktioniert nur, wenn auf eth0 ein Link vorhanden ist. Es funktioniert nicht, wenn das Netzwerkkabel abgezogen ist (unter Ubuntu).
Florin Petriuc
5
Vielleicht schließen (Socke)?
Art Swri
@ ArtSwri Ich denke, Sie hatten Recht, vielleicht Fehler Hanle goto einige LABLE und schließen (Socke)
J.Doe
135

Viel schöner als all dieser Socket- oder Shell-Wahnsinn ist es, einfach sysfs dafür zu verwenden:

Die Datei enthält /sys/class/net/eth0/addressIhre Mac-Adresse als einfache Zeichenfolge, die Sie mit fopen()/ fscanf()/ lesen können fclose(). Nichts einfacher als das.

Und wenn Sie andere Netzwerkschnittstellen als eth0 unterstützen möchten (und dies wahrscheinlich möchten), verwenden Sie einfach opendir()/ readdir()/ closedir()on /sys/class/net/.

user175104
quelle
3
Gute Antwort, aber nicht in allen Situationen anwendbar. zB eingebettete Systeme (insbesondere ältere, wie alte Versionen von Busybox, die weder über sysfs verfügen noch dieses unterstützen können, weil das System selbst möglicherweise zu alt ist)
Alex Marshall,
3
Die Frage speziell für eine C-Lösung gestellt
Charles Salvia
10
@CharlesSalvia Das Öffnen und Lesen einer Systemdatei scheint immer noch eine C-Lösung zu sein ... solange Sie wissen, was Ihr Zielsystem bietet! Zugegeben, Ihr Programm ist an diese Art von System gebunden. Das Kompilieren eines Programms bindet es jedoch an die Architektur eines Systems. Ich denke, diese Antwort wird in vielen (aber nicht allen) Situationen helfen.
Moodboom
(unter Verwendung von Debian-abgeleitetem Bunsen über WLAN) Siehe: / sys / class / net / wlp2s0 / address
AAAfarmclub
25

Sie möchten einen Blick auf die Handbuchseite von getifaddrs (3) werfen . Es gibt ein Beispiel in C in der Manpage selbst, das Sie verwenden können. Sie möchten die Adresse mit dem Typ erhalten AF_LINK.

Juliano
quelle
2
In der Manpage von verlinkten getifaddrs wird der Typ AF_LINKnicht beschrieben. Vielleicht wird dies durch ersetzt AF_PACKET?
Promonet
3
Laut stackoverflow.com/a/26038501/5230867 scheint AF_LINK macOS / BSD-spezifisch zu sein, während AF_PACKET das Linux-Äquivalent ist.
Manfred Urban
20
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>

int main()
{
  struct ifreq s;
  int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);

  strcpy(s.ifr_name, "eth0");
  if (0 == ioctl(fd, SIOCGIFHWADDR, &s)) {
    int i;
    for (i = 0; i < 6; ++i)
      printf(" %02x", (unsigned char) s.ifr_addr.sa_data[i]);
    puts("\n");
    return 0;
  }
  return 1;
}
Angestellt Russisch
quelle
Vergessen Sie nicht, den Socket () auf Fehler zu überprüfen und den Socket zu schließen, wenn er erfolgreich geöffnet wurde.
Anonymouse
17

Mit getifaddrs können Sie die MAC-Adresse von der Familie erhalten AF_PACKET.

Um die MAC-Adresse für jede Schnittstelle anzuzeigen, können Sie wie folgt vorgehen:

#include <stdio.h>
#include <ifaddrs.h>
#include <netpacket/packet.h>

int main (int argc, const char * argv[])
{
    struct ifaddrs *ifaddr=NULL;
    struct ifaddrs *ifa = NULL;
    int i = 0;

    if (getifaddrs(&ifaddr) == -1)
    {
         perror("getifaddrs");
    }
    else
    {
         for ( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
         {
             if ( (ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET) )
             {
                  struct sockaddr_ll *s = (struct sockaddr_ll*)ifa->ifa_addr;
                  printf("%-8s ", ifa->ifa_name);
                  for (i=0; i <s->sll_halen; i++)
                  {
                      printf("%02x%c", (s->sll_addr[i]), (i+1!=s->sll_halen)?':':'\n');
                  }
             }
         }
         freeifaddrs(ifaddr);
    }
    return 0;
}

Ideone

mpromonet
quelle
11

Ich habe gerade eine geschrieben und teste sie auf gentoo in virtualbox.

// get_mac.c
#include <stdio.h>    //printf
#include <string.h>   //strncpy
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>   //ifreq
#include <unistd.h>   //close

int main()
{
    int fd;
    struct ifreq ifr;
    char *iface = "enp0s3";
    unsigned char *mac = NULL;

    memset(&ifr, 0, sizeof(ifr));

    fd = socket(AF_INET, SOCK_DGRAM, 0);

    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name , iface , IFNAMSIZ-1);

    if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) {
        mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;

        //display mac address
        printf("Mac : %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n" , mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    }

    close(fd);

    return 0;
}
Akagi201
quelle
4

Angenommen, C ++ - Code (c ++ 11) ist ebenfalls in Ordnung und die Schnittstelle ist bekannt.

#include <cstdint>
#include <fstream>
#include <streambuf>
#include <regex>

using namespace std;

uint64_t getIFMAC(const string &ifname) {
  ifstream iface("/sys/class/net/" + ifname + "/address");
  string str((istreambuf_iterator<char>(iface)), istreambuf_iterator<char>());
  if (str.length() > 0) {
    string hex = regex_replace(str, std::regex(":"), "");
    return stoull(hex, 0, 16);
  } else {
    return 0;
  }
} 
int main()
{
  string iface = "eth0";
  printf("%s: mac=%016llX\n", iface.c_str(), getIFMAC(iface));
}
Lincoln
quelle
1
  1. Verwenden Sie unter Linux den Dienst "Network Manager" über den DBus.

  2. Es gibt auch ein Good'ol- Shell-Programm, das aufgerufen und das Ergebnis erfasst werden kann (verwenden Sie eine Exec- Funktion unter C):

$ /sbin/ifconfig | grep HWaddr

jldupont
quelle
0

Eine sehr tragbare Methode besteht darin, die Ausgabe dieses Befehls zu analysieren.

ifconfig | awk '$0 ~ /HWaddr/ { print $5 }'

Vorausgesetzt, ifconfig kann als aktueller Benutzer ausgeführt werden (normalerweise) und awk ist installiert (häufig). Dadurch erhalten Sie die Mac-Adresse des Geräts.

Matt
quelle
4
Es ist überhaupt nicht sehr tragbar. Unter Mac OS X gibt es nichts. Die Ausgabe von ifconfigenthält keinen Text HWaddr.
Dreamlax
Musste dies früher im Jahr auf Solaris, Linux und HPux tun.
Matt
1
Nun, das Konzept ist portabel. Der Text muss möglicherweise für andere Plattformen angepasst werden.
Matt
2
Mit Shell-Skripten ist selbst eine so kleine wie Ihre alles andere als portabel. Es gibt einen Witz, der
besagt
8
Außerdem wird dies in keinem mir bekannten C-Compiler kompiliert.
BobbyShaftoe
0

Erweiterung der Antwort von @ user175104 ...

std::vector<std::string> GetAllFiles(const std::string& folder, bool recursive = false)
{
  // uses opendir, readdir, and struct dirent.
  // left as an exercise to the reader, as it isn't the point of this OP and answer.
}

bool ReadFileContents(const std::string& folder, const std::string& fname, std::string& contents)
{
  // uses ifstream to read entire contents
  // left as an exercise to the reader, as it isn't the point of this OP and answer.
}

std::vector<std::string> GetAllMacAddresses()
{
  std::vector<std::string> macs;
  std::string address;

  // from: /programming/9034575/c-c-linux-mac-address-of-all-interfaces
  //  ... just read /sys/class/net/eth0/address

  // NOTE: there may be more than one: /sys/class/net/*/address
  //  (1) so walk /sys/class/net/* to find the names to read the address of.

  std::vector<std::string> nets = GetAllFiles("/sys/class/net/", false);
  for (auto it = nets.begin(); it != nets.end(); ++it)
  {
    // we don't care about the local loopback interface
    if (0 == strcmp((*it).substr(-3).c_str(), "/lo"))
      continue;
    address.clear();
    if (ReadFileContents(*it, "address", address))
    {
      if (!address.empty())
      {
        macs.push_back(address);
      }
    }
  }
  return macs;
}
Jesse Chisholm
quelle
0

Netlink Socket ist möglich

Mann netlink(7) netlink(3) rtnetlink(7) rtnetlink(3)

#include <assert.h>
#include <stdio.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <unistd.h>

#define SZ 8192

int main(){

  // Send
  typedef struct {
    struct nlmsghdr nh;
    struct ifinfomsg ifi;
  } Req_getlink;
  assert(NLMSG_LENGTH(sizeof(struct ifinfomsg))==sizeof(Req_getlink));
  int fd=-1;
  fd=socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
  assert(0==bind(fd,(struct sockaddr*)(&(struct sockaddr_nl){
    .nl_family=AF_NETLINK,
    .nl_pad=0,
    .nl_pid=getpid(),
    .nl_groups=0
  }),sizeof(struct sockaddr_nl)));
  assert(sizeof(Req_getlink)==send(fd,&(Req_getlink){
    .nh={
      .nlmsg_len=NLMSG_LENGTH(sizeof(struct ifinfomsg)),
      .nlmsg_type=RTM_GETLINK,
      .nlmsg_flags=NLM_F_REQUEST|NLM_F_ROOT,
      .nlmsg_seq=0,
      .nlmsg_pid=0
    },
    .ifi={
      .ifi_family=AF_UNSPEC,
      // .ifi_family=AF_INET,
      .ifi_type=0,
      .ifi_index=0,
      .ifi_flags=0,
      .ifi_change=0,
    }
  },sizeof(Req_getlink),0));

  // Receive
  char recvbuf[SZ]={};
  int len=0;
  for(char *p=recvbuf;;){
    const int seglen=recv(fd,p,sizeof(recvbuf)-len,0);
    assert(seglen>=1);
    len += seglen;
    if(((struct nlmsghdr*)p)->nlmsg_type==NLMSG_DONE||((struct nlmsghdr*)p)->nlmsg_type==NLMSG_ERROR)
      break;
    p += seglen;
  }

  struct nlmsghdr *nh=(struct nlmsghdr*)recvbuf;
  for(;NLMSG_OK(nh,len);nh=NLMSG_NEXT(nh,len)){
    if(nh->nlmsg_type==NLMSG_DONE)
      break;

    struct ifinfomsg *ifm=(struct ifinfomsg*)NLMSG_DATA(nh);

    printf("#%d ",ifm->ifi_index);
    #ifdef _NET_IF_H
    #pragma GCC error "include <linux/if.h> instead of <net/if.h>"
    #endif

    // Part 3 rtattr
    struct rtattr *rta=IFLA_RTA(ifm); // /usr/include/linux/if_link.h
    int rtl=RTM_PAYLOAD(nh);

    for(;RTA_OK(rta,rtl);rta=RTA_NEXT(rta,rtl))switch(rta->rta_type){
    case IFLA_IFNAME:printf("%s ",(const char*)RTA_DATA(rta));break;
    case IFLA_ADDRESS:
      printf("hwaddr ");
      for(int i=0;i<5;++i)
        printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
      printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
      break;
    case IFLA_BROADCAST:
      printf("bcast ");
      for(int i=0;i<5;++i)
        printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
      printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
      break;
    case IFLA_PERM_ADDRESS:
      printf("perm ");
      for(int i=0;i<5;++i)
        printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
      printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
      break;
    }

    printf("\n");

  }

  close(fd);
  fd=-1;
  return 0;

}

Beispiel

#1 lo hwaddr 00:00:00:00:00:00 bcast 00:00:00:00:00:00
#2 eth0 hwaddr 57:da:52:45:5b:1a bcast ff:ff:ff:ff:ff:ff perm 57:da:52:45:5b:1a
#3 wlan0 hwaddr 3c:7f:46:47:58:c2 bcast ff:ff:ff:ff:ff:ff perm 3c:7f:46:47:58:c2
Darren Ng
quelle
-1

Dies ist eine Bash-Zeile, die alle verfügbaren Mac-Adressen mit Ausnahme des Loopbacks druckt:

for x in `ls /sys/class/net |grep -v lo`; do cat /sys/class/net/$x/address; done

Kann von einem C-Programm ausgeführt werden.

Lacho Tomov
quelle