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 命令描述
SIOCGIFMTUSIOCGIFMTU 可以获取接口的最大传输单元 (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 的信息对您有用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值