1.介紹
Linux網絡程序與內核交互的方法是通過ioctl來實現的,ioctl與網絡協議棧進行交互,可得到網絡接口的信息,網卡設備的映射屬性和配置網絡接口。並且還能夠查看,修改,刪除ARP高速緩存的信息,所以,我們有必要了解一下ioctl函數的具體實現。
2.相關結構體與相關函數
#include <sys/ioctl.h>int ioctl(int d,int request,....);
參數:
d-文件描述符,這裏是對網絡套接字操作,顯然是套接字描述符。
request-請求碼
省略的部分對應不同的內存緩衝區,而具體的內存緩衝區是由請求碼request來決定的,下面看一下具體都有哪些相關緩衝區。
(1)網絡接口請求結構ifreq
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;//設備協議設置
如果想獲得網絡接口的相關信息,就傳入ifreq結構體。
(2)網卡設備屬性ifmap
struct ifmap{//網卡設備的映射屬性unsigned long mem_start;//開始地址unsigned long mem_end;//結束地址unsigned short base_addr;//基地址unsigned char irq;//中斷號unsigned char dma;//DMAunsigned 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:
#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;//DMAunsigned 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");elseprintf("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);}
運行結果:
(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 -1;}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;}
運行結果:
查看網關的MAC。在查看ARP高速緩存時要傳入IP地址與接口信息。而獲得接口信息要傳入接口名ifr_name,如eth0。
總結:
本文主要介紹了獲得網絡接口請求信息,獲得網卡設備映射屬性、配置網絡接口、獲得ARP高速緩存等。其它ioctl函數還能對操作文件,操作I/O、操作路由等。最後對於網絡接口的操作與ARP高速緩存的操作分別給出了實例。
沒有留言:
張貼留言