首页 > 代码库 > 《网络编程》ioctl 操作

《网络编程》ioctl 操作

概述

         ioctl 函数和 fcntl 函数类似,都可用于对描述符的操作,获取或设置已打开描述符的属性,但是两个函数在网络编程相关中操作针对不同了类型,fcntl 函数有套接字操作、文件操作,而 ioctl 函数除了可以操作 fcntl 函数可操作的类型之外,还可以进行接口操作、路由表操作、 ARP 高速缓存操作以及流系统操作。

ioctl 函数

该函数可以实现对已打开描述符进程操作,其定义如下:

/* 函数功能:操作描述符,设置已打开的描述符属性;
 * 函数原型:
 */
#include <sys/ioctl.h>
/* Perform the I/O control operation specified by REQUEST on FD.
   One argument may follow; its presence and type depend on REQUEST.
   Return value depends on REQUEST.  Usually -1 indicates error.  */
int ioctl (int fd, unsigned long int request, .../* void *arg */);
/*
 * 说明:
 * 把和网络相关的请求request划分为以下 6 类:
 * (1)套接字操作
 * (2)文件操作
 * (3)接口操作
 * (4)ARP高速缓存操作
 * (5)路由表操作
 * (6)流系统
 */
/*
*fcntl函数
*功能:操纵文件描述符,设置已打开的文件的属性*/
int fcntl(int fd, int cmd, ... /* arg */ );
/*说明:
 * fcntl 函数提供了与网络编程相关的特性如下:
 * (1)非阻塞式IO;标志:O_NONBLOCK
 * (2)信号驱动式IO;标志:O_ASYNC
 * (3)套接字属性;标志:F_GETOWN、F_SETOWN
 *
 * cmd的取值可以如下:
 * 复制文件描述符
 *   F_DUPFD (long)
 * 设置/获取文件描述符标志
 *   F_GETFD (void)
 *   F_SETFD (long)
 * 设置/获取文件状态标志
 *   F_GETFL (void)
 *   F_SETFL (long)
 * 获取/设置文件锁
 *   F_GETLK
 *   F_SETLK,F_SETLKW
 */
其中 request 参数以及 arg 地址指向的数据类型与网络相关,以下列出了不同网络类型对应的信息:


下面针对由请求 request 划分的 6 大类进行分析。


套接字操作

由上面表格可以知道,在套接字操作中,ioctl 函数的请求 request 参数有 3 可选值,并且这 3 个值对应的第三个参数的数据类型是一个指向整数的指针。

  1. SIOCATMARK:若套接字的 读指针 当前位于带外标记,则 ioctl 函数通过第三个参数指向的整数返回非 0 值;否则返回 0 值;
  2. SIOCGPRP:通过由第三个参数指向的整数返回套接字的 进程 ID进程组 ID,该 ID 指定针对套接字的 SIGIO 或 SIGURG 信号的接收进程;等价于 fcntl 函数指定 F_GETOWN 命令;
  3. SIOCSPGRP:把套接字的 进程 ID 或 进程组 ID 设置成由第三个参数指向的整数,该 ID 指定针对套接字的 SIGIO 或 SIGURG 信号的接收进程;等价于 fcntl 函数指定 F_SETOWN 命令;


文件操作

根据 请求 request 参数和第三个参数不同有以下的内容:

  1. FIONBIO:根据 ioctl 函数第三个参数指向一个 0 值或非 0 值,可清除或设置套接字的非阻塞 I/O 标志;
  2. FIOASYNC:根据 ioctl 函数第三个参数指向一个 0 值或非 0 值,可清除或设置套接字的信号驱动异步 I/O 标志,决定是否接收针对套接字的异步 I/O 信号(SIGIO);
  3. FIONREAD:根据 ioctl 函数第三个参数指向的整数返回当前套接字接收缓冲区的字节数;
  4. FIOSETOWN:对于套接字和前面的 SIOCSPGRP 等效:
  5. FIOGETOWN:对于套接字和前面的 SIOCGPGRP 等效:


接口操作

        需要处理网络接口,首先必须从内核获取配置在系统上的所有接口信息。具体请求 request 参数的取值由上表可知。SIOCGIFCONF:从内核中获取系统中配置的所有接口。它使用了结构 ifconf,ifconf 又使用了 ifreq 结构。在调用 ioctl 之前分配一个缓冲区和一个 ifconf 结构,然后初始化后者,iotctl 的第三个参数指向 ifconf 结构。

/* 结构定义如下:*/
struct ifconf {
    int ifc_len; /* size of buffer, value-result */
    union {
        caddr_t ifcu_buf; /* input from user->kernel */
        struct ifreq *ifcu_req; /* return from kernel->user */
    }ifc_ifcu;
};
#define ifc_buf     ifc_ifcu.ifcu_buf
#define ifc_req     ifc_ifcu.ifcu_req
#define IFNAMSIZ    16
struct ifreq {
    char ifr_name[IFNAMSIZ];
    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
#define ifr_dstaddr     ifr_ifru.ifru_dstaddr
#define ifr_broadaddr   ifr_ifru.broadaddr
#define ifr_flags       ifr_ifru.ifru_flags
#define ifr_metric      ifr_ifru.ifru_metric
#define ifr_data        ifr_ifru.ifru_data

ARP 高速缓存操作

可以使用 ioctl 函数操作 ARP 高速缓存,ioctl 函数的第三个参数必须指向一个 arpreq 结构,其结构定义如下:

/* ARP ioctl request.  */
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];
  };


/* ARP Flag values.  */
#define ATF_COM		0x02		/* Completed entry (ha valid).  */
#define	ATF_PERM	0x04		/* Permanent entry.  */
#define	ATF_PUBL	0x08		/* Publish entry.  */
#define	ATF_USETRAILERS	0x10		/* Has requested trailers.  */
#define ATF_NETMASK     0x20            /* Want to use a netmask (only
					   for proxy entries).  */
#define ATF_DONTPUB	0x40		/* Don't answer this addresses.  */
#define ATF_MAGIC	0x80		/* Automatically added entry.  */


/* Support for the user space arp daemon, arpd.  */
#define ARPD_UPDATE	0x01
#define ARPD_LOOKUP	0x02
#define ARPD_FLUSH	0x03
以下是请求参数的值及其功能:

  1. SIOCSARP:把一个新的表项添加到 ARP 高速缓存,或修改其中已经存在的一个表项;
  2. SIOCDARP:从 ARP 高速缓存中删除指定的一个表项;
  3. SIOCGARP:从 ARP 高速缓存中获取一个表项;


路由表操作

ioctl 函数有 2 个请求可以操作路由,其对应的第三个参数是指向 rtentry 结构的指针,该结构定义如下

/* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
struct rtentry
  {
    unsigned long int rt_pad1;
    struct sockaddr rt_dst;		/* Target address.  */
    struct sockaddr rt_gateway;		/* Gateway addr (RTF_GATEWAY).  */
    struct sockaddr rt_genmask;		/* Target network mask (IP).  */
    unsigned short int rt_flags;
    short int rt_pad2;
    unsigned long int rt_pad3;
    unsigned char rt_tos;
    unsigned char rt_class;
#if __WORDSIZE == 64
    short int rt_pad4[3];
#else
    short int rt_pad4;
#endif
    short int rt_metric;		/* +1 for binary compatibility!  */
    char *rt_dev;			/* Forcing the device at add.  */
    unsigned long int rt_mtu;		/* Per route MTU/Window.  */
    unsigned long int rt_window;	/* Window clamping.  */
    unsigned short int rt_irtt;		/* Initial RTT.  */
  };
以下是请求参数的值及其功能:

  1. SIOCADDRT:往路由表中增加一个表项;
  2. SIOCDELRT:往路由表中删除一个表项;

总结

        这章内容基本是围绕 ioctl 函数中的请求参数和第三个参数进行的,感觉不好记忆,因为都是一些参数标志,相关信息还是查看书本上的讲解,书本上有给出几个例子;

参考资料:
《Unix 网络编程》

《网络编程》ioctl 操作