1. getifaddrs方法
正如《Linux下C获取所有可用网卡信息》所示。但MISRA规定,for循环中第二部分的判断只能使用 (>, >=, <, <=),且for循环的第三部分的数值改变只能用(++, --)。于是改变为while循环。
获取IP地址、子网掩码时,会用到这样强制转换(一些官方文档也会这样使用)
struct ifaddrs *ifa = NULL;
...
struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_addr;
printf(">>> ipAddress: %s\n", inet_ntoa(sin->sin_addr));
struct sockaddr_in *sin = (struct sockaddr_in *)ifa->ifa_netmask;
printf(">>> subnetMask: %s\n", inet_ntoa(sin->sin_addr));
其中,ifa->ifa_addr
和ifa->ifa_netmask
的数据类型是struct sockaddr
。
MISRA认为,struct sockaddr
和struct sockaddr_in
是不相关的数据类型,不可以进行数据类型转换,而且也要使用C++风格的类型转换(static_cast/reinterpret_cast),不可以使用C语言的类型转换。
网上有些资料讲,程序员可以操作struct sockaddr_in
,struct sockaddr
留给系统操作。但这里没办法,只能读取struct sockaddr
中的sa_data[14]移位生成inet_ntoa()
需要的参数。
int32_t INIPARSER_NSP::INIParser::get_INI_IP(string net_name, string &strIP, string &strNetID)
{
struct ifaddrs *ifList;
int32_t result = -1;
if (getifaddrs(&ifList) >= 0)
{
struct ifaddrs *ifa = ifList;
while (ifa != NULL)
{
if (ifa->ifa_addr->sa_family == AF_INET)
{
if (!static_cast<bool>(strncmp(net_name.c_str(), ifa->ifa_name, net_name.length())))
{
struct in_addr in_addr_IP;
uint32_t s_addr_IP = 0;
s_addr_IP |= static_cast<uint32_t>(static_cast<uint8_t>(ifa->ifa_addr->sa_data[2]));
s_addr_IP |= static_cast<uint32_t>(static_cast<uint8_t>(ifa->ifa_addr->sa_data[3])) << 8;
s_addr_IP |= static_cast<uint32_t>(static_cast<uint8_t>(ifa->ifa_addr->sa_data[4])) << 16;
s_addr_IP |= static_cast<uint32_t>(static_cast<uint8_t>(ifa->ifa_addr->sa_data[5])) << 24;
in_addr_IP.s_addr = s_addr_IP;
strIP = inet_ntoa(in_addr_IP);
struct in_addr in_addr_NetID;
uint32_t s_addr_netmask = 0;
s_addr_netmask |= static_cast<uint32_t>(static_cast<uint8_t>(ifa->ifa_netmask->sa_data[2]));
s_addr_netmask |= static_cast<uint32_t>(static_cast<uint8_t>(ifa->ifa_netmask->sa_data[3])) << 8;
s_addr_netmask |= static_cast<uint32_t>(static_cast<uint8_t>(ifa->ifa_netmask->sa_data[4])) << 16;
s_addr_netmask |= static_cast<uint32_t>(static_cast<uint8_t>(ifa->ifa_netmask->sa_data[5])) << 24;
uint32_t net_id = s_addr_IP & s_addr_netmask;
in_addr_NetID.s_addr = net_id;
strNetID = inet_ntoa(in_addr_NetID);
result = 0;
break;
}
}
ifa = ifa->ifa_next;
}
}
freeifaddrs(ifList);
return result;
}
注:
- 在类型转换中,采取了“char -> unsigned char(uint8_t) -> unsigned int(uint32_t)”的方式。如果直接采用“char -> unsigned int(uint32_t)”,Linux系统默认char为 signed char,当最高位为1(数值为128~255)时,会在高位不全0xff。如128(0x80)会在直接转换后变成(0xffffff80),接下来的按位或(|=)操作会有严重影响。
- 需要先转成uint32_t再做移位。MISRA认为可以移位宽度为对应数据类型的[0…n-1],n为数据类型的bit宽度。
2.ioctl的方法
接上一篇
我们的代码是要运行在LEDE/openWRT系统的多网口工控机上
实际运行时,发现getifaddrs(int getifaddrs (struct ifaddrs **__ifap))
的方法无法使用,具体原因为getifaddrs
无法在我们的设备拿到我们想要的信息,体现在协议族仅有PF_PACKET
,而没有AF_PACKET
,很费解。
发现ioctl可以直接拿到特定网卡的ip地址,而不需要拿到所有ip地址再轮询匹配
#include <arpa/inet.h>
#include <iostream>
#include <cstring>
#include <stdint.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
int32_t fun_ioctl_2(std::string dev, std::string &strIP)
{
int32_t result = 0;
int32_t sock_get_ip = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_get_ip < 0)
{
std::cout << "socket create failse..." << std::endl;
(void)close(sock_get_ip);
result = -1;
}
else
{
struct ifreq ifr_ip;
memset(&ifr_ip, 0, sizeof(ifr_ip));
(void)strncpy(ifr_ip.ifr_name, dev.c_str(), sizeof(ifr_ip.ifr_name) - 1);
if (ioctl(sock_get_ip, SIOCGIFADDR, &ifr_ip) < 0)
{
std::cout << "ioctl Failed" << std::endl;
(void)close(sock_get_ip);
result = -1;
}
else
{
struct sockaddr_in *ip_addr = reinterpret_cast<struct sockaddr_in *>(&ifr_ip.ifr_addr);
strIP = inet_ntoa(ip_addr->sin_addr);
std::cout << "local ip: " << strIP << std::endl;
(void)close(sock_get_ip);
}
}
return result;
}