• UNP学习笔记(第十七章 ioctl操作)


    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 */
    View Code

    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(">
    ");
    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("
    ");
    45         }
    46         if (ifi->ifi_mtu != 0)
    47             printf("  MTU: %d
    ", ifi->ifi_mtu);
    48 
    49         if ( (sa = ifi->ifi_addr) != NULL)
    50             printf("  IP addr: %s
    ",
    51                         Sock_ntop_host(sa, sizeof(*sa)));
    52         if ( (sa = ifi->ifi_brdaddr) != NULL)
    53             printf("  broadcast addr: %s
    ",
    54                         Sock_ntop_host(sa, sizeof(*sa)));
    55         if ( (sa = ifi->ifi_dstaddr) != NULL)
    56             printf("  destination addr: %s
    ",
    57                         Sock_ntop_host(sa, sizeof(*sa)));
    58     }
    59     free_ifi_info(ifihead);
    60     exit(0);
    61 }
    View Code

    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] = '';
    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 }
    View Code

    运行情况

    接口操作

    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
    ", *ptr, *(ptr+1),
    27                *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));
    28     }
    29     exit(0);
    30 }
    View Code

    先调用get_ifi_info获取本机所有的IP地址,然后在一个循环中遍历每个地址

    使用inet_ntop显示IP地址

    使用ioctl调用返回硬件地址(SIOCGARP)

    路由表操作

    有些系统提供两个用于操纵路由表的ioctl请求,在支持路由域套接字的系统中,这些请求改由路由套接字完成

    SIOCADDRT   往路由表中增加一个表项

    SIOCDELRT    从路由表中删除一个表项

  • 相关阅读:
    面向对象的三大特性之继承和多态(20161025)
    面向对象基础和面向对象的三大特性之一:封装(20161024)
    正则表达式和数组(20161023)
    php基础语法(20161021)
    学生选课数据库SQL语句45道练习题整理及mysql常用函数(20161019)
    CRUD操作(20161017)
    TSQL语句和CRUD(20161016)
    Combine Two Tables
    Second Highest Salary
    Employees Earning More Than Their Managers
  • 原文地址:https://www.cnblogs.com/runnyu/p/4668288.html
Copyright © 2020-2023  润新知