• Linux 网络编程之ioctl函数


    1.介绍

    Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的信息,所以,我们有必要了解一下ioctl函数的具体实现.

    2.相关结构体与相关函数

    #include <sys/ioctl.h>

    int ioctl(int d,int request,....);

    参数:

    fd-文件描述符,这里是对网络套接字操作,显然是套接字描述符

    request-请求码

    省略的部分对应不同的内存缓冲区,而具体的内存缓冲区是由请求码request来决定的,下面看一下具体都有哪些相关缓冲区。

    (1)网络接口请求结构ifreq

    #define IFHWADDRLEN 6 //6个字节的硬件地址,即MAC

    struct ifreq {

     union {

      char ifrn_name[IFNAMESIZ]; //网络接口名称

     } ifr_ifrn;

     union {

      struct sockaddr ifru_addr; //本地IP地址

      struct sockaddr ifru_dstaddr; //目标IP地址

      struct sockaddr ifru_broadaddr; //广播IP地址

      struct sockaddr ifru_netmask; //本地子网掩码地址

      struct sockaddr ifru_hwaddr; //本地MAC地址

      short ifru_flags; //网络接口标记

      int ifru_ivalue; //不同的请求含义不同

      struct ifmap ifru_map; //网卡地址映射

      int ifru_mtu; //最大传输单元

      char ifru_slave[IFNAMSIZ]; //占位符

      char ifru_newname[IFNAMSIZE]; //新名称

      void __user* ifru_data; //用户数据

      struct if_settings ifru_settings; //设备协议设置

     } ifr_ifru;

    };

     

    #define ifr_name ifr_ifrn.ifrn_name;//接口名称

    #define ifr_hwaddr ifr_ifru.ifru_hwaddr;//MAC

    #define ifr_addr ifr_ifru.ifru_addr;//本地IP

    #define ifr_dstaddr ifr_ifru.dstaddr;//目标IP

    #define ifr_broadaddr ifr_ifru.broadaddr;//广播IP

    #define ifr_netmask ifr_ifru.ifru_netmask;//子网掩码

    #define ifr_flags ifr_ifru.ifru_flags;//标志

    #define ifr_metric ifr_ifru.ifru_ivalue;//接口侧度

    #define ifr_mtu ifr_ifru.ifru_mtu;//最大传输单元

    #define ifr_map ifr_ifru.ifru_map;//设备地址映射

    #define ifr_slave ifr_ifru.ifru_slave;//副设备

    #define ifr_data ifr_ifru.ifru_data;//接口使用

    #define ifr_ifrindex ifr_ifru.ifru_ivalue;//网络接口序号

    #define ifr_bandwidth ifr_ifru.ifru_ivalue;//连接带宽

    #define ifr_qlen ifr_ifru.ifru_ivalue;//传输单元长度

    #define ifr_newname ifr_ifru.ifru_newname;//新名称

    #define ifr_seeting ifr_ifru.ifru_settings;//设备协议设置

    如果想获得网络接口的相关信息,就传入ifreq结构体.

     

    (2)网卡设备属性ifmap

    struct ifmap{//网卡设备的映射属性
     unsigned long mem_start;//开始地址
     unsigned long mem_end;//结束地址
     unsigned short base_addr;//基地址
     unsigned char irq;//中断号
     unsigned char dma;//DMA
     unsigned char port;//端口
    }

     

    (3)网络配置接口ifconf

    struct ifconf{//网络配置结构体是一种缓冲区
     int ifc_len;//缓冲区ifr_buf的大小
     union{
      char__user *ifcu_buf;//绘冲区指针
     struct ifreq__user* ifcu_req;//指向ifreq指针
    }ifc_ifcu;

    };
    #define ifc_buf ifc_ifcu.ifcu_buf;//缓冲区地址
    #define ifc_req ifc_ifcu.ifcu_req;//ifc_req地址

     

    (4)ARP高速缓存操作arpreq

    /**
    ARP高速缓存操作,包含IP地址和硬件地址的映射表
    操作ARP高速缓存的命令字 SIOCDARP,SIOCGARP,SIOCSARP分别是删除ARP高速缓存的一条记录,获得ARP高速缓存的一条记录和修改ARP高速缓存的一条记录
    struct arpreq{
      struct sockaddr arp_pa;//协议地址
      struct sockaddr arp_ha;//硬件地址
      int arp_flags;//标记
      struct sockaddr arp_netmask;//协议地址的子网掩码
      char arp_dev[16];//查询网络接口的名称
    }

     

    3. 请求码request

     

    类别

    Request

    说明

    数据类型

    SIOCATMARK

    SIOCSPGRP

    SIOCGPGRP

    是否位于带外标记

    设置套接口的进程ID或进程组ID

    获取套接口的进程ID或进程组ID

    int

    int

    int

     

     

     

     

    FIONBIN

    FIOASYNC

    FIONREAD

    FIOSETOWN

    FIOGETOWN

     

    设置/清除非阻塞I/O标志

    设置/清除信号驱动异步I/O标志

    获取接收缓存区中的字节数

    设置文件的进程ID或进程组ID

    获取文件的进程ID或进程组ID

    int

    int

    int

    int

    int

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    SIOCGIFCONF

    SIOCSIFADDR

    SIOCGIFADDR

    SIOCSIFFLAGS

    SIOCGIFFLAGS

    SIOCSIFDSTADDR

    SIOCGIFDSTADDR

    SIOCGIFBRDADDR

    SIOCSIFBRDADDR

    SIOCGIFNETMASK

    SIOCSIFNETMASK

    SIOCGIFMETRIC

    SIOCSIFMETRIC

    SIOCGIFMTU

    SIOCxxx

    获取所有接口的清单

    设置接口地址

    获取接口地址

    设置接口标志

    获取接口标志

    设置点到点地址

    获取点到点地址

    获取广播地址

    设置广播地址

    获取子网掩码

    设置子网掩码

    获取接口的测度

    设置接口的测度

    获取接口MTU

    (还有很多取决于系统的实现)

    struct ifconf

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

    struct ifreq

     

    ARP

    SIOCSARP

    SIOCGARP

    SIOCDARP

    创建/修改ARP表项

    获取ARP表项

    删除ARP表项

    struct arpreq

    struct arpreq

    struct arpreq

    SIOCADDRT

    SIOCDELRT

    增加路径

    删除路径

    struct rtentry

    struct rtentry

    I_xxx

     

     

     

    4. 相关例子

    (1)网络接口信息
    选项获取填充struct ifreq的ifr_name

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <sys/types.h>
      5 #include <sys/socket.h>
      6 #include <netinet/in.h>
      7 #include <linux/if.h>
      8 #include <arpa/inet.h>
      9 #include <linux/sockios.h>
     10 /**
     11  ioctl函数是与内核交互的一种方法,使用ioctl函数与内核协议栈进行交互
     12  ioctl函数可操作I/O请求,文件请求与网络接口请求
     13  网络接口请求的几个结构体:
     14  struct ifreq{
     15  #define IFHWADDRLEN 6 //6个字节的硬件地址,即MAC
     16  union{
     17  char ifrn_name[IFNAMESIZ];//网络接口名称
     18  }ifr_ifrn;
     19  union{
     20  struct sockaddr ifru_addr;//本地IP地址
     21  struct sockaddr ifru_dstaddr;//目标IP地址
     22  struct sockaddr ifru_broadaddr;//广播IP地址
     23  struct sockaddr ifru_netmask;//本地子网掩码地址
     24  struct sockaddr ifru_hwaddr;//本地MAC地址
     25  short ifru_flags;//网络接口标记
     26  int ifru_ivalue;//不同的请求含义不同
     27  struct ifmap ifru_map;//网卡地址映射
     28  int ifru_mtu;//最大传输单元 
     29  char ifru_slave[IFNAMSIZ];//占位符
     30  char ifru_newname[IFNAMSIZE];//新名称
     31  void __user* ifru_data;//用户数据
     32  struct if_settings ifru_settings;//设备协议设置
     33 
     34  }ifr_ifru;
     35  }
     36  #define ifr_name ifr_ifrn.ifrn_name;//接口名称
     37  #define ifr_hwaddr ifr_ifru.ifru_hwaddr;//MAC
     38  #define ifr_addr ifr_ifru.ifru_addr;//本地IP
     39  #define ifr_dstaddr ifr_ifru.dstaddr;//目标IP
     40  #define ifr_broadaddr ifr_ifru.broadaddr;//广播IP
     41  #define ifr_netmask ifr_ifru.ifru_netmask;//子网掩码
     42  #define ifr_flags ifr_ifru.ifru_flags;//标志
     43  #define ifr_metric  ifr_ifru.ifru_ivalue;//接口侧度
     44  #define ifr_mtu ifr_ifru.ifru_mtu;//最大传输单元
     45  #define ifr_map ifr_ifru.ifru_map;//设备地址映射
     46  #define ifr_slave ifr_ifru.ifru_slave;//副设备
     47  #define ifr_data ifr_ifru.ifru_data;//接口使用
     48  #define ifr_ifrindex ifr_ifru.ifru_ivalue;//网络接口序号
     49  #define ifr_bandwidth ifr_ifru.ifru_ivalue;//连接带宽
     50  #define ifr_qlen ifr_ifru.ifru_ivalue;//传输单元长度
     51  #define ifr_newname ifr_ifru.ifru_newname;//新名称 
     52  #define ifr_seeting ifr_ifru.ifru_settings;//设备协议设置
     53 
     54  struct ifmap{//网卡设备的映射属性
     55  unsigned long mem_start;//开始地址
     56  unsigned long mem_end;//结束地址
     57  unsigned short base_addr;//基地址
     58  unsigned char irq;//中断号
     59  unsigned char dma;//DMA
     60  unsigned char port;//端口
     61  }
     62 
     63  struct ifconf{//网络配置结构体是一种缓冲区
     64  int ifc_len;//缓冲区ifr_buf的大小
     65  union{
     66  char__user *ifcu_buf;//绘冲区指针
     67  struct ifreq__user* ifcu_req;//指向ifreq指针
     68  }ifc_ifcu;
     69 
     70  };
     71  #define ifc_buf ifc_ifcu.ifcu_buf;//缓冲区地址
     72  #define ifc_req ifc_ifcu.ifcu_req;//ifc_req地址
     73 
     74  (1)获得配置选项SIOCGIFCONF获得网络接口的配置情况 需要填充struct ifreq中ifr_name变量
     75  (2)其它选项获取填充struct ifreq的ifr_name
     76  **/
     77 
     78 int
     79 main(int argc, char*argv[])
     80 {
     81   int s;
     82   int err;
     83   s = socket(AF_INET, SOCK_DGRAM, 0);
     84   if (s < 0)
     85     {
     86       perror("socket error");
     87       return;
     88     }
     89 
     90 //传入网络接口序号,获得网络接口的名称
     91   struct ifreq ifr;
     92 
     93   ifr.ifr_ifindex = 2; //获得第2个网络接口的名称 
     94   err = ioctl(s, SIOCGIFNAME, &ifr);
     95 
     96   if (err)
     97     {
     98       perror("index error");
     99     }
    100   else
    101     {
    102 
    103       printf("the %dst interface is:%s
    ", ifr.ifr_ifindex, ifr.ifr_name);
    104 
    105     }
    106 //传入网络接口名称,获得标志
    107   memcpy(ifr.ifr_name, "eth0", 5);
    108   err = ioctl(s, SIOCGIFFLAGS, &ifr);
    109   if (!err)
    110     {
    111       printf("SIOCGIFFLAGS:%d
    ", ifr.ifr_flags);
    112     }
    113 //获得MTU和MAC
    114   err = ioctl(s, SIOCGIFMTU, &ifr);
    115   if (!err)
    116     {
    117       printf("SIOCGIFMTU:%d
    ", ifr.ifr_mtu);
    118     }
    119 //获得MAC地址
    120   err = ioctl(s, SIOCGIFHWADDR, &ifr);
    121   if (!err)
    122     {
    123       unsigned char* hw = ifr.ifr_hwaddr.sa_data;
    124       printf("SIOCGIFHWADDR:%02x:%02x:%02x:%02x:%02x:%02x
    ", hw[0], hw[1],
    125           hw[2], hw[3], hw[4], hw[5]);
    126     }
    127 //获得网卡映射参数 命令字SIOCGIFMAP
    128   err = ioctl(s, SIOCGIFMAP, &ifr);
    129   if (!err)
    130     {
    131       printf(
    132           "SIOCGIFMAP,mem_start:%d,mem_end:%d,base_addr:%d,ifr_map:%d,dma:%d,port:%d
    ",
    133           ifr.ifr_map.mem_start, ifr.ifr_map.mem_end, ifr.ifr_map.base_addr,
    134           ifr.ifr_map.irq, ifr.ifr_map.dma, ifr.ifr_map.port);
    135     }
    136 
    137 //获得网卡序号
    138   err = ioctl(s, SIOCGIFINDEX, &ifr);
    139   if (!err)
    140     {
    141       printf("SIOCGIFINDEX:%d
    ", ifr.ifr_ifindex);
    142     }
    143 //获取发送队列的长度
    144   err = ioctl(s, SIOCGIFTXQLEN, &ifr);
    145   if (!err)
    146     {
    147       printf("SIOCGIFTXQLEN:%d
    ", ifr.ifr_qlen);
    148 
    149     }
    150 //获取网络接口IP
    151 
    152   struct sockaddr_in *sin = (struct sockaddr_in*) &ifr.ifr_addr; //保存的是二进制IP
    153   char ip[16]; //字符数组,存放字符串
    154   memset(ip, 0, 16);
    155   err = ioctl(s, SIOCGIFADDR, &ifr);
    156   if (!err)
    157     {
    158       inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, 16); //转换的字符串保存到ip数组中,第二个参数是要转换的二进制IP指针,第三个参数是转换完成存放IP的缓冲区,最后一个参数是缓冲区的长度
    159       printf("SIOCGIFADDR:%s
    ", ip);
    160 
    161     }
    162 
    163 //查询目标IP地址
    164   err = ioctl(s, SIOCGIFDSTADDR, &ifr);
    165   if (!err)
    166     {
    167       inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, 16);
    168       printf("SIOCGIFDSTADDR:%s
    ", ip);
    169     }
    170 
    171 //查询子网掩码
    172   err = ioctl(s, SIOCGIFNETMASK, &ifr);
    173   if (!err)
    174     {
    175       inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, 16);
    176       printf("SIOCGIFNETMASK:%s
    ", ip);
    177     }
    178 
    179 //设置IP地址,设置网络接口
    180   inet_pton(AF_INET, "222.27.253.108", &sin->sin_addr.s_addr); //将字符串IP转换成二进制
    181   err = ioctl(s, SIOCSIFADDR, &ifr); //发送设置本机ip地址请求命令
    182   if (!err)
    183     {
    184       printf("check IP-----");
    185       memset(&ifr, 0, sizeof(ifr));
    186       memcpy(ifr.ifr_name, "eth0", 5);
    187       ioctl(s, SIOCGIFADDR, &ifr);
    188       inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, 16);
    189       printf("%s
    ", ip);
    190     }
    191 //得到接口的广播地址
    192   memset(&ifr, 0, sizeof(ifr));
    193   memcpy(ifr.ifr_name, "eth0", 5);
    194   ioctl(s, SIOCGIFBRDADDR, &ifr);
    195   struct sockaddr_in *broadcast = (struct sockaddr_in*) &ifr.ifr_broadaddr;
    196 //转换成字符串
    197   inet_ntop(AF_INET, &broadcast->sin_addr.s_addr, ip, 16); //inet_ntop将二进制IP转换成点分十进制的字符串
    198   printf("BROADCAST IP:%s
    ", ip);
    199   close(s);
    200 
    201 }
    View Code

    运行结果:

    [root@localhost ~]# ./ioctl-test
    the 2st interface is:eth0
    SIOCGIFFLAGS:4163
    SIOCGIFMTU:1500
    SIOCGIFHWADDR:00:13:d4:36:98:34
    SIOCGIFMAP,mem_start:0,mem_end:0,base_addr:60416,ifr_map:201,dma:0,port:0
    SIOCGIFINDEX:2
    SIOCGIFTXQLEN:1000
    SIOCGIFADDR:222.27.253.108
    SIOCGIFDSTADDR:222.27.253.108
    SIOCGIFNETMASK:255.255.255.0
    check IP-----222.27.253.108
    BROADCAST IP:222.27.253.255

    (2)查看arp高速缓存信息

     1 #include <stdio.h>
     2 #include <sys/types.h>
     3 #include <sys/socket.h>
     4 #include <netinet/in.h>
     5 #include <arpa/inet.h>
     6 #include <net/if_arp.h>
     7 #include <string.h>
     8 #include <stdlib.h>
     9 #include <linux/sockios.h>
    10 /**
    11  ARP高速缓存操作,包含IP地址和硬件地址的映射表
    12  操作ARP高速缓存的命令字 SIOCDARP,SIOCGARP,SIOCSARP分别是删除ARP高速缓存的一条记录,获得ARP高速缓存的一条记录和修改ARP高速缓存的一条记录
    13  
    14 struct arpreq
    15 {
    16     struct sockaddr arp_pa; //协议地址
    17     struct sockaddr arp_ha;//硬件地址
    18     int arp_flags;//标记
    19     struct sockaddr arp_netmask;//协议地址的子网掩码
    20     char arp_dev[16];//查询网络接口的名称
    21 }
    22 
    23 **/
    24 //根据IP地址查找硬件地址
    25 int main(int argc,char*argv[])
    26 {
    27     int s;
    28     int err;
    29     struct arpreq arpreq;
    30     struct sockaddr_in *addr=(struct sockaddr_in*)&arpreq.arp_pa; //IP地址
    31     s=socket(AF_INET,SOCK_DGRAM,0);
    32     if(s<0)
    33       {
    34         perror("socket error");
    35       }
    36 
    37     addr->sin_family=AF_INET;
    38     addr->sin_addr.s_addr=inet_addr(argv[1]); //转换成二进制IP
    39     if(addr->sin_addr.s_addr==INADDR_NONE)
    40       {
    41         printf("IP地址格式错误
    ");
    42       }
    43     strcpy(arpreq.arp_dev,"eth0");
    44     err=ioctl(s,SIOCGARP,&arpreq);
    45     if(err==-1)
    46       {
    47         perror("arp");
    48         return;
    49       }
    50 
    51     unsigned char* hw=(unsigned char*)&arpreq.arp_ha.sa_data; //硬件地址
    52     printf("%s
    ",argv[1]);
    53     printf("%02x:%02x:%02x:%02x:%02x:%02x
    ",hw[0],hw[1],hw[2],hw[3],hw[4],hw[5]);
    54     close(s);
    55     return 0;
    56 
    57 }
    View Code

    运行结果:

    [root@localhost ~]# ./ioctl-arp 222.27.253.1
    222.27.253.1
    00:0f:e2:5f:3c:8c

    查看网关的MAC.在查看ARP高速缓存时要传入IP地址与接口信息.而获得接口信息要传入接口名ifr_name,如eth0.

     

    总结:

    本文主要介绍了获得网络接口请求信息,获得网卡设备映射属性,配置网络接口,获得ARP高速缓存等.其它ioctl函数还能对操作文件,操作I/O,操作路由等。最后,对于网络接口的操作与ARP高速缓存的操作分别给出了实例.

    转自 :http://blog.csdn.net/chenjin_zhong/article/details/7269558

  • 相关阅读:
    类库探源——开头
    码的生产——(二维码、条形码)
    java 接口
    Eclipse下修改工程名
    Oracle 查询库中所有表名、字段名、字段名说明,查询表的数据条数、表名、中文表名、
    oracle中SQL根据生日日期查询年龄的方法
    TRUNCATE 删除表,无法回退。默认选择为整个表的内容,所以不能加条件。
    【kettle】window安装与配置
    SQL SELECT语句
    Oracle 查询类似 select top 的用法
  • 原文地址:https://www.cnblogs.com/minuse/p/3592567.html
Copyright © 2020-2023  润新知