C/C++ Linux 所有接口的 MAC 地址

发布于 2024-12-29 11:21:07 字数 1272 浏览 1 评论 0原文

我使用以下代码来检索当前计算机的所有 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,即使它已“关闭”?

我可以直接使用它来获取其硬件地址,

  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,...)怎么办?我想全部得到。感谢您的帮助。

I am using the following code to retrieve all MAC addresses for current computer:

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 { ... }
}

By running simple shell command ifconfig i can see lo, eth0 and wlan0. I would like to retrieve MAC addresses for eth0 and wlan0 by my C/C++ code. But only wlan0 is returned - eth0 is missing (I got ifr_names lo, lo, wlan0). Probably because eth0 is not active (no ethernet cable connected, with cable it is returned). Can I somehow alter that ioctl(SIOCGIFCONF) command to retrieve eth0 too even if it is "turned off"?

I can get its HW address by using directly

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

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

but what if the name would be not eth0 but something else (eth1, em0,...)? I would like to get all of them. Thanks for help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清晨说晚安 2025-01-05 11:21:07

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

  • 编写自己的 Netlink 接口代码和
  • 自己的 NL 代码,结合使用 libmnl (-> 请参阅 示例
  • 或利用像 libnl3 这样的自主库
  • 解析以下文本输出ip -o link (-o 是获取用于文本解析的输出,与 ifconfig 不同)
  • 或使用 sysfs 并查看 /sys/class/net/eth0/address

You should stop using net-tools and the archaic ioctl interface, and start on using the modern Netlink/sysfs interfaces. You have no less than 5 possibilities:

  • write your own Netlink-interfacing code
  • your own NL code, in combination utilizing libmnl (-> see rtnl-link-dump in Examples
  • or utilize autonomous libs like libnl3
  • parse text output of ip -o link (-o is to get output meant for text parsing, unlike ifconfig)
  • or use sysfs and just look at /sys/class/net/eth0/address
楠木可依 2025-01-05 11:21:07

您可以在这里找到解决方案: 获取给定特定接口的mac地址

您可以跳过特定接口部分。

You can find a solution here: Get mac address given a specific interface

You can just skip the specific interface part.

请帮我爱他 2025-01-05 11:21:07

也许不那么优雅,但你可以捕捉和解析 ifconfig 的结果,因为听起来它正是您正在寻找的内容。

Maybe not as elegant, but you could capture & parse the results from ifconfig, since it sounds like it has just what you are looking for.

清音悠歌 2025-01-05 11:21:07

受到 jørgensen 的回答的启发,下面的 C++17 代码使用 Netlink 来获取每个接口的 MAC 地址和名称。它是这个要点的修改版本,它帮助我克服了使用 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;
}

Inspired by jørgensen's answer, the C++17 code below uses Netlink to get the MAC address and name of each interface. It is a modified version of this gist, which helped me overcome my shyness about using Netlink. It really isn't as complex as it seems at first.

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文