【问题标题】:getnameinfo() compile time error - Extracting MAC address in Linuxgetnameinfo() 编译时错误 - 在 Linux 中提取 MAC 地址
【发布时间】:2018-01-18 14:57:14
【问题描述】:

我必须向服务器发送我的客户端套接字连接并与服务器通信的网络接口的 IP 地址和 MAC 地址。 所有机器都在 Intra-net 上。 我已经提取了我的套接字的 IP,我正在尝试提取 H/W 地址。

我的策略:

  1. 使用getsockname()系统调用提取套接字的IP。
  2. 使用getifaddrs() 系统调用列出所有可用的网络接口。在 for 循环中,我使用 getnameinfo() 系统调用来查找当前迭代接口名称的 IP,然后将此 IP 与套接字 IP(从步骤 1 中提取)进行比较,以查找已连接套接字的接口名称。
  3. 使用ioctl(fd, SIOCGIFHWADDR, &ifr)系统调用,使用在阶段2中找到的接口名称获取硬件地址。

我遇到问题getnameinfo() 系统调用。

如果我不将第一个参数类型转换为 (struct sockaddr_in*),我会收到以下错误:ai_family not supported

getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
                           NULL, 0, NI_NUMERICHOST);

如果我将第一个参数类型转换为 (struct sockaddr_in*),则会收到以下错误:error: cannot convert ‘sockaddr_in*’ to ‘const sockaddr*’ for argument ‘1’ to ‘int getnameinfo(const sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, int)’

getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
                           NULL, 0, NI_NUMERICHOST);

请多多指教。我什至对一些替代策略持开放态度,以编程方式和动态获取套接字 IP 和 MAC 地址。

bool Ethernet::getIp(void)
{
    struct      sockaddr_in addr;
    char        bufferIp[INET_ADDRSTRLEN];
    socklen_t   addrLen = sizeof(addr);
    if(getsockname(this->clientSocket, (struct sockaddr*) &addr, &addrLen) == -1)
    {
        string errStr = strerror(errno);
        FileOperations fo;
        string str;
        str = "Unable to extract IP address of socket";
        str += " Error : " + errStr;
        fo.printError(str);
        return RETURN_FAILURE;
    }

    if(inet_ntop(AF_INET, &addr.sin_addr, bufferIp, INET_ADDRSTRLEN) == NULL)
    {
        string errStr = strerror(errno);
        FileOperations fo;
        string str;
        str = "Unable to convert extracted IP address from binary to char* in Ethernet::getInterfaceDetails.";
        str += " Error : " + errStr;
        fo.printError(str);
        return RETURN_FAILURE;
    }
    this->ip = string(bufferIp);
    return RETURN_SUCCESS;
}

bool Ethernet::getMac(void)
{
    int fd;
    struct ifreq ifr;
//    char *iface = "eth0";
    unsigned char *mac;
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name , this->interfaceName.c_str(), IFNAMSIZ-1);
    ioctl(fd, SIOCGIFHWADDR, &ifr);
    close(fd);
    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]);
    return RETURN_SUCCESS;

}

bool Ethernet::getInterfaceDetails(void)
{
    struct ifaddrs *ifaddr, *ifa;
    int s;
    char host[NI_MAXHOST];
    string tempAddr;
    char   buffer[INET_ADDRSTRLEN];
    if (getifaddrs(&ifaddr) == -1) 
    {
        string errStr = strerror(errno);
        FileOperations fo;
        string str;
        str = "System call 'getifaddrs' failed in Ethernet::getInterfaceDetails.";
        str += " Error : " + errStr;
        fo.printError(str);
        return RETURN_FAILURE;
    }

    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
    {
        if (ifa->ifa_addr == NULL)
            continue;

        this->interfaceName = string(ifa->ifa_name);
        if(this->interfaceName == string("lo"))
            continue;

         s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
                           NULL, 0, NI_NUMERICHOST);

        if(s != 0)
        {
            string errStr = gai_strerror(s);
            cout << "Error : " << errStr << endl;
            FileOperations fo;
            string str;
            str = "Unable to convert extracted IP address address from binary to char* in Ethernet::getInterfaceDetails.";
            str += " Error : " + errStr;
            fo.printError(str);
            return RETURN_FAILURE;
        }
        tempAddr = string(host);      
        if(tempAddr == this->ip)
        {
            freeifaddrs(ifaddr);
            return RETURN_SUCCESS;
        }
    }
    return RETURN_FAILURE;
}

【问题讨论】:

  • C 代码在哪里?似乎你在 C++ cast 那里苦苦挣扎。
  • 我必须向服务器发送连接我的客户端套接字的网络接口的 IP 地址和 MAC 地址 为什么需要这样做?服务器可以知道您从哪个 IP 地址进行连接,并且它可能也可以访问您的 MAC 地址。这似乎是an XY problem
  • @AndrewHenle 它当然不知道。
  • 该程序部署在嵌入式硬件(基于 Linux 的 SBC)上。我被要求提供的 PC 软件开发人员要求提供硬件 IP 和 MAC。
  • @AnttiHaapala 它当然不知道 什么?!?! getpeername() 提供网络连接的远程 IP 地址:“getpeername() 函数应检索指定套接字的对等地址 ...” 如果您不这样做,连接 将无法工作知道你在和谁说话。回复数据包如何返回给发送者?

标签: c++ c linux sockets system-calls


【解决方案1】:

@Antti Haapala,@Mats; 谢谢您的帮助。 问题在于您提到接口中存在其他类型/系列的地址以及导致 getnameinfo() 系统调用出现问题的地方。

现在我正在过滤掉其他地址,只允许 AF_INET。

我添加了以下验证:

if(ifa->ifa_addr->sa_family != AF_INET)
      continue;

【讨论】:

    【解决方案2】:

    关于您对getnameinfo 的调用,我看到一件奇怪的事情:您假设非空ifa_addr 等于sockaddr_in 类型,但我可以想象您可以获得其他类型,例如sockaddr_in6。所以你应该检查ifa_addr-&gt;sa_family 字段以确保它是AF_INET。也许您也应该处理 IPv6?

    我的理论是调用getnameinfo 的结构大小与地址族的预期大小不匹配可能是您出错的原因。

    另请查看MAC address with getifaddrs 进行相关讨论。

    【讨论】:

    • 在进行系统调用之前,我会识别家庭。
    【解决方案3】:

    getifaddrs 手册说

    ifa_addr 字段指向包含接口地址的结构。 (应参考sa_family 子字段来确定地址结构的格式。)该字段可能包含空指针。

    这样

    • 检查该字段是否不是空指针(如果是,那么它不是您要查找的 IP)
    • 长度参数必须与sa_family 中地址的长度相匹配/或仅过滤AF_INET

    但是创建一个到服务器的数据报套接字然后询问它的地址是什么,或者实际上进行 http 连接并询问 那个套接字 正在使用什么 MAC 地址不是更容易吗? p>


    如果这是嵌入式 Linux 并且接口始终命名相同,只需从 /sys/class/net/eth0/address 读取 - 更容易。

    【讨论】:

    • MAC 地址用于识别单个节点。同时 tcp/ip 套接字用于发送数据(在串口和 usb 上接收)。仅使用 POST 请求发送大图像。
    • 我正在检查代码中的空指针,并为 AF_INET 设置了长度。
    • @DarkSorrow 但您可能会传入一个长度为AF_INETAF_INET6 地址,即您必须检查sa_family,如果不是AF_INET,则继续。
    • 我也想过同样的事情,但问题是嵌入式板已经内置了 Wi-Fi 支持。无论网络接口如何,管理层都需要一个通用程序。他们希望我开发无需客户端配置要求即可动态查找 ip 和 mac 的程序。客户端可以使用 GUI(在嵌入式硬件上)在以太网和 wi-fi 之间进行选择。
    • 系统只能在 IPv4 上运行。它是系统的必备条件。没有 IPv6。
    猜你喜欢
    • 1970-01-01
    • 2020-12-18
    • 2020-11-05
    • 1970-01-01
    • 2011-05-15
    • 1970-01-01
    • 2017-06-26
    • 1970-01-01
    • 2012-09-23
    相关资源
    最近更新 更多