IPv4 和 IPv6 的網路介面操作
使用通訊端 ioctl 命令
層級: 中級
Katiyar Manish (manish.katiyar@in.ibm.com), 軟體工程師, IBM, Intel, Microsoft,HP
Shweta Gupta (shwetagupta@in.ibm.com), 進階軟體工程師, 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
應用程式開發人員可以使用這些命令,並且在 AIX Version 5.3 的文檔中對這些命令進行了詳細的描述。本文說明了有關 Internet Protocol Version 6 (IPv6) 地址和 Internet Protocol Version 4 (IPv4) 堆棧的常用命令的典型用法。
使用 ioctl 通訊端控制選項
通常,網路程式需要瞭解系統中所有有關網路介面和 IP 位址的可用資訊。現在,未來的應用程式可以支援 IPv4 和 IPv6 雙重堆疊。ioctl 結構需要對指標進行遍曆和操作,以處理 IPv4 和 IPv6 地址在長度上的差別(除了使用合適的通訊端結構 sockaddr_in6 或 sockaddr_storage 之外)。
AIX Version 5.3 提供了很多 ioctl 通訊端控制選項,以提取各種有關網路介面的資訊。這些 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 返回的所有介面的配置資訊所需的記憶體。
ioctl(fd, SIOCGSIZIFCONF, (caddr_t)&ifconfsize);
int ifconfsize;
SIOCGIFADDR
SIOCSIFADDR SIOCGIFADDR 擷取介面地址,而 SIOCSIFADDR 設定介面地址。ifr.ifr_addr 欄位返回地址。 ioctl(fd, SIOCGIFADDR, (caddr_t)&ifr, sizeof(struct ifreq));
ioctl(fd, SIOCSIFADDR, (caddr_t)&ifr, sizeof(struct ifreq));
struct ifreq ifr;
SIOCGIFCONF 返回系統中配置的所有介面的配置資訊。 ioctl(fd, SIOCGIFCONF, (caddr_t)&ifc);
struct ifconf ifconf;
SIOCGSIZIFCONF
下面的程式碼片段可以擷取用來填充所有配置介面的資訊所需的緩衝區大小。
清單 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 命令可以用來檢索介面的地址。SIOCGIFCONF 可以用來擷取所有配置介面的資訊,SIOCGIFADDR 可以用來檢索特定介面的地址。
清單 4. 使用 SIOCGIFCONF 擷取配置資訊
/*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 可以設定介面標誌。
ioctl(fd, cmd, (caddr_t)&ifr);
struct ifreq ifr;
SIOCGIFFLAGS
在使用 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)。
ioctl(fd, cmd, (caddr_t)&ifr);
struct ifreq ifr;
這個 MTU 值儲存在 ifr.ifr_mtu 欄位中。
SIOCGNETOPT
SIOCGNETOPT1 SIOCGNETOPT 可以擷取一個網路選項的值。
ioctl(fd, cmd, (caddr_t)&oreq);
struct optreq oreq;
SIOCGNETOPT1 可以擷取當前值、預設值和網路選項的範圍。
ioctl(fd, SIOCGNETOPT1, (caddr_t)&oreq);
struct optreq1 oreq;
SIOCGIFMTU
清單 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
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 的資訊對您有用。
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/weixiuc/archive/2008/09/11/2914865.aspx