分类: LINUX
2010-07-29 14:01:06
ioctl函 数
本函数影响由fd参数引用的一个打开的文件。
#include
int ioctl( int fd, int request, .../* void *arg */ );
返回0:成功 -1: 出错
第三个参数总是一个指针,但指针的类型依赖于request参数。
我们可以把和网络相关的请求划分为6类:
套接口操作
文件操作
接口操作
ARP高速缓存操作
路由表操作
流系统
下表列出了网络相关ioctl请求的request参 数以及arg地址必须指向的数据类型:
类别 |
Request |
说明 |
数据类型 |
套 接 口 |
SIOCATMARK SIOCSPGRP SIOCGPGRP |
是否位于带外标记 设置套接口的进程ID或进程组ID 获取套接口的进程ID或进程组ID |
int int int |
文
件
|
FIONBIN FIOASYNC FIONREAD FIOSETOWN FIOGETOWN
|
设置/清除非阻塞I/O标 志 设置/清除信号驱动异步I/O标 志 获取接收缓存区中的字节数 设置文件的进程ID或进程组ID 获取文件的进程ID或进程组ID |
int int int int int |
接 口
|
SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC SIOCGIFMTU SIOCxxx |
获取所有接口的清单 设置接口地址 获取接口地址 设置接口标志 获取接口标志 设置点到点地址 获取点到点地址 获取广播地址 设置广播地址 获取子网掩码 设置子网掩码 获取接口的测度 设置接口的测度 获取接口MTU (还有很多取决于系统的实现) |
struct ifconf struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq |
ARP |
SIOCSARP SIOCGARP SIOCDARP |
创建/修改ARP表 项 获取ARP表项 删除ARP表项 |
struct arpreq struct arpreq struct arpreq |
路 由 |
SIOCADDRT SIOCDELRT |
增加路径 删除路径 |
struct rtentry struct rtentry |
流 |
I_xxx |
|
|
套接口操作:
明确用于套接口操作的ioctl请求有三个,它 们都要求ioctl的第三个参数是指向某个整数的一个指针。
SIOCATMARK: 如果本套接口的的度指针当前位于带外标记,那就通过由第三个参数指向的整数返回一个非0值; 否则返回一个0值。POSIX以函数sockatmark替 换本请求。
SIOCGPGRP: 通过第三个参数指向的整数返回本套接口的进程ID或进 程组ID,该ID指定针对本套接口的SIGIO或SIGURG信 号的接收进程。本请求和fcntl的F_GETOWN命 令等效,POSIX标准化的是fcntl函 数。
SIOCSPGRP: 把本套接口的进程ID或者进程组ID设 置成第三个参数指向的整数,该ID指定针对本套接口的SIGIO或SIGURG信 号的接收进程,本请求和fcntl的F_SETOWN命 令等效,POSIX标准化的是fcntl操 作。
文件操 作:
以下5个 请求都要求ioctl的第三个参数指向一个整数。
FIONBIO: 根据ioctl的第三个参数指向一个0或 非0值分别清除或设置本套接口的非阻塞标志。本请求和O_NONBLOCK文 件状态标志等效,而该标志通过fcntl的F_SETFL命 令清除或设置。
FIOASYNC: 根据iocl的第三个参数指向一个0值 或非0值分别清除或设置针对本套接口的信号驱动异步I/O标 志,它决定是否收取针对本套接口的异步I/O信号(SIGIO)。 本请求和O_ASYNC文件状态标志等效,而该标志可以通过fcntl的F_SETFL命 令清除或设置。
FIONREAD: 通过由ioctl的第三个参数指向的整数返回当前在本 套接口接收缓冲区中的字节数。本特性同样适用于文件,管道和终端。
FIOSETOWN: 对于套接口和SIOCSPGRP等效。
FIOGETOWN: 对于套接口和SIOCGPGRP等效。
接口配置:
得到系统中所有接口由SIOCGIFCONF请 求完成,该请求使用ifconf结构,ifconf又 使用ifreq
结构,如下所示:
Struct ifconf{
int ifc_len; // 缓冲区的大小
union{
caddr_t ifcu_buf; // input from user->kernel
struct ifreq *ifcu_req; // return of structures returned
}ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf //buffer address
#define ifc_req ifc_ifcu.ifcu_req //array of structures returned
#define IFNAMSIZ 16
struct ifreq{
char ifr_name[IFNAMSIZ]; // interface name, e.g., “le0”
union{
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
short ifru_flags;
int ifru_metric;
caddr_t ifru_data;
}ifr_ifru;
};
#define ifr_addr ifr_ifru.ifru_addr // address
#define ifr_dstaddr ifr_ifru.ifru_dstaddr // otner end of p-to-p link
#define ifr_broadaddr ifr_ifru.ifru_broadaddr // broadcast address
#define ifr_flags ifr_ifru.ifru_flags // flags
#define ifr_metric ifr_ifru.ifru_metric // metric
#define ifr_data ifr_ifru.ifru_data // for use by interface
再调用ioctl前 我们必须先分撇一个缓冲区和一个ifconf结构,然后才初始化后者。如下图
展示了一个ifconf结 构的初始化结构,其中缓冲区的大小为1024,ioctl的 第三个参数指向
这样一个ifconf结 构。
ifc_len |
Ifc_buf |
1024
--------------------->缓存
假设内核返回2个ifreq结 构,ioctl返回时通过同一个ifconf结 构缓冲区填入了那2个ifreq结构,ifconf结 构的ifc_len成员也被更新,以反映存放在缓冲区中的信息量。
接 口操作:
SIOCGIFCONF请求为每个已配置的接口返回其名字以及一个套接口地址结构。我们接着可以发出多个接 口类其他请求以设置或获取每个接口的其他特征。这些请求的获取(get)版本(SIOCGxxx) 通常由netstat程序发出,设置(set) 版本(SIGOCSxxx)通常由ifconfig程 序发出。任何用户都可以获取接口信息,设置接口信息却要求有超级用户权限。
这些请求汲取或返回一个一个ifreq结构中的信息,而这个结构的地址则作为ioctl调 用的第三个参数制定。接口总是以其名标志,在ifreq结构的ifr_name成 员中指定,如le0,lo0,ppp0等。
这些请求中有许多使用套接口地址结构在应用进程和内核之间指定或返回具体接口的IP地 址或地址掩码。对于IPV4,这个地址或掩码放在一个网际套接口地址结构的sin_addr成 员中;对于IPV6,它是一个IPV6套接 口地址结构的sin6_addr成员。
SIOCGIFADDR: 在ifr_addr成 员中返回单播地址。
SIOCSIFADDR:用ifr_addr成员设 置接口地址,这个接口的初始化函数也被调用。
SIOCGIFFLAGS:
在ifr_flags成员中返回接口标志。这些接口标志的名字格式为IFF_XXX,在
SIOCSIFFLAGS: 用ifr_flags成员设置接口标志。
SIOCGIFDSTADDR: 在ifr_dstaddr成员中返回点到点地址。
SIOCSIFDSTADDR: 在ifr_dstaddr成员中设置点到点地址
SIOCGIFBRDADDR: 在ifr_broadaddr成员中返回广播地址。应 用进程必须首先获取接口标志,然后发出正确的请求;对于广播接口为SIOCGIFBRDADDR, 对于点到点接口为SIOCGIFDSTADDR
SIOCSIFBRDADDR: 用ifr_broadaddr成员设置广播地址。
SIOCGIFNETMASK: 在ifr_addr成员中返回子网掩码。
SIOCSIFNETMASK: 在ifr_addr成员中设置子网掩码。
SIOCGIFMETRIC: 用ifr_metric成员返回接口测度。接口测度由内核为每个接口维护,不过使用他的是路由守护 进程routed。接口测度被routed加 到跳数上。
SIOCSIFMETRIC: 用ifr_metric成员设置接口的路由测度。
ARP高 速缓存操作
ARP告 诉缓存也通过ioctl函数操纵。使用路由域套接口的系统往往改用路由套接口访问
ARP高
速缓存。这些请求使用如下的arpreq结构,定义在
struct arpreq {
struct sockaddr arp_pa;
struct sockaddr arp_ha;
int arp_flags;
};
#define ATF_INUSE 0x01 //entry in use
#define ATF_COM 0x02 //completed entry (hardware addr valid)
#define ATF_PERM 0x04 // permanent entry
#define ATF_PUBL 0x08 // published entry (respond for other host )
Ioctl的 第三个必须指向某个arpreq结构,操纵ARP高 速缓存的ioctl请求有以下三个:
SIOCSARP: 把一个新的表项加入ARP告诉缓存中区,或者修改其中 已经存在的一个表项,其中arp_pa是一个含有IP地 址的网际套接口地址结构,arp_ha则是一个通用套接口地址结构,他的sa_family值 为AF_unspec,sa_data中含有硬件地址(例如6直 接的以太网地址)。ATF_PERM和ATF_PUBL这 两个标志也可以由应用进程指定。另外两个标志(ATF_INUSE和ATF_COM) 则由内核设置。
SIOCDARP: 从ARP告诉缓存中删除一个表项。调用者指定要删除表 项的网际地址。
SIOCGARP: 从ARP高速缓存中获取一个表项。调用者指定网际地 址,相应的硬件地址(例外以太网地址)随标志一起返回。
只有超级用户才能增加或删除表项。 这三个请求通常由arp程序发出。
注意ioctl没 有办法列出ARP高速缓存中的所有表项。当指定-a标 志执行arp命令时,大多
数版本的arp程 序通过读取内核的内存( /dev/kmem )获得ARP高 速缓存的当前内容。
路由 表操作
有些系统提供2个 用于操纵路由表的ioctl请求。这2个请 求要求ioctl的第三个参数是指向
某个rtentry结
构的一个指针,该结构定义在
程序发出。只有超级用户才能发出这 些请求。
SIOCADDRT: 往路由表中增加一个表项
SIOCDELRT: 从路由表中删除一个表项
Ioctl没 有办法列出路由表中的所有表项。这个操作通常由netstat程序在指定-r标 志自行四
完成。netstat程 序通过读取内核的内存 (/dev/kmem) 获得整个路由表。用sysctl同样可
以做到。
如下是chinaunix论 坛给 出的一个例子。
#include
#include
#include
#include
#include
#include
#include
void err_sys(const char *errmsg);
int main(void)
{
int i, sockfd;
struct ifreq ifr;
struct arpreq arpr;
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
err_sys("socket");
/* get ip address */
if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1)
err_sys("1-ioctl");
/* get hardware address */
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == -1)
err_sys("2-ioctl");
/* output hardware address */
for (i = 0; i < 6; i++) {
unsigned char *mac = (unsigned char *)
ifr.ifr_hwaddr.sa_data;
printf("%x", (int) mac[i]);
if (i != 5)
printf("%c", ':');
}
exit(0);
}
void err_sys(const char *errmsg)
{
perror(errmsg);
exit(1);
}