【Linux网络编程】ioctl函数在网络编程中的应用(C语言实现一个ifconfig)

阅读: 评论:0

【Linux网络编程】ioctl函数在网络编程中的应用(C语言实现一个ifconfig)

【Linux网络编程】ioctl函数在网络编程中的应用(C语言实现一个ifconfig)

文章目录

  • 1 前言
  • 2 网络编程下的ioctl
    • 2.1 函数原型
    • 2.2 请求操作命令码
    • 2.3 数据结构
      • 2.3.1 网卡设备映射属性
      • 2.3.3 网络接口信息
      • 2.3.4 网络接口配置信息
      • 2.3.5 网络接口ARP高速缓存
  • 3 ioctl应用
    • 3.1 获取指定网卡ip
    • 3.2 实现ifconfig功能


1 前言

  linux系统下查看、配置网络相关的信息,如物理地址、ip地址、网关等信息,我们通常是使用ifconfigroute命令操作;亦或者直接修改网络相关的配置文件达到修改的目的。在程序中修改网络相关信息,虽然可以通过 exec函数簇、system函数、popen函数调用ifconfigroute shell命令,通过解析命令返回值获取信息,但很显然这不是理想的方法。

  linux系统用户态与内核态(包括设备、驱动)进行非数据流的相关控制信息交互,一般是通过ioctl函数实现。相同的,用户态与网络设备相关的控制信息也是通过ioctl实现。实质上,ifconfigroute底层最终是通过调用ioctl函数实现其功能的。


2 网络编程下的ioctl

  网络模块下的ioctl控制信息包括网卡设备映射属性、网络接口信息、网络接口配置信息。

2.1 函数原型

#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
  • fd,socket文件描述符,通过open或者socket创建

  • request,请求操作命令码,与具体操作相关

  • 参数3,具体数据结构,依赖于请求码request指定的操作类型


2.2 请求操作命令码

  linux 4.4内核,网络模块编程相关请求码位于"/include/uapi/inux/sockios.h"中定义。更低版本内核可能位于"/include/inux/sockios.h"中定义,命令码命名有特定固有的命名格式。

  • 删除类型命令码:SIOCDxxx,xxx表示具体操作命名

  • 获取类型命令码:SIOCGxxx,xxx表示具体操作命名

  • 设置类型命令码:SIOCSxxx,xxx表示具体操作命名

  下面只列出常用的请求命令码,更多命令码宏参考sockios.h定义。

请求操作码数据类型含义
SIOCGIFCONF0x8912struct ifconf获取所有网络接口
SIOCGIFFLAGS0x8913struct ifreq获取网络接口标识
SIOCSIFFLAGS0x8914struct ifreq设置网络接口标识
SIOCGIFADDR0x8915struct ifreq获取本地ip地址
SIOCSIFADDR0x8916struct ifreq设置本地ip地址
SIOCGIFBRDADDR0x8919struct ifreq获取广播地址
SIOCSIFBRDADDR0x891astruct ifreq设置广播地址
SIOCGIFNETMASK0x891bstruct ifreq获取本地ip子网掩码
SIOCSIFNETMASK0x891cstruct ifreq设置本地ip子网掩码
SIOCGIFMETRIC0x891dstruct ifreq获取metric值
SIOCSIFMETRIC0x891estruct ifreq设置metric值
SIOCGIFMTU0x8921struct ifreq获取最大传输单元
SIOCSIFMTU0x8922struct ifreq设置最大传输单元
SIOCGIFTXQLEN0x8942struct ifreq获取发送队列大小
SIOCSIFTXQLEN0x8943struct ifreq设置发送队列大小
SIOCDARP0x8953struct arpreq删除ARP表项
SIOCGARP0x8954struct arpreq获取ARP表项
SIOCSARP0x8955struct arpreq设置ARP表项

2.3 数据结构

  linux 4.4内核,网卡设备映射属性、网络接口信息、网络接口配置信息数据结构位于"/include/uapi/inux/if.h"中定义。更低版本内核可能位于"/include/inux/if.h"中定义。

2.3.1 网卡设备映射属性

struct ifmap {unsigned long mem_start;	/* 映射起始地址 */unsigned long mem_end;		/* 映射结束地址 */unsigned short base_addr; 	/* 基地址 */unsigned char irq;			/* 中断号 */unsigned char dma;			/* 网卡DMA映射 */unsigned char port;			/* 端口号 *//* 3 bytes spare */
};

2.3.3 网络接口信息

struct ifreq {
#define IFHWADDRLEN	6		/* 物理地址长度 */union{char	ifrn_name[IFNAMSIZ];		/* 网卡名称 */} ifr_ifrn;union {struct	sockaddr ifru_addr;			/* 本地ip */struct	sockaddr ifru_dstaddr;		/* 目标ip */struct	sockaddr ifru_broadaddr;	/* 广播ip */struct	sockaddr ifru_netmask;		/* 子网掩码 */struct  sockaddr ifru_hwaddr;		/* 本地物理地址 */short	ifru_flags;					/* 接口标识 */int	ifru_ivalue;					/* 请求值,与具体请求相关 */int	ifru_mtu;						/* 最大传输单元 */struct  ifmap ifru_map;				/* 网卡映射属性 */char	ifru_slave[IFNAMSIZ];		/* 子设备 */char	ifru_newname[IFNAMSIZ];		/* 修改后网卡新名称 */void __user *	ifru_data;			/* 用户私有数据 */struct	if_settings ifru_settings;	/* 设备协议配置信息 */} ifr_ifru;
};
  • ifru_flags,接口标识,表示网卡的工作状态、运行模式、数据传输模式等,具体标识含义在if.h中定义,常见标识如下。

    标识含义
    IFF_BROADCAST1<<1广播传输
    IFF_DEBUG1<<2调试模式
    IFF_LOOPBACK1<<3回环传输
    IFF_UP1<<0接口已开启,但可能无法正常传输数据,如未接网线
    IFF_RUNNING1<<6接口已开启,数据可正常传输
    IFF_LOWER_UP1<<16接口物理连接已就绪

  如果要获取网络接口信息,则ioctl第三个参数传入struct ifreq数据结构地址。ifr_ifru成员参数是一个联合体,linux内核已定义了常用的信息宏,可以通过这些宏获取参数信息。

#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 ifr_settings	ifr_ifru.ifru_settings	/* Device/proto settings*/

实例:

/* 访问网络接口名称 */
struct ifreq ifr = {0};
ifr.ifr_name = "eth0";

2.3.4 网络接口配置信息

struct ifconf  {int	ifc_len;			/*  配置缓冲区大小 */union {char __user *ifcu_buf;	/* char类型缓冲区 */struct ifreq __user *ifcu_req;/* struct ifreq类型缓冲区 */} ifc_ifcu;
};

2.3.5 网络接口ARP高速缓存

  ARP高速缓存信息数据结构位于"/include/uapi/inux/if_arp.h"中定义。更低版本内核可能位于"/include/inux/if_arp.h"中定义。

struct arpreq {struct sockaddr	arp_pa;		/* protocol address	*/struct sockaddr	arp_ha;		/* hardware address	*/int			arp_flags;		/* flags */struct sockaddr       arp_netmask;    /* netmask (only for proxy arps) */char			arp_dev[16];
};

3 ioctl应用

  网络编程中的ioctl函数获取、设置网口属性应用步骤:

【1】通过socket函数创建文件描述符fd(套接字)

#include <sys/socket.h>
int socket(int af, int type, int protocol);
  • af,地址族(Address Family),ip地址类型,分为IPv4(AF_INET)和IPv6(AF_INET6);这里使用IPv4
  • type,套接字类型,常用有原始套接字( SOCK_RAW )、流格式套接字(SOCK_STRAAM)、数据报套接字(SOCK_DGRAM);这里可以填三者任意值
  • protocol,传输协议,常用有TCP协议(IPPROTO_TCP)和UDP协议(IPPROTO_UDP);这里可以填“0”,系统会根据套接字类型选择相应的传输协议

【2】初始化struct ifreqstruct ifconf数据结构

【3】调用ioctl,传入指定请求码,访问套接字fd

【4】解析struct ifreqstruct ifconf数据结构返回值


3.1 获取指定网卡ip

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <net/if.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{int fd = 0;struct ifreq ifr = {0};	struct ifconf ifc = {0};int ret = 0;int i;if (argc < 2) {printf("parameter invalid,usage: [%s if_name]n", argv[0]);return -1;}memset(&ifr, 0, sizeof(ifr));ifr.ifr_addr.sa_family = AF_INET;strncpy(ifr.ifr_name, argv[1], IFNAMSIZ);	/* 网卡名称 *//* 创建socket描述符 */fd = socket(AF_INET, SOCK_DGRAM, 0);if (fd <= 0){printf("create socket fd failed,%sn", strerror(errno));return -1;}ret = ioctl(fd, SIOCGIFADDR, &ifr);if (ret < 0){printf("ioctl failed,%sn", strerror(errno));goto __exit;}printf("%s ip addr: %sn", argv[1], inet_ntoa(((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr));__exit:if (fd != 0){close(fd);}return ret;
}

执行结果

acuity@ubuntu:/mnt/hgfs/LSW/STHB/TCP/ioctl$ gcc getip.c -o getip
acuity@ubuntu:/mnt/hgfs/LSW/STHB/TCP/ioctl$ ./getip ens33
ens33 ip addr: 192.168.0.24

3.2 实现ifconfig功能

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <net/if.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{int fd = 0;struct ifreq ifr_buf[10];	/* 最大查询10个网口信息 */struct ifconf ifc = {0};int if_num = 0;int ret = 0;int i= 0;char buf[128] = {0};/* 创建socket描述符 */fd = socket(AF_INET, SOCK_DGRAM, 0);if (fd <= 0){printf("create socket fd failed,%sn", strerror(errno));return -1;}ifc.ifc_len = sizeof(ifr_buf);ifc.ifc_buf = (caddr_t)ifr_buf;ret = ioctl(fd, SIOCGIFCONF, (char*)&ifc);if(ret){printf("ioctl failed,%sn", strerror(errno));close(fd);return -1;}if_num = ifc.ifc_len/sizeof(struct ifreq);	/* 实际网口个数 */while(if_num--){printf("%s", ifr_buf[if_num].ifr_name);/* 获取网卡状态标识 */ret = ioctl(fd, SIOCGIFFLAGS, &ifr_buf[if_num]);if(ret != 0){printf("ioctl SIOCGIFFLAGS failed,%sn", strerror(errno));continue;}if (ifr_buf[if_num].ifr_flags&IFF_LOOPBACK){sprintf(buf,"%s", "Link encap:Local Loopback");}else{sprintf(buf,"%s", "Link encap:Ethernet");}/* 获取物理地址 */ret = ioctl(fd, SIOCGIFHWADDR, &ifr_buf[if_num]);if(ret != 0){printf("ioctl SIOCGIFHWADDR failed,%sn", strerror(errno));continue;}sprintf(buf+strlen(buf),"  HWaddr %02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)ifr_buf[if_num].ifr_hwaddr.sa_data[0],(unsigned char)ifr_buf[if_num].ifr_hwaddr.sa_data[1],(unsigned char)ifr_buf[if_num].ifr_hwaddr.sa_data[2],(unsigned char)ifr_buf[if_num].ifr_hwaddr.sa_data[3],(unsigned char)ifr_buf[if_num].ifr_hwaddr.sa_data[4],(unsigned char)ifr_buf[if_num].ifr_hwaddr.sa_data[5]);for (i=0; i<(10-strlen(ifr_buf[if_num].ifr_name)); i++){printf(" ");}printf("%sn", buf);/* 获取本地ip */ret = ioctl(fd, SIOCGIFADDR, &ifr_buf[if_num]);if(ret){printf("ioctl SIOCGIFADDR failed,%sn", strerror(errno));continue;}sprintf(buf,"inet addr:%s", inet_ntoa(((struct sockaddr_in *)(&ifr_buf[if_num].ifr_addr))->sin_addr));/* 获取广播ip */ret = ioctl(fd, SIOCGIFBRDADDR, &ifr_buf[if_num]);if(ret){printf("ioctl SIOCGIFBRDADDR failed,%sn", strerror(errno));continue;}sprintf(buf+strlen(buf), "  Bcast:%s", inet_ntoa(((struct sockaddr_in *)(&ifr_buf[if_num].ifr_broadaddr))->sin_addr));/* 获取子网掩码 */ret = ioctl(fd, SIOCGIFNETMASK, &ifr_buf[if_num]);if(ret){printf("ioctl SIOCGIFNETMASK failed,%sn", strerror(errno));continue;}sprintf(buf+strlen(buf), "  Mask:%s", inet_ntoa(((struct sockaddr_in *)(&ifr_buf[if_num].ifr_netmask))->sin_addr));printf("          %sn", buf);/* 获取网卡状态标识 */ret = ioctl(fd, SIOCGIFFLAGS, &ifr_buf[if_num]);if(ret != 0){printf("ioctl SIOCGIFFLAGS failed,%sn", strerror(errno));continue;}if (ifr_buf[if_num].ifr_flags&IFF_UP){sprintf(buf, "%s","UP");}if (ifr_buf[if_num].ifr_flags&IFF_LOOPBACK){sprintf(buf+strlen(buf), " %s", "LOOPBACK");}else{sprintf(buf+strlen(buf), " %s", "BROADCAST");}if (ifr_buf[if_num].ifr_flags&IFF_RUNNING){sprintf(buf+strlen(buf), " %s", "RUNNING");}if (ifr_buf[if_num].ifr_flags&IFF_MULTICAST){sprintf(buf+strlen(buf), " %s", "MULTICAST");}/* 获取最大传输MTU */ret = ioctl(fd, SIOCGIFMTU, &ifr_buf[if_num]);if(ret != 0){printf("ioctl SIOCGIFMTU failed,%sn", strerror(errno));continue;}sprintf(buf+strlen(buf), " MTU:%d", ifr_buf[if_num].ifr_mtu);/* 获取Metric */ret = ioctl(fd, SIOCGIFMETRIC, &ifr_buf[if_num]);if(ret != 0){printf("ioctl SIOCGIFMETRIC failed,%sn", strerror(errno));continue;}sprintf(buf+strlen(buf), " Metric:%d", ifr_buf[if_num].ifr_metric);printf("          %sn", buf);/* 获取发送队列大小 */ret = ioctl(fd, SIOCGIFTXQLEN, &ifr_buf[if_num]);if(ret != 0){printf("ioctl SIOCGIFTXQLEN failed,%sn", strerror(errno));continue;}sprintf(buf, "txqueuelen:%d", ifr_buf[if_num].ifr_qlen);printf("          %sn", buf);printf("n");}close(fd);return 0;
}

执行结果

/* 程序执行 */
acuity@ubuntu:/mnt/hgfs/LSW/STHB/TCP/ioctl$ gcc ifconfig.c -o ifconfig
acuity@ubuntu:/mnt/hgfs/LSW/STHB/TCP/ioctl$ ./ifconfig
ens33     Link encap:Ethernet  HWaddr 00:0c:29:99:a4:35inet addr:192.168.0.24  Bcast:192.168.0.255  Mask:255.255.255.0UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1txqueuelen:1000lo        Link encap:Local Loopback  HWaddr 00:00:00:00:00:00inet addr:127.0.0.1  Bcast:0.0.0.0  Mask:255.0.0.0UP LOOPBACK RUNNING MTU:65536 Metric:1txqueuelen:1000/* ifconfig命令 */
acuity@ubuntu:/mnt/hgfs/LSW/STHB/TCP/ioctl$ ifconfig 
ens33     Link encap:Ethernet  HWaddr 00:0c:29:99:a4:35  inet addr:192.168.0.24  Bcast:192.168.0.255  Mask:255.255.255.0inet6 addr: fe80::1513:8861:4aa:c9c4/64 Scope:LinkUP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1RX packets:88317 errors:0 dropped:0 overruns:0 frame:0TX packets:22411 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:59721319 (59.7 MB)  TX bytes:2169702 (2.1 MB)lo        Link encap:Local Loopback  inet addr:127.0.0.1  Mask:255.0.0.0inet6 addr: ::1/128 Scope:HostUP LOOPBACK RUNNING  MTU:65536  Metric:1RX packets:138 errors:0 dropped:0 overruns:0 frame:0TX packets:138 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:12635 (12.6 KB)  TX bytes:12635 (12.6 KB)

本文发布于:2024-02-04 12:02:46,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170706750155387.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:网络编程   函数   语言   Linux   ifconfig
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23