linux系统下查看、配置网络相关的信息,如物理地址、ip地址、网关等信息,我们通常是使用ifconfig
、route
命令操作;亦或者直接修改网络相关的配置文件达到修改的目的。在程序中修改网络相关信息,虽然可以通过 exec函数簇、system函数、popen函数调用ifconfig
、route
shell命令,通过解析命令返回值获取信息,但很显然这不是理想的方法。
linux系统用户态与内核态(包括设备、驱动)进行非数据流的相关控制信息交互,一般是通过ioctl
函数实现。相同的,用户态与网络设备相关的控制信息也是通过ioctl
实现。实质上,ifconfig
、route
底层最终是通过调用ioctl
函数实现其功能的。
网络模块下的ioctl
控制信息包括网卡设备映射属性、网络接口信息、网络接口配置信息。
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
fd
,socket文件描述符,通过open
或者socket
创建
request
,请求操作命令码,与具体操作相关
参数3,具体数据结构,依赖于请求码request
指定的操作类型
linux 4.4内核,网络模块编程相关请求码位于"/include/uapi/inux/sockios.h"
中定义。更低版本内核可能位于"/include/inux/sockios.h"
中定义,命令码命名有特定固有的命名格式。
删除类型命令码:SIOCDxxx
,xxx表示具体操作命名
获取类型命令码:SIOCGxxx
,xxx表示具体操作命名
设置类型命令码:SIOCSxxx
,xxx表示具体操作命名
下面只列出常用的请求命令码,更多命令码宏参考sockios.h
定义。
请求操作码 | 值 | 数据类型 | 含义 |
---|---|---|---|
SIOCGIFCONF | 0x8912 | struct ifconf | 获取所有网络接口 |
SIOCGIFFLAGS | 0x8913 | struct ifreq | 获取网络接口标识 |
SIOCSIFFLAGS | 0x8914 | struct ifreq | 设置网络接口标识 |
SIOCGIFADDR | 0x8915 | struct ifreq | 获取本地ip地址 |
SIOCSIFADDR | 0x8916 | struct ifreq | 设置本地ip地址 |
SIOCGIFBRDADDR | 0x8919 | struct ifreq | 获取广播地址 |
SIOCSIFBRDADDR | 0x891a | struct ifreq | 设置广播地址 |
SIOCGIFNETMASK | 0x891b | struct ifreq | 获取本地ip子网掩码 |
SIOCSIFNETMASK | 0x891c | struct ifreq | 设置本地ip子网掩码 |
SIOCGIFMETRIC | 0x891d | struct ifreq | 获取metric值 |
SIOCSIFMETRIC | 0x891e | struct ifreq | 设置metric值 |
SIOCGIFMTU | 0x8921 | struct ifreq | 获取最大传输单元 |
SIOCSIFMTU | 0x8922 | struct ifreq | 设置最大传输单元 |
SIOCGIFTXQLEN | 0x8942 | struct ifreq | 获取发送队列大小 |
SIOCSIFTXQLEN | 0x8943 | struct ifreq | 设置发送队列大小 |
SIOCDARP | 0x8953 | struct arpreq | 删除ARP表项 |
SIOCGARP | 0x8954 | struct arpreq | 获取ARP表项 |
SIOCSARP | 0x8955 | struct arpreq | 设置ARP表项 |
linux 4.4内核,网卡设备映射属性、网络接口信息、网络接口配置信息数据结构位于"/include/uapi/inux/if.h"
中定义。更低版本内核可能位于"/include/inux/if.h"
中定义。
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 */
};
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_BROADCAST | 1<<1 | 广播传输 |
IFF_DEBUG | 1<<2 | 调试模式 |
IFF_LOOPBACK | 1<<3 | 回环传输 |
IFF_UP | 1<<0 | 接口已开启,但可能无法正常传输数据,如未接网线 |
IFF_RUNNING | 1<<6 | 接口已开启,数据可正常传输 |
IFF_LOWER_UP | 1<<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";
struct ifconf {int ifc_len; /* 配置缓冲区大小 */union {char __user *ifcu_buf; /* char类型缓冲区 */struct ifreq __user *ifcu_req;/* struct ifreq类型缓冲区 */} ifc_ifcu;
};
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];
};
网络编程中的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);这里使用IPv4type
,套接字类型,常用有原始套接字( SOCK_RAW
)、流格式套接字(SOCK_STRAAM
)、数据报套接字(SOCK_DGRAM
);这里可以填三者任意值protocol
,传输协议,常用有TCP协议(IPPROTO_TCP
)和UDP协议(IPPROTO_UDP
);这里可以填“0”,系统会根据套接字类型选择相应的传输协议【2】初始化struct ifreq
和struct ifconf
数据结构
【3】调用ioctl
,传入指定请求码,访问套接字fd
【4】解析struct ifreq
和struct ifconf
数据结构返回值
#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
#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小时内删除。
留言与评论(共有 0 条评论) |