UNP_17_ioctl操作
17.1 概述
ioctl 函数传统上一直作为那些不适合归入其他精细定义类别的特性的系统接口。POSIX 创造了一系列函数用来代替 icotl,但是它还是必不可少的。网络程序经常在程序启动后执行使用 ioctl 获取所在主机全部网络接口的信息:接口地址,是否支持广播,是否支持多播等等
17.2 ioctl 函数
本函数影响由 fd 参数引用的一个打开的文件
#include<unistd.h>
int ioctl(int fd, int request, ...)
// 返回:成功返回 0,出错返回 -1
第三个参数总是一个指针,但是指针的类型依赖于 request 参数
网络相关请求可以分为 6 类:
- 套接字操作
- 文件操作
- 接口操作
- ARP 高速缓存操作
- 路由表操作
- 流系统 详细的表见 UNP 图17-1
17.3 套接字操作
用于套接字的 ioctl 有三个,都要求 ioctl 的第三个参数指向某一个整数的一个指针
- SIOCATMARK:如果本套接字的都指针当前处于带外标记,那就通过第三个指向的整数返回一个非 0 值;否则返回一个 0 值。POSIX 用函数 sockatmark 替换本请求
- SIOCGPGRP:通过第三个参数指向的整数返回本套接字的进程 ID 或进程组 ID,该 ID 指定本套接字的 SIGIO 或 SIGURG 信号的接收进程。本请求和 fcntl 的 F_GETOWN 命令等效
- SIOCSPGRP:把本套接字的进程 ID 或进程组 ID 设置成由第三个参数指向的整数,该 ID 指定本套接字的 SIGIO 或 SIGURG 信号的接收进程。本请求和 fcntl 的 F_SETOWN 命令等效
17.4 文件操作
文件操作的请求以 FIO 开头,它们可能还适用于除套接字外某些特定类型的文件。本节进讨论对于套接字的操作
- FIONBIO:根据 ioctl 的第三个参数指向一个 0 值或非 0 值,可清除或设置本套接字的非阻塞 I/O 标志。本请求和 O_NONBLOCK 文件状态标志等效,可以通过 fcntl 的 F_SETFL 命令清除或设置该标志
- FIOASYNC:根据 ioctl 的第三个参数指向一个 0 值或非 0 值,可清除或设置针对本套接字的信号驱动异步 I/O 标志,它决定是否收取针对本套接字的异步 I/O 信号(SIGIO)。本请求和 O_ASYNC 文件状态标志等效,可以通过 fcntl 的 F_SETFL 命令清除或设置该标志
- FIONREAO:通过由 ioctl 的第三个参数指向的整数返回当前在套接字接收缓冲区中的字节数。本特性同样适用于文件、管道和终端
- FIOSETOWN:对于套接字,和 SIOCSPGRP 等效
- FIOGETOWN:对于套接字,和 SIOCGPGRP 等效
17.5 接口配置
使用 SIOCGIFCONF 请求可以获取所有配置在系统中的接口,它使用 ifconf 结构,ifconf 中又使用 ifreq 结构,内核会返回多个 ifreq 结构,用来返回 ioctl 缓冲区中的信息,我们所需要的信息就在缓冲区中,ifreq 是索引。
17.6 get_ifi_info 函数
UNP 实现了一个 get_ifi_info 函数用来获取系统中的所有接口,返回一个结构链表,每一个结构对应一个打开的接口。使用 SIOCGIFCONF ioctl 实现这个函数。
17.7 接口操作
SIOCGIFCONF 请求给每个已配置的接口返回其名字以及一个套接字地址结构。我们接着可以发出多个接口类其他请求以设置或获取每个接口的其他特征。这些请求的获取版本通常使用 netstat 程序发出,设置版本通常使用 ifconfig 程序发出。任何用户都可以获取,但只有超级用户才可以设置
这些请求接受或返回一个 ifreq 结构中的信息,作为第三个参数。接口总是以其名字标识,在 ifreq 的 ifr_name 中指定,如 le0、lo0、ppp0 等等
- SIOCGIFADDR:在 if_addr 成员中返回单播地址
- SIOCSIFADDR:在 if_addr 成员设置接口地址。这个接口的初始化函数也会被调用
- SIOCGIFFLAGS:用 ifr_flags 成员返回接口标志,标志用 IFF_xx 格式,可以表示接口是否处于工作状态等等
- SIOCSIFFLAGS:用 ifr_flags 成员设置接口标志
- SIOCGIFDSTADDR:用 ifr_dstaddr 成员返回点到点地址
- SIOCSIFDSTADDR:用 ifr_dstaddr 成员设置点到点地址
- SIOCGIFBRDADDR:用 ifr_boardaddr 成员返回广播地址。应用进程需要先获取接口标志,然后发出正确的请求,对于广播接口为 SIOCGIFBRDADDR,对于点到点接口为 SIOCGIFDSTADDR
- SIOCSIFBRDADDR:用 ifr_boardaddr 成员设置广播地址
- SIOCGIFNETMASK:在 if_addr 成员中返回子网掩码
- SIOCSIFNETMASK:在 if_addr 成员中设置子网掩码
- SIOCGIFMETRIC:用 ifr_metric 成员返回接口测度,接口测度由内核为每个接口维护,不过使用它的是路由守护进程 routed。接口测度被 routed 加到跳数上(使得某个接口更不被看好)
- SIOCSIFMETRIC:用 ifr_metric 成员设置路由的接口测度
17.8 arp 高速缓存操作
ARP 高速缓存也通过 ioctl 进行操作,在使用路由域套接字的系统往往改由路由套接字访问 ARP 高速缓存。ioctl 的第三个参数指向 arpreq 结构
struct arpreq{
struct sockaddr arp_pa;
struct sockaddr arp_ha;
int srp_flags;
}
#define ATF_INUSE 0x01
#define ATF_COM 0x02
#define ATF_PERM 0x03
#define ATF_PUBL 0x04
ARP 高速缓存操作有三个请求:
- SIOCSARP:把一个新的表项添加到 ARP 高速缓存中,或修改已经存在的表项。其中 arp_pa 是一个含有 IP 地址和的网际网套接字地址结构,arp_ha 则是一个通用套接字地址结构。
- SIOCDARP:从 ARP 高速缓存中删除一个表项,调用者指定要删除的表项的网际网地址
- SIOCGARP:从 ARP 高速缓存中获取一个表项,调用者指定网际网地址,相应的硬件地址(比如以太网地址)随标志一起返回
ioctl 没有办法列出 ARP 高速缓存中的所有表项
17.9 路由表操作
某些系统提供两个路由表操作的 ioctl 请求,并要求 ioctl 的第三个参数是指向某个 rtentry 结构的一个指针,该结构定义在 <net/route.h> 中。通常由 route 程序发出。只有超级用户才可以使用。在支持路由域套接字的系统中,这些请求改由路由套接字执行
- SIOCADDRT:往路由表中增加一个表项
- SIOCDELRT:从路由表中删除一个表项
ioctl 没有办法列出路由表中的所有表项
小结
网络编程的相关请求可以分为 6 类:
- 套接字操作(是否位于外带标记等)
- 文件操作(设置或清除非阻塞标志等)
- 接口操作(返回接口列表,获取广播地址等)
- ARP 高速缓存操作(创建,修改,获取,删除)
- 路由表操作(增加或删除)
- 流系统(31 章笔记)
我们将使用其中的套接字操作和文件操作,而接口列表的获取是一个相当常规的操作,我们为此开发了一个完成本操作的函数。只有一些特殊的用途会使用 ioctl 的 ARP 高速缓存操作和路由表操作