ioctl相当于一个杂物箱,它一直作为那些不适合归入其他精细定义类别的特性的系统接口。
本章笔记先放着,到时候有需要再看
ioctl函数
#include <unistd.h> int ioctl(int fd,int request,.../* void *arg */);
其中第三个参数总是一个指针,但指针的类型依赖于request参数。
我们可以把网络相关的请求(request)划分为6类:
1.套接字操作
2.文件操作
3.接口操作
4.ARP高速缓存操作
5.路由表操作
6.流系统
下图列出了网络相关ioctl请求的request参数以及arg地址必须指向的数据类型:
套接字操作
SIOCATMARK 如果本套接字的读指针当前位于带外标记,那就通过由第三个参数指向的整数返回一个非0值,否则返回一个0值
SIOCGPGRP 通过由第三个参数指向的整数返回本套接字的进程ID或进程组ID
SIOCSGRP 把本进程进程ID或进程组ID设置成由第三个参数指向的整数
文件操作
FIONBIO 根据ioctl的第三个参数指向一个0值或非0值,可清除或设置本套接字的非阻塞式I/O标志
FIOASYNC 根据ioctl的第三个参数指向一个0值或非0值,可清除或设置本套接字的信号驱动异步I/O标志,它决定是否收取针对本套接字的异步I/O信号(SIGIO)
FIONREAD 通过由ioctl的第三个参数指向的整数返回当前本套接字接收缓冲区中的字节数
FIOSETOWN 对于套接字和SIOCSPGRP等效
FIOGETOWN 对于套接字和SIOCGPGRP等效
接口配置
需处理网络接口的许多程序的初始步骤之一就是从内核获取配置在系统中的所有接口。本任务由SIOCGIFCONF请求完成,它使用ifconf结构,ifconf又使用ifreq结构。这两个结构定义如下:
struct ifconf { int ifc_len; /* size of buffer */ union { char *ifcu_buf; /* input from user->kernel*/ struct ifreq *ifcu_req; /* return from kernel->user*/ } ifc_ifcu; }; #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ #define ifc_req ifc_ifcu.ifcu_req /* array of structures */ //ifreq用来保存某个接口的信息 //if.h 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.ifru_broadaddr
在调用ioctl前我们先分配一个缓冲区和一个ifconf结构,然后初始化后者。下面展示这个ifconf结构的初始化结果,其中缓冲区的大小为1024字节
假设内核返回2个ifreq结构,在ioctl返回时通过同一个ifconf结构所返回的值如下图。缓冲区被填入两个ifreq结构,而且ifconf结构的ifc_len成员也被更新
get_ifi_info函数(暂时不看)
我们使用ioctl开发一个名为get_ifi_info的函数,它返回一个结构链表,其中每个结构对应一个当前处于“up”状态的接口。
头文件
1 /* Our own header for the programs that need interface configuration info. 2 Include this file, instead of "unp.h". */ 3 4 #ifndef __unp_ifi_h 5 #define __unp_ifi_h 6 7 #include "unp.h" 8 #include <net/if.h> 9 10 #define IFI_NAME 16 /* same as IFNAMSIZ in <net/if.h> */ 11 #define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */ 12 13 struct ifi_info { 14 char ifi_name[IFI_NAME]; /* interface name, null-terminated */ 15 short ifi_index; /* interface index */ 16 short ifi_mtu; /* interface MTU */ 17 u_char ifi_haddr[IFI_HADDR]; /* hardware address */ 18 u_short ifi_hlen; /* # bytes in hardware address: 0, 6, 8 */ 19 short ifi_flags; /* IFF_xxx constants from <net/if.h> */ 20 short ifi_myflags; /* our own IFI_xxx flags */ 21 struct sockaddr *ifi_addr; /* primary address */ 22 struct sockaddr *ifi_brdaddr;/* broadcast address */ 23 struct sockaddr *ifi_dstaddr;/* destination address */ 24 struct ifi_info *ifi_next; /* next of these structures */ 25 }; 26 27 #define IFI_ALIAS 1 /* ifi_addr is an alias */ 28 29 /* function prototypes */ 30 struct ifi_info *get_ifi_info(int, int); 31 struct ifi_info *Get_ifi_info(int, int); 32 void free_ifi_info(struct ifi_info *); 33 34 #endif /* __unp_ifi_h */
main函数
1 #include "unpifi.h" 2 3 int 4 main(int argc, char **argv) 5 { 6 struct ifi_info *ifi, *ifihead; 7 struct sockaddr *sa; 8 u_char *ptr; 9 int i, family, doaliases; 10 11 if (argc != 3) 12 err_quit("usage: prifinfo <inet4|inet6> <doaliases>"); 13 14 if (strcmp(argv[1], "inet4") == 0) 15 family = AF_INET; 16 #ifdef IPv6 17 else if (strcmp(argv[1], "inet6") == 0) 18 family = AF_INET6; 19 #endif 20 else 21 err_quit("invalid <address-family>"); 22 doaliases = atoi(argv[2]); 23 24 for (ifihead = ifi = Get_ifi_info(family, doaliases); 25 ifi != NULL; ifi = ifi->ifi_next) { 26 printf("%s: ", ifi->ifi_name); 27 if (ifi->ifi_index != 0) 28 printf("(%d) ", ifi->ifi_index); 29 printf("<"); 30 /* *INDENT-OFF* */ 31 if (ifi->ifi_flags & IFF_UP) printf("UP "); 32 if (ifi->ifi_flags & IFF_BROADCAST) printf("BCAST "); 33 if (ifi->ifi_flags & IFF_MULTICAST) printf("MCAST "); 34 if (ifi->ifi_flags & IFF_LOOPBACK) printf("LOOP "); 35 if (ifi->ifi_flags & IFF_POINTOPOINT) printf("P2P "); 36 printf(">\n"); 37 /* *INDENT-ON* */ 38 39 if ( (i = ifi->ifi_hlen) > 0) { 40 ptr = ifi->ifi_haddr; 41 do { 42 printf("%s%x", (i == ifi->ifi_hlen) ? " " : ":", *ptr++); 43 } while (--i > 0); 44 printf("\n"); 45 } 46 if (ifi->ifi_mtu != 0) 47 printf(" MTU: %d\n", ifi->ifi_mtu); 48 49 if ( (sa = ifi->ifi_addr) != NULL) 50 printf(" IP addr: %s\n", 51 Sock_ntop_host(sa, sizeof(*sa))); 52 if ( (sa = ifi->ifi_brdaddr) != NULL) 53 printf(" broadcast addr: %s\n", 54 Sock_ntop_host(sa, sizeof(*sa))); 55 if ( (sa = ifi->ifi_dstaddr) != NULL) 56 printf(" destination addr: %s\n", 57 Sock_ntop_host(sa, sizeof(*sa))); 58 } 59 free_ifi_info(ifihead); 60 exit(0); 61 }
get_ifi_info.c
1 /* include get_ifi_info1 */ 2 #include "unpifi.h" 3 4 struct ifi_info * 5 get_ifi_info(int family, int doaliases) 6 { 7 struct ifi_info *ifi, *ifihead, **ifipnext; 8 int sockfd, len, lastlen, flags, myflags, idx = 0, hlen = 0; 9 char *ptr, *buf, lastname[IFNAMSIZ], *cptr, *haddr, *sdlname; 10 struct ifconf ifc; 11 struct ifreq *ifr, ifrcopy; 12 struct sockaddr_in *sinptr; 13 struct sockaddr_in6 *sin6ptr; 14 15 sockfd = Socket(AF_INET, SOCK_DGRAM, 0); 16 17 lastlen = 0; 18 len = 100 * sizeof(struct ifreq); /* initial buffer size guess */ 19 for ( ; ; ) { 20 buf = Malloc(len); 21 ifc.ifc_len = len; 22 ifc.ifc_buf = buf; 23 if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { 24 if (errno != EINVAL || lastlen != 0) 25 err_sys("ioctl error"); 26 } else { 27 if (ifc.ifc_len == lastlen) 28 break; /* success, len has not changed */ 29 lastlen = ifc.ifc_len; 30 } 31 len += 10 * sizeof(struct ifreq); /* increment */ 32 free(buf); 33 } 34 ifihead = NULL; 35 ifipnext = &ifihead; 36 lastname[0] = 0; 37 sdlname = NULL; 38 /* end get_ifi_info1 */ 39 40 /* include get_ifi_info2 */ 41 for (ptr = buf; ptr < buf + ifc.ifc_len; ) { 42 ifr = (struct ifreq *) ptr; 43 44 #ifdef HAVE_SOCKADDR_SA_LEN 45 len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len); 46 #else 47 switch (ifr->ifr_addr.sa_family) { 48 #ifdef IPV6 49 case AF_INET6: 50 len = sizeof(struct sockaddr_in6); 51 break; 52 #endif 53 case AF_INET: 54 default: 55 len = sizeof(struct sockaddr); 56 break; 57 } 58 #endif /* HAVE_SOCKADDR_SA_LEN */ 59 ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ 60 61 #ifdef HAVE_SOCKADDR_DL_STRUCT 62 /* assumes that AF_LINK precedes AF_INET or AF_INET6 */ 63 if (ifr->ifr_addr.sa_family == AF_LINK) { 64 struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr; 65 sdlname = ifr->ifr_name; 66 idx = sdl->sdl_index; 67 haddr = sdl->sdl_data + sdl->sdl_nlen; 68 hlen = sdl->sdl_alen; 69 } 70 #endif 71 72 if (ifr->ifr_addr.sa_family != family) 73 continue; /* ignore if not desired address family */ 74 75 myflags = 0; 76 if ( (cptr = strchr(ifr->ifr_name, ‘:‘)) != NULL) 77 *cptr = 0; /* replace colon with null */ 78 if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) { 79 if (doaliases == 0) 80 continue; /* already processed this interface */ 81 myflags = IFI_ALIAS; 82 } 83 memcpy(lastname, ifr->ifr_name, IFNAMSIZ); 84 85 ifrcopy = *ifr; 86 Ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); 87 flags = ifrcopy.ifr_flags; 88 if ((flags & IFF_UP) == 0) 89 continue; /* ignore if interface not up */ 90 /* end get_ifi_info2 */ 91 92 /* include get_ifi_info3 */ 93 ifi = Calloc(1, sizeof(struct ifi_info)); 94 *ifipnext = ifi; /* prev points to this new one */ 95 ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ 96 97 ifi->ifi_flags = flags; /* IFF_xxx values */ 98 ifi->ifi_myflags = myflags; /* IFI_xxx values */ 99 #if defined(SIOCGIFMTU) && defined(HAVE_STRUCT_IFREQ_IFR_MTU) 100 Ioctl(sockfd, SIOCGIFMTU, &ifrcopy); 101 ifi->ifi_mtu = ifrcopy.ifr_mtu; 102 #else 103 ifi->ifi_mtu = 0; 104 #endif 105 memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME); 106 ifi->ifi_name[IFI_NAME-1] = ‘\0‘; 107 /* If the sockaddr_dl is from a different interface, ignore it */ 108 if (sdlname == NULL || strcmp(sdlname, ifr->ifr_name) != 0) 109 idx = hlen = 0; 110 ifi->ifi_index = idx; 111 ifi->ifi_hlen = hlen; 112 if (ifi->ifi_hlen > IFI_HADDR) 113 ifi->ifi_hlen = IFI_HADDR; 114 if (hlen) 115 memcpy(ifi->ifi_haddr, haddr, ifi->ifi_hlen); 116 /* end get_ifi_info3 */ 117 /* include get_ifi_info4 */ 118 switch (ifr->ifr_addr.sa_family) { 119 case AF_INET: 120 sinptr = (struct sockaddr_in *) &ifr->ifr_addr; 121 ifi->ifi_addr = Calloc(1, sizeof(struct sockaddr_in)); 122 memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); 123 124 #ifdef SIOCGIFBRDADDR 125 if (flags & IFF_BROADCAST) { 126 Ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); 127 sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr; 128 ifi->ifi_brdaddr = Calloc(1, sizeof(struct sockaddr_in)); 129 memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in)); 130 } 131 #endif 132 133 #ifdef SIOCGIFDSTADDR 134 if (flags & IFF_POINTOPOINT) { 135 Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); 136 sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr; 137 ifi->ifi_dstaddr = Calloc(1, sizeof(struct sockaddr_in)); 138 memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in)); 139 } 140 #endif 141 break; 142 143 case AF_INET6: 144 sin6ptr = (struct sockaddr_in6 *) &ifr->ifr_addr; 145 ifi->ifi_addr = Calloc(1, sizeof(struct sockaddr_in6)); 146 memcpy(ifi->ifi_addr, sin6ptr, sizeof(struct sockaddr_in6)); 147 148 #ifdef SIOCGIFDSTADDR 149 if (flags & IFF_POINTOPOINT) { 150 Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); 151 sin6ptr = (struct sockaddr_in6 *) &ifrcopy.ifr_dstaddr; 152 ifi->ifi_dstaddr = Calloc(1, sizeof(struct sockaddr_in6)); 153 memcpy(ifi->ifi_dstaddr, sin6ptr, sizeof(struct sockaddr_in6)); 154 } 155 #endif 156 break; 157 158 default: 159 break; 160 } 161 } 162 free(buf); 163 return(ifihead); /* pointer to first structure in linked list */ 164 } 165 /* end get_ifi_info4 */ 166 167 /* include free_ifi_info */ 168 void 169 free_ifi_info(struct ifi_info *ifihead) 170 { 171 struct ifi_info *ifi, *ifinext; 172 173 for (ifi = ifihead; ifi != NULL; ifi = ifinext) { 174 if (ifi->ifi_addr != NULL) 175 free(ifi->ifi_addr); 176 if (ifi->ifi_brdaddr != NULL) 177 free(ifi->ifi_brdaddr); 178 if (ifi->ifi_dstaddr != NULL) 179 free(ifi->ifi_dstaddr); 180 ifinext = ifi->ifi_next; /* can‘t fetch ifi_next after free() */ 181 free(ifi); /* the ifi_info{} itself */ 182 } 183 } 184 /* end free_ifi_info */ 185 186 struct ifi_info * 187 Get_ifi_info(int family, int doaliases) 188 { 189 struct ifi_info *ifi; 190 191 if ( (ifi = get_ifi_info(family, doaliases)) == NULL) 192 err_quit("get_ifi_info error"); 193 return(ifi); 194 }
运行情况
接口操作
SIOCGIFCONF请求为每个已配置的接口返回其明知以及一个套接字地址结构。我们接着可以发出多个接口类的其他请求以设置或获取每个接口的其他特性
这些请求的get版本(SIOCGxxx)通常由netstat程序发出,set版本(SIOCSxxx)通常由ifconfig程序发出
这些请求接受或返回一个ifreq结构中的信息,而这个结构的地址作为ioctl调用的第三个参数。
SIGCGIFADDR 在ifr_addr成员中返回单播地址
SIOCSIFADDR 用ifr_addr成员设置接口地址
SIOCGIFFLAGS 在ifr_flags成员中返回接口标志,如:是否处于在工状态(IFF_UP)
SIOCSIFFLAGS 用ifr_flags成员设置接口标志
SIOCGIFDSTADDR 在if_dstaddr成员中返回点到点地址
SIGCSIFDSTADDR 用if_dstaddr成员设置点到点地址
SIOCGIFBRDADDR 在ifr_broadaddr成员中返回广播地址
SIOCSIFBRDADDR 用ifr_broadaddr成员设置广播地址
SIOCGIFNETMASK 在ifr_addr成员中返回子网掩码
SIOCSIFNETMASK 用ifr_addr成员设置子网掩码
SIOCGIFMETRIC 在ifr_metric成员返回接口测度
SIGCSIFMETRIC 用ifr_metric成员设置接口测度
ARP高速缓存操作
这些请求使用如下所示的arpreq结构
struct arpreq{ struct sockaddr arp_pa; //协议地址 struct sockaddr arp_ha; //硬件地址 int arp_flags;//标志位 } #define ATR_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 (repond for other host) */
操纵ARP高速缓存的ioctl请求有以下3个
SIOCSARP 把一个新的表项加到ARP高速缓存,或者修改其中一个已经存在的表项
SIOCDARP 从ARP高速缓存中删除一个表项。调用者指定要删除的arp_pa
SIOCGARP 从ARP高速缓存中获取一个表项。调用者指定arp_pa,相应的硬件地址随标志一起返回
下面程序用于输出主机的硬件地址
1 #include "unpifi.h" 2 #include <net/if_arp.h> 3 4 int 5 main(int argc, char **argv) 6 { 7 int sockfd; 8 struct ifi_info *ifi; 9 unsigned char *ptr; 10 struct arpreq arpreq; 11 struct sockaddr_in *sin; 12 13 sockfd = Socket(AF_INET, SOCK_DGRAM, 0); 14 for (ifi = get_ifi_info(AF_INET, 0); ifi != NULL; ifi = ifi->ifi_next) { 15 printf("%s: ", Sock_ntop(ifi->ifi_addr, sizeof(struct sockaddr_in))); 16 17 sin = (struct sockaddr_in *) &arpreq.arp_pa; 18 memcpy(sin, ifi->ifi_addr, sizeof(struct sockaddr_in)); 19 20 if (ioctl(sockfd, SIOCGARP, &arpreq) < 0) { 21 err_ret("ioctl SIOCGARP"); 22 continue; 23 } 24 25 ptr = &arpreq.arp_ha.sa_data[0]; 26 printf("%x:%x:%x:%x:%x:%x\n", *ptr, *(ptr+1), 27 *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)); 28 } 29 exit(0); 30 }
先调用get_ifi_info获取本机所有的IP地址,然后在一个循环中遍历每个地址
使用inet_ntop显示IP地址
使用ioctl调用返回硬件地址(SIOCGARP)
路由表操作
有些系统提供两个用于操纵路由表的ioctl请求,在支持路由域套接字的系统中,这些请求改由路由套接字完成
SIOCADDRT 往路由表中增加一个表项
SIOCDELRT 从路由表中删除一个表项