本函数影响由 fd 参数引用的一个打开的文件。
#include<unistd.h>
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, 在 <net/if.h> 头 文件中定义。举例来说,这些标志指示接口是否处于 UP 即在工状态 (IFF_UP), 是 否为一个点到点接口( IFF_POINTOPOINT ) , 是 否支持广播( IFF_BROADCAST ) , 等 等。
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 结构,定义在 <net/if_arp.h>
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 结 构的一个指针,该结构定义在 <net/route.h> 头文件中。这些请求通常由 route
程序发出。只有超级用户才能发出这 些请求。
SIOCADDRT : 往路由表中增加一个表项
SIOCDELRT : 从路由表中删除一个表项
Ioctl 没 有办法列出路由表中的所有表项。这个操作通常由 netstat 程序在指定 -r 标 志自行四
完成。 netstat 程 序通过读取内核的内存 ( /dev/kmem ) 获得整个路由表。用 sysctl 同样可
以做到。