Linux系统利用C语言获取网口信息(IP地址,MAC地址,状态,带宽speed等)

本文详细介绍了如何在Linux系统下利用ioctl接口获取网络接口的详细信息,包括网口状态、IP地址、MAC地址和带宽等。通过定义structifinfo结构体来存储网口信息,并提供了获取接口状态、IP地址和MAC地址的函数。代码示例展示了如何遍历所有网口并打印相关信息。测试结果显示,该程序在虚拟机中无法查询到网口的带宽信息,但在物理服务器上则可以获取正确的带宽数据。
摘要由CSDN通过智能技术生成

目录

设计思路

ioctl操作参数

获取网口的遍历结构

获取对应的参数信息

源代码

ifinfo.c

ifinfo.h

test.c

测试结果


 

设计思路

首先给需要查询的信息定义个结构(当然,这里可以扩展)

/**
 *  struct ifinfo - 网卡信息结构
 *  
 *  if_idx  网口在系统中的索引,          这里需要注意的是,派生的网口 em1:1 与 em1 的索引相同
 *  if_name 网口名,例如:eth0
 *  if_flag 网口状态,参见 IFSTAT_XXX 宏定义
 *  if_ipv4 网口IP地址,例如:10.170.6.66
 *  if_eth.if_ethmac    网口MAC地址,格式:28:6E:D4:88:C7:9A
 *  if_eth.if_ethspeed  网口带宽,单位:Mbps
 */
struct ifinfo {
    unsigned int if_idx;    //接口索引号
    char if_name[32];       //接口名 如:eth0

    int if_flag;
#define IFSTAT_UP           0x0001  /* 接口状态为 up */
#define IFSTAT_BROADCAST    0x0002  /* 广播地址可用 */
#define IFSTAT_LOOPBACK     0x0004  /* 该网口是回环口 */
#define IFSTAT_POINTOPOINT  0x0010  /* 点对点连接 */
#define IFSTAT_MULTICAST    0x0020  /* 支持多播 */

    char if_ipv4[16];       //接口IPv4地址
    struct {
        char if_ethmac[64];         //MAC地址
        unsigned int if_ethspeed;   //速率 Mbps
    }if_eth;
};

ioctl操作参数

才头文件 sys/sockios.h,linux/sockios.h 中定义了一些列的操作数

#include <sys/sockios.h>
#include <linux/sockios.h>


#define SIOCGIFNAME	0x8910		/* get iface name		*/
#define SIOCSIFLINK	0x8911		/* set iface channel		*/
#define SIOCGIFCONF	0x8912		/* get iface list		*/
#define SIOCGIFFLAGS	0x8913		/* get flags			*/
#define SIOCSIFFLAGS	0x8914		/* set flags			*/
#define SIOCGIFADDR	0x8915		/* get PA address		*/
#define SIOCSIFADDR	0x8916		/* set PA address		*/
#define SIOCGIFDSTADDR	0x8917		/* get remote PA address	*/
#define SIOCSIFDSTADDR	0x8918		/* set remote PA address	*/
#define SIOCGIFBRDADDR	0x8919		/* get broadcast PA address	*/
#define SIOCSIFBRDADDR	0x891a		/* set broadcast PA address	*/
#define SIOCGIFNETMASK	0x891b		/* get network PA mask		*/
#define SIOCSIFNETMASK	0x891c		/* set network PA mask		*/

 

获取网口的遍历结构

使用ioctl接口的SIOCGIFCONF选项,

/* 获取所有网口的句柄信息 */
static int get_ifconf_r(int sockfd, struct ifconf *ifc, void *buf, int buf_len)
{
    if(sockfd <= 2 || !ifc || !buf || buf_len <= 0) {
        return -1;
    }

    ifc->ifc_len = buf_len;
    ifc->ifc_buf = buf;
    if (ioctl(sockfd, SIOCGIFCONF, ifc) == -1) {
        return -1;
    }
    return 0;
}

获取对应的参数信息

使用ioctl接口的SIOCGIFADDR选项,

/* 查询网口 IPv4 地址 */
static int get_ifaddr(int sockfd, const char *if_name, char *addr, int addr_len)
{
    if(sockfd <= 2 || !if_name || !addr || addr_len <= 0) {
        return -1;
    }
    struct ifreq ifr;
    
    strcpy(ifr.ifr_name, if_name);
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) == 0) {
        if (ioctl(sockfd, SIOCGIFADDR, &ifr) == 0) {
            snprintf(addr,addr_len,"%s",
                        inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
        }
    }else{
        return -1;
    }
    return 0;
}

 

源代码

GitHub地址:https://github.com/Rtoax/test/tree/master/nic/ifinfo

ifinfo.c

/**
 *  获取网口信息,IP地址,MAC地址,状态,带宽等
 *  
 *  日期:2020年10月16日
 *  作者:RToax
 */
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <linux/sockios.h>
#include <linux/ethtool.h>

#include "ifinfo.h"

/* 创建Socket */
static int if_socket()
{
    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (sock == -1) {
        return -1;
    }
    return sock;
}

/* 获取所有网口的句柄信息 */
static int get_ifconf_r(int sockfd, struct ifconf *ifc, void *buf, int buf_len)
{
    if(sockfd <= 2 || !ifc || !buf || buf_len <= 0) {
        return -1;
    }

    ifc->ifc_len = buf_len;
    ifc->ifc_buf = buf;
    if (ioctl(sockfd, SIOCGIFCONF, ifc) == -1) {
        return -1;
    }
    return 0;
}

/* 获取网口状态 */
static int get_ifstat(int sockfd, const char *if_name, int *if_flag)
{
    if(sockfd <= 2 || !if_name || !if_flag) {
        return -1;
    }
    struct ifreq ifr;
    *if_flag = 0;
        
    strcpy(ifr.ifr_name, if_name);
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) == 0) {
        if(ifr.ifr_flags & IFF_UP) *if_flag |= IFSTAT_UP;
        if(ifr.ifr_flags & IFF_BROADCAST) *if_flag |= IFSTAT_BROADCAST;
        if(ifr.ifr_flags & IFF_LOOPBACK) *if_flag |= IFSTAT_LOOPBACK;
        if(ifr.ifr_flags & IFF_POINTOPOINT) *if_flag |= IFSTAT_POINTOPOINT;
        if(ifr.ifr_flags & IFF_MULTICAST) *if_flag |= IFSTAT_MULTICAST;
    }else{
        return -1;
    }
    return 0;
}

/* 查询网口 IPv4 地址 */
static int get_ifaddr(int sockfd, const char *if_name, char *addr, int addr_len)
{
    if(sockfd <= 2 || !if_name || !addr || addr_len <= 0) {
        return -1;
    }
    struct ifreq ifr;
    
    strcpy(ifr.ifr_name, if_name);
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) == 0) {
        if (ioctl(sockfd, SIOCGIFADDR, &ifr) == 0) {
            snprintf(addr,addr_len,"%s",
                        inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
        }
    }else{
        return -1;
    }
    return 0;
}

/* 查询网口 MAC 地址 */
static int get_ifhwaddr(int sockfd, const char *if_name, char *hwaddr, int hwaddr_len)
{
    if(sockfd <= 2 || !if_name || !hwaddr || hwaddr_len <= 0) {
        return -1;
    }
    struct ifreq ifr;
    
    strcpy(ifr.ifr_name, if_name);
    if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == 0) {
        unsigned char * ptr ;
        ptr = (unsigned char  *)&ifr.ifr_ifru.ifru_hwaddr.sa_data[0];
        snprintf(hwaddr,hwaddr_len,"%02X:%02X:%02X:%02X:%02X:%02X",
                        *ptr,*(ptr+1),*(ptr+2),*(ptr+3),*(ptr+4),*(ptr+5));
    }else{
        return -1;
    }
    return 0;
}

/* 查询网口带宽 */
static int get_ifethspeed(int sockfd, const char *if_name, unsigned int *speed)
{
    struct ifreq ifr;
    struct ethtool_cmd ep;
    
    strcpy(ifr.ifr_name, if_name);
    
    ep.cmd = ETHTOOL_GSET; 
    ifr.ifr_data = (caddr_t)&ep;  

    if (ioctl(sockfd, SIOCETHTOOL, &ifr) != 0) { // 如果出错退出; 
        return -1;
    }
    *speed = ep.speed;
    return 0;
}

/**
 *  get_ifinfo - 获取网口信息
 *
 *    该接口可以获取网口的速率,但是,在虚拟机环境中,无法查询网口带宽
 *  
 *  display_fn  查询的回调函数,参见: ifinfo_display 说明
 *  arg         回调函数被调用时的 arg 参数,该参数可以为 NULL
 *  
 *  return      失败,返回 -1, 成功,返回查询的网口总数
 */
int get_ifinfo(ifinfo_display display_fn, void *arg)
{
    /* 如果回调函数为空,则返回失败 */
    if(!display_fn) {
        return -1;
    }

    struct ifconf ifc;
    struct ifinfo info;
    
    char buf[2048];
    int ret = 0;    
    int count = 0;

    int sock = if_socket();/* 创建socket */
    
    ret = get_ifconf_r(sock, &ifc, buf, sizeof(buf));
    if(ret != 0) {
        close(sock);
        return -1;
    }

    /* 巧妙的网口结构保存 */
    struct ifreq* it = ifc.ifc_req;
    const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));

    /* 轮询所有网口 */
    for (; it != end; ++it, count++) {
        memset(&info, 0, sizeof(struct ifinfo));
        
        strcpy(info.if_name, it->ifr_name);
        
        info.if_idx = if_nametoindex(info.if_name);

        /* 获取一系列的网口信息,当然可以在后面继续开发,
            但不要忘记在 struct ifinfo 添加对应的单数字段 */
        get_ifstat(sock, info.if_name, &info.if_flag);
        get_ifaddr(sock, info.if_name, info.if_ipv4, sizeof(info.if_ipv4));
        get_ifhwaddr(sock, info.if_name, info.if_eth.if_ethmac, sizeof(info.if_eth.if_ethmac));
        get_ifethspeed(sock, info.if_name, &info.if_eth.if_ethspeed);

        /* 在这里,回调函数将被调用 */
        if(display_fn) display_fn(&info, arg);
    }

    close(sock);

    return count;
}

ifinfo.h

/**
 *  获取网口信息,IP地址,MAC地址,状态,带宽等
 *  
 *  日期:2020年10月16日
 *  作者:RToax
 */
#ifndef __IF_INFO_H
#define __IF_INFO_H 1


/**
 *  struct ifinfo - 网卡信息结构
 *  
 *  if_idx  网口在系统中的索引,          这里需要注意的是,派生的网口 em1:1 与 em1 的索引相同
 *  if_name 网口名,例如:eth0
 *  if_flag 网口状态,参见 IFSTAT_XXX 宏定义
 *  if_ipv4 网口IP地址,例如:10.170.6.66
 *  if_eth.if_ethmac    网口MAC地址,格式:28:6E:D4:88:C7:9A
 *  if_eth.if_ethspeed  网口带宽,单位:Mbps
 */
struct ifinfo {
    unsigned int if_idx;    //接口索引号
    char if_name[32];       //接口名 如:eth0

    int if_flag;
#define IFSTAT_UP           0x0001  /* 接口状态为 up */
#define IFSTAT_BROADCAST    0x0002  /* 广播地址可用 */
#define IFSTAT_LOOPBACK     0x0004  /* 该网口是回环口 */
#define IFSTAT_POINTOPOINT  0x0010  /* 点对点连接 */
#define IFSTAT_MULTICAST    0x0020  /* 支持多播 */

    char if_ipv4[16];       //接口IPv4地址
    struct {
        char if_ethmac[64];         //MAC地址
        unsigned int if_ethspeed;   //速率 Mbps
    }if_eth;
};

/**
 *  ifinfo_display - 网口状态查询回调函数
 *  
 *  info    网口信息结构指针,参见: struct ifinfo 说明
 *  arg     调用 get_ifinfo 内存地址传入的 arg 地址指针
 */
typedef void (*ifinfo_display)(const struct ifinfo *info, void *arg);


/**
 *  get_ifinfo - 获取网口信息
 *
 *    该接口可以获取网口的速率,但是,在虚拟机环境中,无法查询网口带宽
 *
 *  display_fn  查询的回调函数,参见: ifinfo_display 说明
 *  arg         回调函数被调用时的 arg 参数,该参数可以为 NULL
 *  
 *  return      失败,返回 -1, 成功,返回查询的网口总数
 */
int get_ifinfo(ifinfo_display display_fn, void *arg);


#endif /*<__IF_INFO_H>*/

test.c


#include <stdio.h>

#include "ifinfo.h"


void ifdisplay(const struct ifinfo *info, void *arg)
{
    printf("%2d: %-10s %16s %18s %d Mb \n",
            info->if_idx, info->if_name,info->if_ipv4, info->if_eth.if_ethmac,info->if_eth.if_ethspeed);
}


int main()
{
    int ret = 0;
    while(1) {
        ret = get_ifinfo(ifdisplay, NULL);
        printf("Total %d interface.\n", ret);
        sleep(1);
    }
}

 

测试结果

在虚拟机中无法查询网口带宽

[root@localhost ifinfo]# make 
gcc ifinfo.c test.c -o test.out
[root@localhost ifinfo]# ./test.out 
 1: lo                127.0.0.1  00:00:00:00:00:00 0 Mb 
 2: eth0            10.170.6.66  28:6E:D4:88:C7:9A 0 Mb 
 5: docker0          172.17.0.1  02:42:57:6F:10:40 0 Mb 
Total 3 interface.

用ifconfig查询的结果
-----------------------------------
[root@localhost ifinfo]# ifconfig -a
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:57:6f:10:40  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.170.6.66  netmask 255.255.255.0  broadcast 10.170.6.255
        inet6 fe80::2731:24c7:9924:2028  prefixlen 64  scopeid 0x20<link>
        inet6 fe80::4b76:dca0:14ca:2d95  prefixlen 64  scopeid 0x20<link>
        inet6 fe80::cb30:c5c4:34dd:895f  prefixlen 64  scopeid 0x20<link>
        ether 28:6e:d4:88:c7:9a  txqueuelen 1000  (Ethernet)
        RX packets 3347634794  bytes 2727102880807 (2.4 TiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 43937802  bytes 187858063132 (174.9 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 888190  bytes 13054543624 (12.1 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 888190  bytes 13054543624 (12.1 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

这里介绍一个工具《Linux查看网口带宽状态(ifconfig,netstat,ethtool)》,该工具的:github地址

[root@localhost ifstatus]# ./ifstatus.sh 
	Name 	Speed  	Status  
	docker0 	Unknown! 	down
	eth0 	Unknown! 	up
	lo 	Unknown! 	up

在真实的物理服务器上运行结果为

[root@localhost rtoax]# ./test.out 
 1: lo                127.0.0.1  00:00:00:00:00:00 0 Mb 
 2: em1            10.170.7.166  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:1          10.170.7.183  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:2          10.170.7.184  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:3          10.170.7.185  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:4          10.170.7.186  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:5          10.170.7.187  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:6          10.170.7.188  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:7          10.170.7.189  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:8          10.170.7.190  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:9          10.170.7.191  20:04:0F:FA:87:3C 1000 Mb 
 2: em1:10         10.170.7.192  20:04:0F:FA:87:3C 1000 Mb 
 3: em2            10.171.7.166  20:04:0F:FA:87:3D 65535 Mb 
 7: virbr0        192.168.122.1  52:54:00:9F:56:64 0 Mb 
Total 14 interface.

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值