使用套接字 ioctl 命令
級別: 中級
Katiyar Manish ([email protected]),
軟件工程師, IBM, Intel, Microsoft,HP
Shweta Gupta ([email protected]),
高級軟件工程師, IBM
2007 年 3 月 13 日
瞭解更多關於套接字 I/O 控制 (ioctl) 命令的內容,以及如何使用它們完成各種網絡相關的操作.操作系統爲套接字、路由表、ARP 表、全局網絡參數和接口提供了相應的控制操作方式。本文適用於對 Internet Protocol Version 4 (IPv4) 和 Internet Protocol Version 6 (IPv6) 堆棧網絡級操作感興趣的 AIX® Version 5.3 開發人員。
文件描述符是低層的輸入和輸出接口。描述符可以表示到設備、管道或套接字的連接,這些連接用於與另一個進程或普通文件進行通信。I/O 控制 (ioctl) 函數調用可以用來對特殊文件的基礎設備參數進行操作。它們可以完成與打開的文件描述符相關聯的控制功能。這些命令涉及文件、流、普通數據鏈路控制以及其他各種設備。
本文將討論 AIX® Version 5.3 中提供的與網絡操作和套接字相關的命令。在下列文件中列出了與套接字相關的命令和結構:
sys/ioctl.h
net/if_arp.h
net/if.h
net/netopt.h
netinet/in.h
通常,網絡程序需要了解系統中所有有關網絡接口和 IP 地址的可用信息。現在,未來的應用程序可以支持 IPv4 和 IPv6 雙協議棧。ioctl 結構需要對指針進行遍歷和操作,以處理 IPv4 和 IPv6 地址在長度上的差別(除了使用合適的套接字結構 sockaddr_in6 或 sockaddr_storage 之外)。
AIX Version 5.3 提供了很多 ioctl 套接字控制選項,以提取各種有關網絡接口的信息。這些 ioctl 命令用於查詢接口的狀態並對其屬性進行操作。下面的部分中包含了一些有用的命令的代碼段。有關 ioctl 命令的完整列表,請參見參考資料部分。
下面的清單介紹了一些最重要的結構,使用 ioctl 套接字命令時常常用到這些結構。
清單 1. struct ifreq (/usr/include/net/if.h)
/* Interface request structure used for socket * ioctl's. All interface ioctl's must have parameter * definitions which begin with ifr_name. The * remainder may be interface specific. */ struct ifreq { #ifndef IFNAMSIZ #define IFNAMSIZ 16 #endif char ifr_name[IFNAMSIZ]; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; __ulong32_t ifru_flags; int ifru_metric; caddr_t ifru_data; u_short ifru_site6; __ulong32_t ifru_mtu; int ifru_baudrate; } ifr_ifru; Following macros are provided for convenience #define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other 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 */ #define ifr_site6 ifr_ifru.ifru_site6 /* IPv6 site index */ #define ifr_mtu ifr_ifru.ifru_mtu /* mtu of interface */ #define ifr_isno ifr_ifru.ifru_data /* pointer to if_netopts */ #define ifr_baudrate ifr_ifru.ifru_baudrate /* baudrate of interface */ }; |
清單 2. struct ifconf (/usr/include/net/if.h)
/* * Structure used in SIOCGIFCONF request. * Used to retrieve interface configuration * for machine (useful for programs which * must know all networks accessible). */ struct ifconf { int ifc_len; /* size of associated buffer */ union { caddr_t ifcu_buf; struct ifreq *ifcu_req; } ifc_ifcu; Following macros are provided for convenience #define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ #define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */ }; |
下面的表 1 介紹了一些接口檢索命令。該信息來源於 IBM System p™ 和 AIX。
表 1. 接口檢索命令
ioctl 命令 | 描述 | |
---|---|---|
SIOCGSIZIFCONF | 獲得獲取 SIOCGIFCONF 返回的所有接口的配置信息所需的內存。
|
|
SIOCGIFADDR SIOCSIFADDR |
SIOCGIFADDR 獲取接口地址,而 SIOCSIFADDR 設置接口地址。ifr.ifr_addr 字段返回地址。
|
|
SIOCGIFCONF | 返回系統中配置的所有接口的配置信息。
|
下面的代碼段可以獲取用來填充所有配置接口的信息所需的緩衝區大小。
清單 3. 使用 SIOCGSIZIFCONF 獲得所需的緩衝區大小
/*Function to get the size needed to allocate for buffers*/ int get_interface_size(int sd){ int ret,size; ret=ioctl(sd,SIOCGSIZIFCONF,&size); if(ret==-1){ perror("Error getting size of interface :"); return ret; } return size; } main { struct ifconf ifc; int sd; sd=socket(AF_INET6,SOCK_DGRAM,0); printf("Size of memory needed = %d/n",get_interface_size(sd)); } |
上面代碼的輸出是:
$> ./myprog Size of memory needed = 628 |
SIOCGIFCONF 和 SIOCGIFADDR 命令可以用來檢索接口的地址。SIOCGIFCONF 可以用來獲取所有配置接口的信息,SIOCGIFADDR 可以用來檢索特定接口的地址。
/*This function uses loops to find out buffer size instead of SIOCGSIZIFCONF allocates the buffer and gets the information list*/ int alloc_buffer_size(int sd,struct ifconf *ifc){ int ret=-1,bsz=sizeof(struct ifreq); int prevsz=bsz; ifc->ifc_req=NULL; ifc->ifc_len=bsz; do{ ifc->ifc_req=(struct ifreq *)realloc(ifc->ifc_req,bsz); if(!ifc->ifc_req){ perror("Malloc failed :"); return ret; } ifc->ifc_len=bsz; ret=ioctl(sd,SIOCGIFCONF,(caddr_t)ifc); if(ret==-1){ perror("Error getting size of interface :"); return ret; } if(prevsz==ifc->ifc_len) break; else{ bsz*=2; prevsz=(0==ifc->ifc_len ? bsz : ifc->ifc_len) ; } }while(1); ifc->ifc_req=(struct ifreq *)realloc(ifc->ifc_req,prevsz); return ifc->ifc_len; } |
在使用 ifreq
結構中的配置信息填充了列表之後,可以對其進行遍歷以檢索所需的數據。根據調用之後該結構中填充的套接字地址的長度,可以對這個指針進行移動。下面的清單
5 顯示了上面代碼的列表中獲得的系統中可用的 IP。
清單 5. 從配置列表中檢索信息
#define MAX(x,y) ((x) > (y) ? (x) : (y)) #define SIZE(p) MAX((p).sa_len, sizeof(p)) /* This function prints all the configured IPs on the * system given the ifconf structure allocated previously*/ void print_ips(struct ifconf *ifc){ char *cp,*cplim,addr[INET6_ADDRSTRLEN]; struct ifreq *ifr=ifc->ifc_req; cp=(char *)ifc->ifc_req; cplim=cp+ifc->ifc_len; for(;cp<cplim;cp+=(sizeof(ifr->ifr_name) + SIZE(ifr->ifr_addr))){ /* NOTE: You cannot just increment cp with sizeof(struct ifreq) * as structures returned are of different length. */ ifr=(struct ifreq *)cp; printf("%s :",ifr->ifr_name); getip(ifr,addr); printf("%s/n",addr); } return ; } main { struct ifconf ifc; int sd; sd=socket(AF_INET6,SOCK_DGRAM,0); alloc_buffer_size(sd,&ifc); print_ips(&ifc); } |
上面代碼的輸出是:
$> ./myprog en0 :6.3.6.0 en0 :9.182.192.169 en0 :fe80::209:6bff:feeb:70b2 sit0 :1.4.4.0 sit0 :::9.182.192.169 lo0 :24.3.0.0 lo0 :127.0.0.1 lo0 :::1 |
清單 6. 使用 SIOCGIFADDR 獲取接口地址
/* Given a ifreq structure this function returns its IP address */ void getip(struct ifreq *ifr,char *addr){ struct sockaddr *sa; sa=(struct sockaddr *)&(ifr->ifr_addr); switch(sa->sa_family){ case AF_INET6: inet_ntop(AF_INET6,(struct in6_addr *)&(((struct sockaddr_in6 *)sa)->sin6_addr), addr,INET6_ADDRSTRLEN); break; default : strcpy(addr,inet_ntoa(((struct sockaddr_in *)sa)->sin_addr)); } return; } main { char netaddr[INET_ADDRSTRLEN]; int sd; sd=socket(AF_INET,SOCK_DGRAM,0); strcpy(ifr.ifr_name,"en0"); if((ioctl(sd,SIOCGIFADDR,(caddr_t)&ifr,sizeof(struct ifreq)))<0) perror("Error : "); getip(&ifr,netaddr); printf("%s/n",netaddr); } |
上面代碼的輸出是:
$./myprog 9.182.192.35 |
下面的表 2 介紹了接口標誌和一些屬性檢索命令。
表 2. 接口標誌和屬性檢索命令
ioctl 命令 | 描述 | |
---|---|---|
SIOCGIFFLAGS SIOCSIFFLAGS |
SIOCGIFFLAGS 可以獲取接口標誌。
SIOCSIFFLAGS 可以設置接口標誌。
|
在使用 SIOCGIFCONF 獲取了接口列表之後,您可以使用 SIOCGIFFLAGS-SIOCSIFFLAGS 對來獲取和設置接口的屬性。接口標誌表示爲 IFF_XXX。有關完整的列表,請參閱文件 /usr/include/net/if.h。
下面的示例使用一對接口標誌來檢索其當前狀態,並檢查環回。也可以使用其他選項。
清單 7. 驗證接口的狀態和類型
for(;cp<cplim;){ ifr=(struct ifreq *)cp; cp+=(sizeof(ifr->ifr_name) + SIZE(ifr->ifr_addr)); printf("%-9s ",ifr->ifr_name); ret=ioctl(sd,SIOCGIFFLAGS,(caddr_t)ifr); if(ret==-1){ perror("Error getting flags for interface :"); return ; } if((ifr->ifr_flags)&IFF_UP) printf("UP"); else printf("DOWN"); if((ifr->ifr_flags)&IFF_LOOPBACK) printf(",Loopback"); printf("/n"); } |
上面代碼的輸出是:
$> ./myprog en0 UP en0 UP en0 UP sit0 UP sit0 UP lo0 UP,Loopback lo0 UP,Loopback lo0 UP,Loopback |
清單 8. 設置接口標誌
Partial output of ifconfig before setting the flag sit0: flags=4900041<UP,RUNNING,LINK0,64BIT> inet6 ::9.182.192.169/96 Code to set the interface flag int set_interface_flag(int sd,short flag,char *interface){ struct ifreq ifr; int oldflags; strcpy(ifr.ifr_name,interface); if((ioctl(sd,SIOCGIFFLAGS,(caddr_t)&ifr))==-1){ perror("Error getting interface flags :"); return -1; } printf("Setting new interface flag 0X%x/n",flag); ifr.ifr_flags|=flag; if((ioctl(sd,SIOCSIFFLAGS,(caddr_t)&ifr))==-1){ perror("Error setting interface flags :"); return -1; } return 0; } main { int sd; sd=socket(AF_INET6,SOCK_DGRAM,0); set_interface_flag(sd,IFF_NOTRAILERS,"sit0"); } |
上面代碼的輸出是:
You must have permission to change the flag $./myprog Setting new interface flag 0X20 $ifconfig -a ............. ............. sit0: flags=4900061<UP,NOTRAILERS,RUNNING,LINK0,64BIT> inet6 ::9.182.192.169/96 ............. ............. |
表 3 介紹了一些用於網絡優化的命令。
表 3. 用於網絡優化的命令
ioctl 命令 | 描述 | ||
---|---|---|---|
SIOCGIFMTU | SIOCGIFMTU 可以獲取接口的最大傳輸單元 (MTU)。
這個 MTU 值存儲在 ifr.ifr_mtu 字段中。
|
||
SIOCGNETOPT SIOCGNETOPT1 |
SIOCGNETOPT 可以獲取一個網絡選項的值。
SIOCGNETOPT1 可以獲取當前值、缺省值和網絡選項的範圍。
|
清單 9. 從 ifreq 結構獲取 MTU
ret=ioctl(sd,SIOCGIFMTU,ifr); if(ret==-1){ perror("Error getting mtu for interface :"); return ; } printf(" %d/n",ifr->ifr_mtu); |
SIOCGNETOPT1 給出當前值、缺省值和網絡選項的範圍。
清單 10. 從 optreq1 結構獲取網絡選項
/*Function to get the network options*/ int print_network_options(int sd){ int ret; struct optreq1 oreq; oreq.getnext=1; while((ioctl(sd,SIOCGNETOPT1,(caddr_t)&oreq))!=-1) printf("%s = %s/n",oreq.name,oreq.data); return 0; } |
上面代碼的輸出是:
$> ./myprog arpqsize = 12 arpt_killc = 20 arptab_bsiz = 7 arptab_nb = 149 ........ ........ ........ ........ ifsize = 256 inet_stack_size = 16 ip6_defttl = 64 ........ |
在 AIX 中,您還可以使用 no
命令來管理各種網絡優化參數。有關更詳細的信息,請參閱相應的 man 頁面。
清單 11. AIX 計算機上“no”命令的輸出
$> no -a|more arpqsize = 12 arpt_killc = 20 arptab_bsiz = 7 arptab_nb = 149 bcastping = 0 clean_partial_conns = 0 delayack = 0 delayackports = {} dgd_packets_lost = 3 dgd_ping_time = 5 dgd_retry_time = 5 directed_broadcast = 0 extendednetstats = 0 fasttimo = 200 ........ ........ |
ioctl 命令的功能非常強大,您可以使用它們來訪問或修改網絡(或其他)設備的可配置參數。它們使用了各種各樣的數據結構,應該使用正確的數據來填充這些數據結構,以便實現預期的結果。您將發現在使用 AF_INET 和 AF_INET6 系列時存在的區別。本文提供了一小部分常用命令子集的代碼示例。要獲得 AIX Version 5.3 中完整的命令清單,請轉到參考資料部分。我們希望這些關於 ioctl 的信息對您有用。