目录
1.ioctl函数简介
1.1 ioctl函数
ioctl函数是内核开放给用户空间的一种通信机制,用户程序通过该机制可以和内核进行通信,实现一些定制或者扩展功能,用户可以注册自己的ioctl函数,完成用户特定的功能。
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
参数:
fd:文件描述符
request:ioctl命令
...:命令参数
返回值:
成功:返回0
失败:返回-1,并设置errno
EBADF:无效文件描述符
EFAULT:arg指针指向不可访问内存空间
EINVAL:命令或命令参数无效
ENOTTY:fd没有指向一个字符设备,或者fd指向的字符设备不支持ioctl操作
1.2 ioctl常用命令
ioctl常见命令可以分为6大类:套接字,文件,接口,路由,ARP,流。表中只列出常见的一些命令,用户可以注册自己的命令,自己注册命令在Linux驱动开发中很常见。
2.ioctl底层原理
ioctl通过系统调用执行sys_ioctl函数,sys_ioctl会先去判断ioctl命令是否是通用命令,如果是通用命令则按照通用命令处理方式执行,如果不是通用命令,则会执行注册在文件描述符的ioctl函数,用户注册的ioctl函数就是通过这个机制完成调用。
图 1
3.ioctl常用示例
3.1 ioctl设置文件IO阻塞和非阻塞
void set_block() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
int on = 1;
//on:0(阻塞),1(非阻塞)
int ret = ioctl(sockfd, FIONBIO, &on);
if (ret) {
perror("set block");
}
close(sockfd);
}
3.2 ioctl获取接口列表
void get_if_conf() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct ifconf conf;
#define MAX_IF_NUM (10)
struct ifreq reqs[MAX_IF_NUM] = {0};
conf.ifc_len = MAX_IF_NUM * sizeof(struct ifreq);
conf.ifc_ifcu.ifcu_req = reqs;
int ret = ioctl(sockfd, SIOCGIFCONF, &conf);
if (ret) {
perror("get if conf");
}
int num = conf.ifc_len / sizeof(struct ifreq); //ifc_len会被内核修改为实际长度
for (int i = 0; i < num; i++) {
printf("if name:%s\n", reqs[i].ifr_ifrn.ifrn_name);
}
close(sockfd);
}
3.3 ioctl获取接口信息
void print_if_addr(const char *msg, void *data) {
struct ifreq *req = (struct ifreq *)data;
struct sockaddr_in *addr = ((struct sockaddr_in *)&(req->ifr_addr));
printf("%s %s\n", msg, inet_ntoa(addr->sin_addr));
}
void print_if_flags(const char *msg, void *data) {
struct ifreq *req = (struct ifreq *)data;
printf("%s 0x%04x\n", msg, req->ifr_flags);
}
void print_if_dstaddr(const char *msg, void *data) {
struct ifreq *req = (struct ifreq *)data;
struct sockaddr_in *addr = ((struct sockaddr_in *)&(req->ifr_dstaddr));
printf("%s %s\n", msg, inet_ntoa(addr->sin_addr));
}
void print_if_broadaddr(const char *msg, void *data) {
struct ifreq *req = (struct ifreq *)data;
struct sockaddr_in *addr = ((struct sockaddr_in *)&(req->ifr_broadaddr));
printf("%s %s\n", msg, inet_ntoa(addr->sin_addr));
}
void print_if_netmask(const char *msg, void *data) {
struct ifreq *req = (struct ifreq *)data;
struct sockaddr_in *addr = ((struct sockaddr_in *)&(req->ifr_netmask));
printf("%s %s\n", msg, inet_ntoa(addr->sin_addr));
}
void print_if_metric(const char *msg, void *data) {
struct ifreq *req = (struct ifreq *)data;
printf("%s %d\n", msg, req->ifr_metric);
}
void print_if_mtu(const char *msg, void *data) {
struct ifreq *req = (struct ifreq *)data;
printf("%s %d\n", msg, req->ifr_mtu);
}
void print_if_hwaddr(const char *msg, void *data) {
struct ifreq *req = (struct ifreq *)data;
struct sockaddr *addr = ((struct sockaddr*)&(req->ifr_hwaddr));
printf("%s %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", msg,
addr->sa_data[0],
addr->sa_data[1],
addr->sa_data[2],
addr->sa_data[3],
addr->sa_data[4],
addr->sa_data[5]);
}
void get_if_info() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct cmd_item {
unsigned long cmd;
char cmd_name[20];
void (*print)(const char *msg, void *data);
};
#define CMD_NUM (8)
struct cmd_item cmd_list[CMD_NUM] =
{
{SIOCGIFADDR, "接口地址", print_if_addr},//获取接口地址
{SIOCGIFFLAGS, "接口标识", print_if_flags}, //获取接口标志
{SIOCGIFDSTADDR, "点对点地址", print_if_dstaddr}, //获取点对点地址
{SIOCGIFBRDADDR, "广播地址", print_if_broadaddr}, //获取广播地址
{SIOCGIFNETMASK, "子网掩码", print_if_netmask}, //获取子网掩码
{SIOCGIFMETRIC, "接口测度", print_if_metric}, //获取接口测度
{SIOCGIFMTU, "MTU", print_if_mtu}, //获取接口测度
{SIOCGIFHWADDR, "硬件地址", print_if_hwaddr}, //获取接口测度
};
for (uint32_t i = 0; i < CMD_NUM; i++) {
struct ifreq req = {0};
#define IF_NAME "ens33"
memcpy(req.ifr_ifrn.ifrn_name, IF_NAME, strlen(IF_NAME));
int ret = ioctl(sockfd, cmd_list[i].cmd, &req);
if (ret) {
printf("%s get %s info errno:%d(%s)\n", IF_NAME, cmd_list[i].cmd_name, errno, strerror(errno));
continue;
}
if (cmd_list[i].print != NULL)
cmd_list[i].print(cmd_list[i].cmd_name, &req);
}
close(sockfd);
}
3.4 ioctl获取和设置路由
void add_route() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
#define GATEWAY "192.168.0.1"
#define DST "192.168.10.0"
#define GENMASK "255.255.255.0"
#define DEVNAME "ens33"
struct rtentry rt = {0};
struct sockaddr_in *gw = (struct sockaddr_in *)&rt.rt_gateway;
gw->sin_family = AF_INET;
gw->sin_addr.s_addr = inet_addr(GATEWAY);
struct sockaddr_in *dst = (struct sockaddr_in *)&rt.rt_dst;
dst->sin_family = AF_INET;
dst->sin_addr.s_addr = inet_addr(DST);
struct sockaddr_in *genmask = (struct sockaddr_in *)&rt.rt_genmask;
genmask->sin_family = AF_INET;
genmask->sin_addr.s_addr = inet_addr(GENMASK);
rt.rt_dev = DEVNAME;
rt.rt_flags = RTF_UP | RTF_GATEWAY;
int ret = ioctl(sockfd, SIOCADDRT, &rt);
if (ret) {
perror("add route");
}
close(sockfd);
}
3.5 ioctl获取和设置ARP表项
void get_arp() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct arpreq req = {0};
struct sockaddr_in *sin = (struct sockaddr_in *)&req.arp_pa;
#define DEV "ens33"
#define IP "192.168.0.107"
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(IP);
memcpy(req.arp_dev, DEV, sizeof(DEV));
int ret = ioctl(sockfd, SIOCGARP, &req);
if (ret) {
perror("get arp");
} else {
printf("%s %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", IP,
req.arp_ha.sa_data[0],
req.arp_ha.sa_data[1],
req.arp_ha.sa_data[2],
req.arp_ha.sa_data[3],
req.arp_ha.sa_data[4],
req.arp_ha.sa_data[5]);
}
close(sockfd);
}