满足 MISRA C++ 的网卡对应IP及网段号获取

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_addrifa->ifa_netmask的数据类型是struct sockaddr
MISRA认为,struct sockaddrstruct sockaddr_in是不相关的数据类型,不可以进行数据类型转换,而且也要使用C++风格的类型转换(static_cast/reinterpret_cast),不可以使用C语言的类型转换。
网上有些资料讲,程序员可以操作struct sockaddr_instruct 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;
}

注:

  1. 在类型转换中,采取了“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),接下来的按位或(|=)操作会有严重影响。
  2. 需要先转成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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值