UNIX網路程式設計——ioctl 函式的用法詳解
Linux網路程式與核心互動的方法是通過ioctl來實現的,ioctl與網路協議棧進行互動,可得到網路介面的資訊,網絡卡裝置的對映屬性和配置網路介面。並且還能夠檢視,修改,刪除ARP快取記憶體的資訊,所以,我們有必要了解一下ioctl函式的具體實現。
2.相關結構體與相關函式
#include <sys/ioctl.h>
int ioctl(int d,int request,....);
引數:d-檔案描述符,這裡是對網路套接字操作,顯然是套接字描述符。
request-請求碼
省略的部分對應不同的記憶體緩衝區,而具體的記憶體緩衝區是由請求碼request來決定的,下面看一下具體都有哪些相關緩衝區。
(1)網路介面請求結構ifreq
如果想獲得網路介面的相關資訊,就傳入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;//裝置協議設定
(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快取記憶體的操作分別給出了例項。