第17章——ioctl操作

1.概述

  我们经常用ioctl获取所在主机全部网络的接口信息(包括,接口地址,是否支持广播,是否支持多播等)。

2.ioctl函数

int ioctl(int fd, int request, ... /* void *arg */);

  第三个参数依赖于 request。

  和网络相关的 请求有以下几类:

套接字操作
文件操作
接口操作
ARP高速缓冲操作
路由表操作
流系统操作

  ioctl请求request参数及arg地址必须指向的数据类型,总结如下

2.1 接口配置

  涉及结构

struct ifconf
  {
    int ifc_len;                        /* Size of buffer.  */
    union
      {
        __caddr_t ifcu_buf;
        struct ifreq *ifcu_req;
      } ifc_ifcu;
  };
# define ifc_buf        ifc_ifcu.ifcu_buf       /* Buffer address.  */
# define ifc_req        ifc_ifcu.ifcu_req       /* Array of structures.  */

struct ifreq
  {
# define IFHWADDRLEN    6
# define IFNAMSIZ       IF_NAMESIZE
    union
      {
        char ifrn_name[IFNAMSIZ];       /* Interface name, e.g. "en0".  */
      } ifr_ifrn;

    union
      {
        struct sockaddr ifru_addr;
        struct sockaddr ifru_dstaddr;
        struct sockaddr ifru_broadaddr;
        struct sockaddr ifru_netmask;
        struct sockaddr ifru_hwaddr;
        short int ifru_flags;
        int ifru_ivalue;
        int ifru_mtu;
        struct ifmap ifru_map;
        char ifru_slave[IFNAMSIZ];      /* Just fits the size */
        char ifru_newname[IFNAMSIZ];
        __caddr_t ifru_data;
      } ifr_ifru;
  };
# define ifr_name       ifr_ifrn.ifrn_name      /* interface name       */
# define ifr_hwaddr     ifr_ifru.ifru_hwaddr    /* MAC address          */
# define ifr_addr       ifr_ifru.ifru_addr      /* address              */
# define ifr_dstaddr    ifr_ifru.ifru_dstaddr   /* other end of p-p lnk */
# define ifr_broadaddr  ifr_ifru.ifru_broadaddr /* broadcast address    */
# define ifr_netmask    ifr_ifru.ifru_netmask   /* interface net mask   */
# define ifr_flags      ifr_ifru.ifru_flags     /* flags                */
# define ifr_metric     ifr_ifru.ifru_ivalue    /* metric               */
# define ifr_mtu        ifr_ifru.ifru_mtu       /* mtu                  */
# define ifr_map        ifr_ifru.ifru_map       /* device map           */
# define ifr_slave      ifr_ifru.ifru_slave     /* slave device         */
# define ifr_data       ifr_ifru.ifru_data      /* for use by interface */
# define ifr_ifindex    ifr_ifru.ifru_ivalue    /* interface index      */
# define ifr_bandwidth  ifr_ifru.ifru_ivalue    /* link bandwidth       */
# define ifr_qlen       ifr_ifru.ifru_ivalue    /* queue length         */
# define ifr_newname    ifr_ifru.ifru_newname   /* New name             */

使用示例

/* include get_ifi_info1 */
#include    "unpifi.h"

struct ifi_info *
get_ifi_info(int family, int doaliases)
{
    struct ifi_info        *ifi, *ifihead, **ifipnext;
    int                    sockfd, len, lastlen, flags, myflags, idx = 0, hlen = 0;
    char                *ptr, *buf, lastname[IFNAMSIZ], *cptr, *haddr, *sdlname;
    struct ifconf        ifc;
    struct ifreq        *ifr, ifrcopy;
    struct sockaddr_in    *sinptr;
    struct sockaddr_in6    *sin6ptr;

    sockfd = Socket(AF_INET, SOCK_DGRAM, 0);

    lastlen = 0;
    len = 100 * sizeof(struct ifreq);    /* initial buffer size guess */
    for ( ; ; ) {
        buf = Malloc(len);
        ifc.ifc_len = len;
        ifc.ifc_buf = buf;
        if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {  // 因为ifc->buf空间不够大,ioctl不会报错,只会截断输出并返回正确,所以只有获取两次buf,并当len稳定不变时,才说明获得了所有数据
            if (errno != EINVAL || lastlen != 0)
                err_sys("ioctl error");
        } else {
            if (ifc.ifc_len == lastlen)
                break;        /* success, len has not changed */
            lastlen = ifc.ifc_len;
        }
        len += 10 * sizeof(struct ifreq);    /* increment */
        free(buf);
    }
    ifihead = NULL;
    ifipnext = &ifihead;
    lastname[0] = 0;
    sdlname = NULL;
/* end get_ifi_info1 */

/* include get_ifi_info2 */
    for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
        ifr = (struct ifreq *) ptr;

        switch (ifr->ifr_addr.sa_family) {
#ifdef    IPV6
        case AF_INET6:    
            len = sizeof(struct sockaddr_in6);
            break;
#endif
        case AF_INET:    
        default:    
            len = sizeof(struct sockaddr);
            break;
        }
        ptr += sizeof(ifr->ifr_name) + len;    /* for next one in buffer */


        if (ifr->ifr_addr.sa_family != family)
            continue;    /* ignore if not desired address family */

        myflags = 0;
        printf("ifr_name : %s\n", ifr->ifr_name);
        if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL)
            *cptr = 0;        /* replace colon with null */
        if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
            if (doaliases == 0)
                continue;    /* already processed this interface */
            myflags = IFI_ALIAS;
        }
        memcpy(lastname, ifr->ifr_name, IFNAMSIZ);

        ifrcopy = *ifr;
        Ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
        flags = ifrcopy.ifr_flags;
        if ((flags & IFF_UP) == 0)
            continue;    /* ignore if interface not up */
/* end get_ifi_info2 */

/* include get_ifi_info3 */
        ifi = Calloc(1, sizeof(struct ifi_info));
        *ifipnext = ifi;            /* prev points to this new one */
        ifipnext = &ifi->ifi_next;    /* pointer to next one goes here */

        ifi->ifi_flags = flags;        /* IFF_xxx values */
        ifi->ifi_myflags = myflags;    /* IFI_xxx values */
#if defined(SIOCGIFMTU) && defined(HAVE_STRUCT_IFREQ_IFR_MTU)
        Ioctl(sockfd, SIOCGIFMTU, &ifrcopy);
        ifi->ifi_mtu = ifrcopy.ifr_mtu;
#else
        ifi->ifi_mtu = 0;
#endif
        memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
        ifi->ifi_name[IFI_NAME-1] = '\0';
        /* If the sockaddr_dl is from a different interface, ignore it */
        if (sdlname == NULL || strcmp(sdlname, ifr->ifr_name) != 0)
            idx = hlen = 0;
        ifi->ifi_index = idx;
        ifi->ifi_hlen = hlen;
        if (ifi->ifi_hlen > IFI_HADDR)
            ifi->ifi_hlen = IFI_HADDR;
        if (hlen)
            memcpy(ifi->ifi_haddr, haddr, ifi->ifi_hlen);
/* end get_ifi_info3 */
/* include get_ifi_info4 */
        switch (ifr->ifr_addr.sa_family) {
        case AF_INET:
            sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
            ifi->ifi_addr = Calloc(1, sizeof(struct sockaddr_in));
            memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));

#ifdef    SIOCGIFBRDADDR
            if (flags & IFF_BROADCAST) {
                Ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
                sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
                ifi->ifi_brdaddr = Calloc(1, sizeof(struct sockaddr_in));
                memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
            }
#endif

#ifdef    SIOCGIFDSTADDR
            if (flags & IFF_POINTOPOINT) {
                Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
                sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
                ifi->ifi_dstaddr = Calloc(1, sizeof(struct sockaddr_in));
                memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
            }
#endif
            break;

        case AF_INET6:
            sin6ptr = (struct sockaddr_in6 *) &ifr->ifr_addr;
            ifi->ifi_addr = Calloc(1, sizeof(struct sockaddr_in6));
            memcpy(ifi->ifi_addr, sin6ptr, sizeof(struct sockaddr_in6));

#ifdef    SIOCGIFDSTADDR
            if (flags & IFF_POINTOPOINT) {
                Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
                sin6ptr = (struct sockaddr_in6 *) &ifrcopy.ifr_dstaddr;
                ifi->ifi_dstaddr = Calloc(1, sizeof(struct sockaddr_in6));
                memcpy(ifi->ifi_dstaddr, sin6ptr, sizeof(struct sockaddr_in6));
            }
#endif
            break;

        default:
            break;
        }
    }
    free(buf);
    return(ifihead);    /* pointer to first structure in linked list */
}
/* end get_ifi_info4 */

6. 操作ARP缓存

使用 arpreq 结构

<net/if_arp.h>

/* ARP ioctl request.  */
struct arpreq
  {
    struct sockaddr arp_pa;             /* Protocol address.  */
    struct sockaddr arp_ha;             /* Hardware address.  */
    int arp_flags;                      /* Flags.  */
    struct sockaddr arp_netmask;        /* Netmask (only for proxy arps).  */
    char arp_dev[16];
  };

arp_pa是一个含有IP地址的网际套接字地址结构,arp_ha是一个通用套接字结构,它的sa_family为 AF_UNSPEC,sa_data中含有硬件地址。

ioctl没办法列出ARP缓存所有表项。而arp 程序(指定 -a 时),通过读取内存(/dev/kmem)获得ARP高速缓存的当前内容。

int
main(int argc, char **argv)
{
        int                                     sockfd;
        struct ifi_info                 *ifi;
        unsigned char           *ptr;
        struct arpreq           arpreq;
        struct sockaddr_in      *sin;

        sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
        for (ifi = get_ifi_info(AF_INET, 0); ifi != NULL; ifi = ifi->ifi_next) {
                printf("%s: ", Sock_ntop(ifi->ifi_addr, sizeof(struct sockaddr_in)));

                sin = (struct sockaddr_in *) &arpreq.arp_pa;
                memcpy(sin, ifi->ifi_addr, sizeof(struct sockaddr_in));

                if (ioctl(sockfd, SIOCGARP, &arpreq) < 0) {
                        err_ret("ioctl SIOCGARP");
                        continue;
                }

                ptr = &arpreq.arp_ha.sa_data[0];
                printf("%x:%x:%x:%x:%x:%x\n", *ptr, *(ptr+1),
                           *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5));  // 大端,高位在前
        }
        exit(0);
}

7.路由操作

  ioctl虽然有读写路由表的请求项,但是通常路由操作是通过路由套接字,而非ioctl.

  ioctl没办法获得整个路由表, netstat 程序通过读取内核的内存 (/dev/kmem)获得整个路由表。

猜你喜欢

转载自www.cnblogs.com/yangxinrui/p/12453586.html