【问题标题】:C/C++ Linux MAC Address of all interfaces所有接口的 C/C++ Linux MAC 地址
【发布时间】:2012-02-20 12:25:40
【问题描述】:

我正在使用以下代码检索当前计算机的所有 MAC 地址:

ifreq ifr;
ifconf ifc;
char buf[1024];

int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) { ... };

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

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

for (; it != end; ++it) {
    strcpy(ifr.ifr_name, it->ifr_name);
    if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
        if (!(ifr.ifr_flags & IFF_LOOPBACK)) {
            if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
               unsigned char mac_address[6];
               memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
               ...
            }
        }
    }
    else { ... }
}

通过运行简单的 shell 命令 ifconfig 我可以看到 lo、eth0 和 wlan0。我想通过我的 C/C++ 代码检索 eth0 和 wlan0 的 MAC 地址。但只返回 wlan0 - 缺少 eth0(我得到了 ifr_names lo、lo、wlan0)。可能是因为 eth0 未激活(未连接以太网电缆,使用电缆返回)。我可以以某种方式更改该 ioctl(SIOCGIFCONF) 命令以检索 eth0,即使它已“关闭”?

我可以直接使用得到它的HW地址

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

  strcpy(s.ifr_name, "eth0");
  if (0 == ioctl(fd, SIOCGIFHWADDR, &s)) { ... }

但是如果名称不是 eth0 而是其他名称(eth1、em0、...)怎么办?我想得到所有这些。感谢您的帮助。

【问题讨论】:

    标签: c linux mac-address ioctl ifconfig


    【解决方案1】:

    您应该停止使用 net-tools 和过时的 ioctl 接口,并开始使用现代的 Netlink/sysfs 接口。您有不少于 5 种可能性:

    • 编写您自己的 Netlink 接口代码
    • 您自己的 NL 代码,结合使用 libmnl (-> 参见Examples 中的 rtnl-link-dump
    • 或使用像 libnl3 这样的自治库
    • 解析ip -o link 的文本输出(-o 是获取用于文本解析的输出,与 ifconfig 不同)
    • 或者使用 sysfs 看看/sys/class/net/eth0/address

    【讨论】:

      【解决方案2】:

      您可以在这里找到解决方案:Get mac address given a specific interface

      你可以跳过特定的界面部分。

      【讨论】:

      • 谢谢,但我不确定我明白你的意思。在您的代码示例中,所有内容都是关于询问名称为 argv[1] 的接口参数 - 例如,可以是我的第二个代码 sn-p 中的 eth0。没关系。但我需要列出所有接口。通过按照您的建议跳过“特定”部分,会留下什么?我需要获取一些我会询问详细信息的接口列表(MAC),是否正确?据我了解,这就是我通过调用 SIOCGIFCONF 所做的。但 eth0 不在列表中。
      • 如何结合这两个答案:解析 ifconfig 的结果只是名称(我认为这与平台无关)并使用这种方法查找每个名称的 MAC 地址你找到了。
      • 或者获取 ifconfig 的源代码,看看它是如何找到这些名称的。
      【解决方案3】:

      也许没有那么优雅,但您可以从 ifconfig 捕获并解析结果,因为它听起来就像您正在寻找的一样。

      【讨论】:

      • 谢谢,这总是一个解决方案,但我宁愿使用一些C解决方案,解析ifconfig结果可能会出现问题,格式可能因linux版本不同而不同等等。
      【解决方案4】:

      jørgensen's answer的启发,下面的C++17代码使用Netlink获取每个接口的MAC地址和名称。它是this gist 的修改版本,它帮助我克服了使用 Netlink 的害羞。它实际上并不像一开始看起来那么复杂。

      #include <linux/rtnetlink.h>
      #include <net/if_arp.h>
      #include <unistd.h>
      
      #include <cstddef>
      #include <cstring>
      #include <iostream>
      #include <string>
      #include <tuple>
      #include <vector>
      
      using std::byte;
      using std::string;
      using std::vector;
      
      struct RtReq {
          nlmsghdr header;
          ifinfomsg msg;
      };
      
      class fd_wrapper
      {
        public:
          fd_wrapper( int const fd_arg ) : fd_( fd_arg ) {}
          ~fd_wrapper() { close( fd_ ); }
          int fd() const noexcept { return fd_; }
      
        private:
          int fd_;
      };
      
      void print_all_macs()
      {
          fd_wrapper fd = ( []() -> fd_wrapper {
              RtReq req = ( []() {
                  RtReq req;
                  memset( &req, 0, sizeof( req ) );
                  req.header.nlmsg_len = NLMSG_LENGTH( sizeof( struct ifinfomsg ) );
                  req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
                  req.header.nlmsg_type = RTM_GETLINK;
                  req.header.nlmsg_seq = 1; // We're only sending one message
                  req.msg.ifi_family = AF_UNSPEC;
                  req.msg.ifi_change = 0xffffffff;
                  return std::move( req );
              } )();
      
              sockaddr_nl sa;
              memset( &sa, 0, sizeof( sa ) );
              sa.nl_family = AF_NETLINK;
              iovec iov = {&req, req.header.nlmsg_len};
              msghdr msg = {&sa, sizeof( sa ), &iov, 1, nullptr, 0, 0};
      
              int fd = socket( AF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
      
              sendmsg( fd, &msg, 0 );
      
              return fd_wrapper( fd );
          } )();
      
          enum class Result { not_done, done };
      
          auto read_message = []( fd_wrapper const& fd ) -> Result {
              char buf[16 * 1024];
              iovec iov = {buf, sizeof( buf )};
              sockaddr_nl sa;
              msghdr msg = {&sa, sizeof( sa ), &iov, 1, nullptr, 0, 0};
              int len = recvmsg( fd.fd(), &msg, 0 );
              for( nlmsghdr* nh = (nlmsghdr*)buf; NLMSG_OK( nh, len ); nh = NLMSG_NEXT( nh, len ) ) {
                  if( nh->nlmsg_type == NLMSG_DONE ) {
                      return Result::done;
                  }
      
                  if( nh->nlmsg_type != RTM_BASE )
                      continue;
      
                  ifinfomsg* msg = (ifinfomsg*)NLMSG_DATA( nh );
                  if( msg->ifi_type != ARPHRD_ETHER )
                      continue;
      
                  rtattr* rta = IFLA_RTA( msg );
                  int alen = nh->nlmsg_len - NLMSG_LENGTH( sizeof( *msg ) );
      
                  string name;
                  vector<byte> addr;
      
                  for( ; RTA_OK( rta, alen ); rta = RTA_NEXT( rta, alen ) ) {
                      if( rta->rta_type == IFLA_ADDRESS ) {
                          auto const p = reinterpret_cast<byte const*>( RTA_DATA( rta ) );
                          addr.insert( addr.begin(), p, p + 6 );
                      }
      
                      if( rta->rta_type == IFLA_IFNAME ) {
                          name = string{(char*)RTA_DATA( rta )};
                      }
                  }
      
                  auto const c = reinterpret_cast<uint8_t const*>( addr.data() );
                  printf( "%02x:%02x:%02x:%02x:%02x:%02x %s\n", c[0], c[1], c[2], c[3], c[4], c[5],
                          name.c_str() );
              }
          };
      
          Result result;
          while( ( result = read_message( fd ) ) == Result::not_done )
              ;
      }
      
      int main( int /*argc*/, char** /*argv*/ )
      {
          print_all_macs();
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-28
        • 2020-10-15
        • 1970-01-01
        • 2020-05-20
        • 2017-10-28
        • 2012-01-07
        • 2016-05-21
        • 1970-01-01
        相关资源
        最近更新 更多