ioctl 用法(转)

来源:互联网 发布:淘宝c2c模式分析 编辑:程序博客网 时间:2024/05/19 01:31
 文件描述符是低层的输入和输出接口。描述符可以表示到设备、管道或套接字的连接,这些连接用于与另一个进程或普通文件进行通信。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)

  

  

  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   

  #define ifr_dstaddr   ifr_ifru.ifru_dstaddr 

  #define ifr_broadaddr  ifr_ifru.ifru_broadaddr

  #define ifr_flags    ifr_ifru.ifru_flags  

  #define ifr_metric   ifr_ifru.ifru_metric  

  #define ifr_data    ifr_ifru.ifru_data   

  #define ifr_site6    ifr_ifru.ifru_site6  

  #define ifr_mtu     ifr_ifru.ifru_mtu   

  #define ifr_isno    ifr_ifru.ifru_data   

  #define ifr_baudrate  ifr_ifru.ifru_baudrate 

  };  

  清单 2. struct ifconf (/usr/include/net/if.h)  

  

  struct ifconf {

  int   ifc_len;        

  union {

  caddr_t ifcu_buf;

  struct ifreq *ifcu_req;

  } ifc_ifcu;

  Following macros are provided for convenience

  #define ifc_buf ifc_ifcu.ifcu_buf   

  #define ifc_req ifc_ifcu.ifcu_req   

  };

  

  代码示例

  下面的表 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 获得所需的缓冲区大小

  

  

  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",get_interface_size(sd));

  }

  

  上面代码的输出是:

  

  $> ./myprog

  Size of memory needed = 628

  

  SIOCGIFCONF 和 SIOCGIFADDR

  SIOCGIFCONF 和 SIOCGIFADDR 命令可以用来检索接口的地址。SIOCGIFCONF 可以用来获取所有配置接口的信息,SIOCGIFADDR 可以用来检索特定接口的地址。

  

  清单 4. 使用 SIOCGIFCONF 获取配置信息

  

  

  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))

  

  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(;cpifr_name) + SIZE(ifr->ifr_addr))){

  

  ifr=(struct ifreq *)cp;

  printf("%s :",ifr->ifr_name);

  getip(ifr,addr);

  printf("%s",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 获取接口地址

  

  

  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",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

  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("");

  }

  

  上面代码的输出是:

  

  $> ./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

  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",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

  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",ifr->ifr_mtu);

  

  SIOCGNETOPT1

  SIOCGNETOPT1 给出当前值、缺省值和网络选项的范围。

  

  清单 10. 从 optreq1 结构获取网络选项

  

  

  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",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 的信息对您有用。

转自:http://hi.baidu.com/leezongfanblog/blog/item/a86a564583934a88b3b7dc0e.html

最后补上一个用ioctl获取本机ip地址的例子

#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <net/if.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>

 

int main()

{
    int i=0;
    int sockfd;
  struct ifconf ifconf;
  unsigned char buf[512];
  struct ifreq *ifreq;
  //初始化ifconf
  ifconf.ifc_len = 512;
  ifconf.ifc_buf = buf;
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
    {
        perror("socket");
        exit(1);
   
  ioctl(sockfd, SIOCGIFCONF, &ifconf);    //获取所有接口信息
  //接下来一个一个的获取IP地址
  ifreq = (struct ifreq*)buf; 
  for(i=(ifconf.ifc_len/sizeof(struct ifreq)); i>0; i--)
  {
//      if(ifreq->ifr_flags == AF_INET){            //for ipv4
      printf("name = [%s]\n", ifreq->ifr_name);
      printf("local addr = [%s]\n", inet_ntoa((((struct sockaddr_in*)&(ifreq->ifr_addr))->sin_addr).s_addr));
      ifreq++;

//  }

  }
    return 0;
}