您的位置:首页 > 理论基础 > 计算机网络

Linux 网络编程之ioctl函数

2014-10-09 09:29 423 查看
1.介绍Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的信息,所以,我们有必要了解一下ioctl函数的具体实现.2.相关结构体与相关函数#include <sys/ioctl.h>int ioctl(int d,int request,....);参数:d-文件描述符,这里是对网络套接字操作,显然是套接字描述符request-请求码省略的部分对应不同的内存缓冲区,而具体的内存缓冲区是由请求码request来决定的,下面看一下具体都有哪些相关缓冲区。(1)网络接口请求结构ifreqstruct ifreq{#define IFHWADDRLEN 6 //6个字节的硬件地址,即MACunion{ 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)网卡设备属性ifmapstruct ifmap{//网卡设备的映射属性 unsigned long mem_start;//开始地址 unsigned long mem_end;//结束地址 unsigned short base_addr;//基地址 unsigned char irq;//中断号 unsigned char dma;//DMA unsigned char port;//端口}(3)网络配置接口ifconfstruct 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说明数据类型
SIOCATMARKSIOCSPGRPSIOCGPGRP是否位于带外标记设置套接口的进程ID或进程组ID获取套接口的进程ID或进程组IDintintint
    FIONBINFIOASYNCFIONREADFIOSETOWNFIOGETOWN 设置/清除非阻塞I/O标志设置/清除信号驱动异步I/O标志获取接收缓存区中的字节数设置文件的进程ID或进程组ID获取文件的进程ID或进程组IDintintintintint
              SIOCGIFCONFSIOCSIFADDRSIOCGIFADDRSIOCSIFFLAGSSIOCGIFFLAGSSIOCSIFDSTADDRSIOCGIFDSTADDRSIOCGIFBRDADDRSIOCSIFBRDADDRSIOCGIFNETMASKSIOCSIFNETMASKSIOCGIFMETRICSIOCSIFMETRICSIOCGIFMTUSIOCxxx获取所有接口的清单设置接口地址获取接口地址设置接口标志获取接口标志设置点到点地址获取点到点地址获取广播地址设置广播地址获取子网掩码设置子网掩码获取接口的测度设置接口的测度获取接口MTU(还有很多取决于系统的实现)struct ifconfstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreq
 ARPSIOCSARPSIOCGARPSIOCDARP创建/修改ARP表项获取ARP表项删除ARP表项struct arpreqstruct arpreqstruct arpreq
SIOCADDRTSIOCDELRT增加路径删除路径struct rtentrystruct rtentry
I_xxx  
4. 相关例子(1)网络接口信息选项获取填充struct ifreq的ifr_name#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <linux/if.h>#include <arpa/inet.h>#include <linux/sockios.h>/**ioctl函数是与内核交互的一种方法,使用ioctl函数与内核协议栈进行交互ioctl函数可操作I/O请求,文件请求与网络接口请求网络接口请求的几个结构体:struct ifreq{#define IFHWADDRLEN 6 //6个字节的硬件地址,即MACunion{ 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;//设备协议设置struct ifmap{//网卡设备的映射属性 unsigned long mem_start;//开始地址 unsigned long mem_end;//结束地址 unsigned short base_addr;//基地址 unsigned char irq;//中断号 unsigned char dma;//DMA unsigned char port;//端口}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地址(1)获得配置选项SIOCGIFCONF获得网络接口的配置情况 需要填充struct ifreq中ifr_name变量(2)其它选项获取填充struct ifreq的ifr_name**/int main(int argc,char*argv[]){int s;int err;s=socket(AF_INET,SOCK_DGRAM,0);if(s<0){  perror("socket error");  return;}//传入网络接口序号,获得网络接口的名称struct ifreq ifr;ifr.ifr_ifindex=2;//获得第2个网络接口的名称err=ioctl(s,SIOCGIFNAME,&ifr);if(err){  perror("index error");}else{  printf("the %dst interface is:%s\n",ifr.ifr_ifindex,ifr.ifr_name);}//传入网络接口名称,获得标志memcpy(ifr.ifr_name,"eth0",5);err=ioctl(s,SIOCGIFFLAGS,&ifr);if(!err){  printf("SIOCGIFFLAGS:%d\n",ifr.ifr_flags);}//获得MTU和MACerr=ioctl(s,SIOCGIFMTU,&ifr);if(!err){  printf("SIOCGIFMTU:%d\n",ifr.ifr_mtu);}//获得MAC地址err=ioctl(s,SIOCGIFHWADDR,&ifr);if(!err){  unsigned char* hw=ifr.ifr_hwaddr.sa_data;  printf("SIOCGIFHWADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",hw[0],hw[1],hw[2],hw[3],hw[4],hw[5]);}//获得网卡映射参数 命令字SIOCGIFMAPerr=ioctl(s,SIOCGIFMAP,&ifr);if(!err){  printf("SIOCGIFMAP,mem_start:%d,mem_end:%d,base_addr:%d,ifr_map:%d,dma:%d,port:%d\n",ifr.ifr_map.mem_start,ifr.ifr_map.mem_end,ifr.ifr_map.base_addr,ifr.ifr_map.irq,ifr.ifr_map.dma,ifr.ifr_map.port);}//获得网卡序号err=ioctl(s,SIOCGIFINDEX,&ifr);if(!err){  printf("SIOCGIFINDEX:%d\n",ifr.ifr_ifindex);}//获取发送队列的长度err=ioctl(s,SIOCGIFTXQLEN,&ifr);if(!err){  printf("SIOCGIFTXQLEN:%d\n",ifr.ifr_qlen);}//获取网络接口IPstruct sockaddr_in *sin=(struct sockaddr_in*)&ifr.ifr_addr;//保存的是二进制IPchar ip[16];//字符数组,存放字符串memset(ip,0,16);err=ioctl(s,SIOCGIFADDR,&ifr);if(!err){ inet_ntop(AF_INET,&sin->sin_addr.s_addr,ip,16);//转换的字符串保存到ip数组中,第二个参数是要转换的二进制IP指针,第三个参数是转换完成存放IP的缓冲区,最后一个参数是缓冲区的长度printf("SIOCGIFADDR:%s\n",ip);}//查询目标IP地址err=ioctl(s,SIOCGIFDSTADDR,&ifr);if(!err){   inet_ntop(AF_INET,&sin->sin_addr.s_addr,ip,16);  printf("SIOCGIFDSTADDR:%s\n",ip);}//查询子网掩码err=ioctl(s,SIOCGIFNETMASK,&ifr);if(!err){  inet_ntop(AF_INET,&sin->sin_addr.s_addr,ip,16);  printf("SIOCGIFNETMASK:%s\n",ip);}//设置IP地址,设置网络接口inet_pton(AF_INET,"222.27.253.108",&sin->sin_addr.s_addr);//将字符串IP转换成二进制err=ioctl(s,SIOCSIFADDR,&ifr);//发送设置本机ip地址请求命令if(!err){  printf("check IP-----");    memset(&ifr,0,sizeof(ifr));  memcpy(ifr.ifr_name,"eth0",5);  ioctl(s,SIOCGIFADDR,&ifr);  inet_ntop(AF_INET,&sin->sin_addr.s_addr,ip,16);  printf("%s\n",ip);}//得到接口的广播地址memset(&ifr,0,sizeof(ifr));memcpy(ifr.ifr_name,"eth0",5);ioctl(s,SIOCGIFBRDADDR,&ifr);struct sockaddr_in *broadcast=(struct sockaddr_in*)&ifr.ifr_broadaddr;//转换成字符串inet_ntop(AF_INET,&broadcast->sin_addr.s_addr,ip,16);//inet_ntop将二进制IP转换成点分十进制的字符串printf("BROADCAST IP:%s\n",ip);close(s);}运行结果:[root@localhost ~]# ./ioctl-testthe 2st interface is:eth0SIOCGIFFLAGS:4163SIOCGIFMTU:1500SIOCGIFHWADDR:00:13:d4:36:98:34SIOCGIFMAP,mem_start:0,mem_end:0,base_addr:60416,ifr_map:201,dma:0,port:0SIOCGIFINDEX:2SIOCGIFTXQLEN:1000SIOCGIFADDR:222.27.253.108SIOCGIFDSTADDR:222.27.253.108SIOCGIFNETMASK:255.255.255.0check IP-----222.27.253.108BROADCAST IP:222.27.253.255(2)查看arp高速缓存信息#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <net/if_arp.h>#include <string.h>#include <stdlib.h>#include <linux/sockios.h>/**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];//查询网络接口的名称}**///根据IP地址查找硬件地址int main(int argc,char*argv[]){ int s; int err; struct arpreq arpreq; struct sockaddr_in *addr=(struct sockaddr_in*)&arpreq.arp_pa;//IP地址 s=socket(AF_INET,SOCK_DGRAM,0); if(s<0){   perror("socket error");}addr->sin_family=AF_INET;addr->sin_addr.s_addr=inet_addr(argv[1]);//转换成二进制IPif(addr->sin_addr.s_addr==INADDR_NONE){  printf("IP地址格式错误\n");}strcpy(arpreq.arp_dev,"eth0");err=ioctl(s,SIOCGARP,&arpreq);if(err==-1){  perror("arp");  return;}  unsigned char* hw=(unsigned char*)&arpreq.arp_ha.sa_data;//硬件地址 printf("%s\n",argv[1]); printf("%02x:%02x:%02x:%02x:%02x:%02x\n",hw[0],hw[1],hw[2],hw[3],hw[4],hw[5]);close(s);return 0;}运行结果:[root@localhost ~]# ./ioctl-arp 222.27.253.1222.27.253.100:0f:e2:5f:3c:8c查看网关的MAC.在查看ARP高速缓存时要传入IP地址与接口信息.而获得接口信息要传入接口名ifr_name,如eth0.总结:本文主要介绍了获得网络接口请求信息,获得网卡设备映射属性,配置网络接口,获得ARP高速缓存等.其它ioctl函数还能对操作文件,操作I/O,操作路由等。最后,对于网络接口的操作与ARP高速缓存的操作分别给出了实例.

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: