كيفية الحصول على عنوان MAC الخاص بجهازك باستخدام برنامج C؟

StackOverflow https://stackoverflow.com/questions/1779715

  •  21-09-2019
  •  | 
  •  

سؤال

أنا أعمل على أوبونتو. كيف يمكنني الحصول على عنوان MAC الخاص بجهازي أو واجهة تقول ETH0 باستخدام برنامج C.

هل كانت مفيدة؟

المحلول

تحتاج إلى التكرار على جميع الواجهات المتاحة على جهازك ، واستخدامها ioctl مع SIOCGIFHWADDR العلم للحصول على عنوان MAC. سيتم الحصول على عنوان MAC كصفيف ثنائي 6 أكتوت. تريد أيضًا تخطي واجهة الاسترجاع.

#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);
}

نصائح أخرى

أجمل بكثير من كل هذا المقبس أو جنون القشرة هو ببساطة استخدام sysfs لهذا:

الملف /sys/class/net/eth0/address يحمل Mac Abress الخاص بك كسلسلة بسيطة يمكنك قراءتها fopen()/fscanf()/fclose(). لا شيء أسهل من ذلك.

وإذا كنت ترغب في دعم واجهات الشبكة الأخرى من eth0 (وربما تريد) ، فما عليك سوى استخدام opendir()/readdir()/closedir() تشغيل /sys/class/net/.

تريد إلقاء نظرة على getifaddrs (3) الصفحة اليدوية. هناك مثال في C في Manpage نفسه يمكنك استخدامه. تريد الحصول على العنوان بالنوع AF_LINK.

#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;
}

استخدام getifaddrs يمكنك الحصول على عنوان Mac من العائلة AF_PACKET.

لعرض عنوان MAC لكل واجهة ، يمكنك المتابعة مثل هذا:

#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

لقد كتبت واحدة فقط وأختبرها على gentoo في 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;
}

على افتراض أن رمز C ++ (C ++ 11) على ما يرام أيضًا والواجهة معروفة.

#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=%016lX\n", iface.c_str(), getIFMAC(iface));
}
  1. على Linux ، استخدم خدمة "Network Manager" عبر DBU.

  2. يوجد ايضا جيد برنامج شل الذي يمكن استدعاؤه واكتسبت النتيجة (استخدم exec الوظيفة تحت ج):

$ /sbin/ifconfig | grep HWaddr

طريقة محمولة للغاية هي تحليل إخراج هذا الأمر.

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

شريطة أن يتم تشغيل ifconfig كمستخدم الحالي (عادة يمكن) تثبيت AWK (غالبًا ما يكون). هذا سوف يمنحك عنوان MAC للجهاز.

هذا خط باش يطبع جميع عناوين MAC المتاحة ، باستثناء الاسترجاع:

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

يمكن تنفيذها من برنامج C.

التوسع على الإجابة التي قدمها @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: https://stackoverflow.com/questions/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;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top