Linux系统获取设备网卡ip地址的几种方法

本文详细介绍了在Linux系统中获取指定网卡IPv4地址的三种方法。第一种方法利用getifaddrs和getnameinfo函数,但存在内环地址覆盖的问题。第二种方法通过socket对象和ioctl命令获取,避免了第一种方法的bug。第三种方法使用inet_ntop函数代替getnameinfo。每种方法都涉及到不同的系统调用和结构体,如struct ifaddrs、struct ifreq和struct ifconf。需要注意的是,未进行IO通信的网卡可能被视为无效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一种:获取指定网卡名称的ipv4地址
这种方法通过调用Linux提供的应用接口函数: getifaddrs、getnameinfo,遍历设备所有网卡获取相关配置信息;getnameinfo函数获取本地网卡ip地址时, 有一个bug获取到的ip地址可能被内环地址覆盖,而获取不到真正的ip地址。
例子
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <termios.h>
#include <ifaddrs.h>

char* hostname_to_ip(char * hostname );

int main(int agrc,char *agrv[])
{
	char *data;
	data = hostname_to_ip(agrv[1]);
	if(NULL == data)
		return 0;
	printf("%s\n",data);
	free(data);
	return 0;
}

char* hostname_to_ip(char * hostname )  //获取hostname对应的ipv4地址//
{
        struct ifaddrs *ifaddr, *ifa;
       int family, s;
       char *host = (char*)malloc(sizeof(char)*1024);
	   memset(host,0,sizeof(char)*1024);
       if (getifaddrs(&ifaddr) == -1)
       {
       //    perror("getifaddrs");
           //exit(EXIT_FAILURE);
       }


       for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
       {
           if (ifa->ifa_addr == NULL)
               continue;
           s=getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),host, 1024, NULL, 0, 10);

           if((strcmp(ifa->ifa_name,hostname)==0)&&(ifa->ifa_addr->sa_family==AF_INET))
           {
               if (s != 0)
               {
                 //  printf("getnameinfo() failed: %s\n", gai_strerror(s));
                   //exit(EXIT_FAILURE);
               }
               freeifaddrs(ifaddr);
                return host;
           }
       }
}

 

出现bug的原因当套接字被绑定到一个未指定的地址时,getsockname函数并不总是返回关于主机地址的信息,除非套接字已经连接到connect或accept(例如,使用ADDR_ANY)。 除非套接字已连接,否则Windows套接字应用程序不能假定将指定该地址。 将用于套接字的地址是未知的,除非套接字在多主机中使用时是连接的。 如果套接字使用无连接协议,则在套接字上发生I/O之前,地址可能不可用。
/
第二种:建立socket对象,利用socket对象扫描出设备可用网卡
涉及重要的结构体: struct ifreq、struct ifconf。这里将避开第一种方案带来的问题。
例子
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int get_local_ip(char *ip,char *dev)
{

    int fd, intrface, retn = 0;
    struct ifreq buf[INET_ADDRSTRLEN];  //这个结构定义在/usr/include/net/if.h,用来配置和获取ip地址,掩码,MTU等接口信息的
    struct ifconf ifc;

	/*1 建立socket链接,利用ioctl来自动扫描可用网卡*/
    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
    {

        ifc.ifc_len = sizeof(buf);

        // caddr_t,linux内核源码里定义的:typedef void *caddr_t;
        ifc.ifc_buf = (caddr_t)buf;

        if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc))  /*2  这里获取到可用网卡清单,包括网卡ip地址,mac地址*/
        {
            intrface = ifc.ifc_len/sizeof(struct ifreq);  //计算出有效网卡的数量//  
            while (intrface-- > 0)
             {
                if (!(ioctl(fd, SIOCGIFADDR, (char *)&buf[intrface])))  /*3  遍历并索引指定网卡的地址*/
                {
					if(strcmp(buf[intrface].ifr_ifrn.ifrn_name,dev) == 0)
					{
						ip=(inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));
						printf("IP:%s\n", ip);
					}
                }

            }
        }

        close(fd);

        return 0;
    }
}

int main(int agrc,char *agrv[])
{
    char ip[64];
    memset(ip, 0, sizeof(ip));
    get_local_ip(ip,agrv[1]);  //执行时:./a.out ethx//

    return 0;
}

 

对结构体的详解
 
  • struct ifreq
  • {
  • # define IFHWADDRLEN 6
  • # define IFNAMSIZ IF_NAMESIZE
  • union
  • {
  • char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */
  • } ifr_ifrn;
  • union
  • {
  • struct sockaddr ifru_addr;
  • struct sockaddr ifru_dstaddr;
  • struct sockaddr ifru_broadaddr;
  • struct sockaddr ifru_netmask;
  • struct sockaddr ifru_hwaddr;
  • short int ifru_flags;
  • int ifru_ivalue;
  • int ifru_mtu;
  • struct ifmap ifru_map;
  • char ifru_slave[IFNAMSIZ]; /* Just fits the size */
  • char ifru_newname[IFNAMSIZ];
  • __caddr_t ifru_data;
  • } ifr_ifru;
  • };
  • # define ifr_name ifr_ifrn.ifrn_name /* interface name */
  • # define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
  • # define ifr_addr ifr_ifru.ifru_addr /* address */
  • # define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
  • # define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
  • # define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
  • # define ifr_flags ifr_ifru.ifru_flags /* flags */
  • # define ifr_metric ifr_ifru.ifru_ivalue /* metric */
  • # define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
  • # define ifr_map ifr_ifru.ifru_map /* device map */
  • # define ifr_slave ifr_ifru.ifru_slave /* slave device */
  • # define ifr_data ifr_ifru.ifru_data /* for use by interface */
  • # define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
  • # define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
  • # define ifr_qlen ifr_ifru.ifru_ivalue /* queue length */
  • # define ifr_newname ifr_ifru.ifru_newname /* New name */
  • # define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)
  • # define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)
  • # define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)
 
struct ifconf
  {
    int ifc_len;            /* Size of buffer.  */
    union
      {
    __caddr_t ifcu_buf;
    struct ifreq *ifcu_req;
      } ifc_ifcu;
  };  //通常用来保存所有的接口信息
 
第三种,通过函数 inet_ntop,获取到网卡信息,类似于第一种方式,换了个函数。
例子
#include <stdio.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>

int get_local_ip(char *ip) {
    struct ifaddrs *ifAddrStruct;
    void *tmpAddrPtr=NULL;
    getifaddrs(&ifAddrStruct);
    while (ifAddrStruct != NULL) {
        if (ifAddrStruct->ifa_addr->sa_family==AF_INET) {
            tmpAddrPtr=&((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
            inet_ntop(AF_INET, tmpAddrPtr, ip, INET_ADDRSTRLEN);
            printf("%s IP Address:%s\n", ifAddrStruct->ifa_name, ip);
        }
        ifAddrStruct=ifAddrStruct->ifa_next;
    }

    //free ifaddrs
    freeifaddrs(ifAddrStruct);
    return 0;
}
int main()
{
    char ip[16];
    memset(ip, 0, sizeof(ip));
    get_local_ip(ip);
    return 0;
}

 

注意和第一种方式一样,如果设备网卡只是被分配到ip地址而为发生任何IO通信,将被认为无效网卡!!!
 
 
 
 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值