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個字節的硬件地址,即MAC
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:
#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個字節的硬件地址,即MAC
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;//設備協議設置
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和MAC
err=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]);
}
//獲得網卡映射參數 命令字SIOCGIFMAP
err=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);
//獲取網絡接口IP
struct sockaddr_in *sin=(struct sockaddr_in*)&ifr.ifr_addr;//保存的是二進制IP
char 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]);//轉換成二進制IP
if(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高速緩存的操作分別給出了實例。
沒有留言:
張貼留言