getnameinfo() 编译时错误 - 在 Linux 中提取 MAC 地址

getnameinfo() compile time error - Extracting MAC address in Linux

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

我的攻略:

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

getifaddrs 手册说

The ifa_addr field points to a structure containing the interface address. (The sa_family subfield should be consulted to determine the format of the address structure.) This field may contain a null pointer.

因此

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

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


如果这是嵌入的 Linux 并且接口总是命名相同,只需阅读 /sys/class/net/eth0/address - 更容易。

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

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

另请参阅 MAC address with getifaddrs 进行相关讨论。

@Antti Haapala,@Mats; 谢谢您的帮助。 正如您提到的那样,问题是接口中存在的其他 types/families 地址以及导致 getnameinfo() 系统调用出现问题的地方。

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

我添加了以下验证:

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