ioctl函數

ioctl函數

 

本函數影響由fd參數引用的一個打開的文件。

 

#include<unistd.h>

int ioctl( int fd, int request, .../* void *arg */ );

返回0:成功    -1:出錯

 

第三個參數總是一個指針,但指針的類型依賴於request參數。

 

我們可以把和網絡相關的請求劃分爲6類:

套接口操作

文件操作

接口操作

ARP高速緩存操作

路由表操作

流系統

 

下表列出了網絡相關ioctl請求的request參數以及arg地址必須指向的數據類型:

 

類別

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

 

 

 

 

套接口操作:

明確用於套接口操作的ioctl請求有三個,它們都要求ioctl的第三個參數是指向某個整數的一個指針。

 

SIOCATMARK:    如果本套接口的的度指針當前位於帶外標記,那就通過由第三個參數指向的整數返回一個非0值;否則返回一個0值。POSIX以函數sockatmark替換本請求。

SIOCGPGRP       通過第三個參數指向的整數返回本套接口的進程ID或進程組ID,該ID指定針對本套接口的SIGIOSIGURG信號的接收進程。本請求和fcntlF_GETOWN命令等效,POSIX標準化的是fcntl函數。

SIOCSPGRP     把本套接口的進程ID或者進程組ID設置成第三個參數指向的整數,該ID指定針對本套接口的SIGIOSIGURG信號的接收進程,本請求和fcntlF_SETOWN命令等效,POSIX標準化的是fcntl操作。

 

文件操作:

以下5個請求都要求ioctl的第三個參數指向一個整數。

 

FIONBIO        根據ioctl的第三個參數指向一個0或非0值分別清除或設置本套接口的非阻塞標誌。本請求和O_NONBLOCK文件狀態標誌等效,而該標誌通過fcntlF_SETFL命令清除或設置。

 

FIOASYNC      根據iocl的第三個參數指向一個0值或非0值分別清除或設置針對本套接口的信號驅動異步I/O標誌,它決定是否收取針對本套接口的異步I/O信號(SIGIO)。本請求和O_ASYNC文件狀態標誌等效,而該標誌可以通過fcntlF_SETFL命令清除或設置。

 

FIONREAD     通過由ioctl的第三個參數指向的整數返回當前在本套接口接收緩衝區中的字節數。本特性同樣適用於文件,管道和終端。

 

FIOSETOWN    對於套接口和SIOCSPGRP等效。

FIOGETOWN    對於套接口和SIOCGPGRP等效。

 

接口配置:

得到系統中所有接口由SIOCGIFCONF請求完成,該請求使用ifconf結構,ifconf又使用ifreq

結構,如下所示:

 

Struct ifconf{

    int ifc_len;                 // 緩衝區的大小

    union{

        caddr_t ifcu_buf;        // input from user->kernel

        struct ifreq *ifcu_req;    // return of structures returned

    }ifc_ifcu;

};

 

#define  ifc_buf  ifc_ifcu.ifcu_buf    //buffer address

#define  ifc_req  ifc_ifcu.ifcu_req    //array of structures returned

 

#define  IFNAMSIZ  16

 

struct ifreq{

    char ifr_name[IFNAMSIZ];           // interface name, e.g., “le0”

    union{

        struct sockaddr ifru_addr;

        struct sockaddr ifru_dstaddr;

        struct sockaddr ifru_broadaddr;

        short ifru_flags;

        int ifru_metric;

        caddr_t ifru_data;

    }ifr_ifru;

};

 

#define ifr_addr     ifr_ifru.ifru_addr            // address

#define ifr_dstaddr   ifr_ifru.ifru_dstaddr         // otner end of p-to-p link

#define ifr_broadaddr ifr_ifru.ifru_broadaddr    // broadcast address

#define ifr_flags     ifr_ifru.ifru_flags        // flags

#define ifr_metric    ifr_ifru.ifru_metric      // metric

#define ifr_data      ifr_ifru.ifru_data        // for use by interface

 

再調用ioctl前我們必須先分撇一個緩衝區和一個ifconf結構,然後才初始化後者。如下圖

展示了一個ifconf結構的初始化結構,其中緩衝區的大小爲1024ioctl的第三個參數指向

這樣一個ifconf結構。

ifc_len

 Ifc_buf

1024

--------------------->緩存

 

 

假設內核返回2ifreq結構,ioctl返回時通過同一個ifconf結構緩衝區填入了那2ifreq結構,ifconf結構的ifc_len成員也被更新,以反映存放在緩衝區中的信息量。

接口操作:

SIOCGIFCONF請求爲每個已配置的接口返回其名字以及一個套接口地址結構。我們接着可以發出多個接口類其他請求以設置或獲取每個接口的其他特徵。這些請求的獲取(get)版本(SIOCGxxx)通常由netstat程序發出,設置(set)版本(SIGOCSxxx)通常由ifconfig程序發出。任何用戶都可以獲取接口信息,設置接口信息卻要求有超級用戶權限。

 

這些請求汲取或返回一個一個ifreq結構中的信息,而這個結構的地址則作爲ioctl調用的第三個參數制定。接口總是以其名標誌,在ifreq結構的ifr_name成員中指定,如le0lo0ppp0等。

這些請求中有許多使用套接口地址結構在應用進程和內核之間指定或返回具體接口的IP地址或地址掩碼。對於IPV4,這個地址或掩碼放在一個網際套接口地址結構的sin_addr成員中;對於IPV6,它是一個IPV6套接口地址結構的sin6_addr成員。

 

SIOCGIFADDR  ifr_addr成員中返回單播地址。

SIOCSIFADDR:用ifr_addr成員設置接口地址,這個接口的初始化函數也被調用。

 

SIOCGIFFLAGS:在ifr_flags成員中返回接口標誌。這些接口標誌的名字格式爲IFF_XXX,<net/if.h>頭文件中定義。舉例來說,這些標誌指示接口是否處於UP即在工狀態(IFF_UP),是否爲一個點到點接口(IFF_POINTOPOINT,是否支持廣播(IFF_BROADCAST,等等。

SIOCSIFFLAGS:用ifr_flags成員設置接口標誌。

SIOCGIFDSTADDR:在ifr_dstaddr成員中返回點到點地址。

SIOCSIFDSTADDR:  ifr_dstaddr成員中設置點到點地址

SIOCGIFBRDADDR:  ifr_broadaddr成員中返回廣播地址。應用進程必須首先獲取接口標誌,然後發出正確的請求;對於廣播接口爲SIOCGIFBRDADDR,對於點到點接口爲SIOCGIFDSTADDR

SIOCSIFBRDADDR:用ifr_broadaddr成員設置廣播地址。

SIOCGIFNETMASK:在ifr_addr成員中返回子網掩碼。

SIOCSIFNETMASK:在ifr_addr成員中設置子網掩碼。

SIOCGIFMETRIC:用ifr_metric成員返回接口測度。接口測度由內核爲每個接口維護,不過使用他的是路由守護進程routed。接口測度被routed加到跳數上。

SIOCSIFMETRIC:用ifr_metric成員設置接口的路由測度。

 

 

 

ARP高速緩存操作

ARP告訴緩存也通過ioctl函數操縱。使用路由域套接口的系統往往改用路由套接口訪問

ARP高速緩存。這些請求使用如下的arpreq結構,定義在<net/if_arp.h>

 

struct arpreq {

    struct sockaddr      arp_pa;

    struct sockaddr      arp_ha;

    int                  arp_flags;

};

 

#define ATF_INUSE      0x01   //entry in use

#define ATF_COM         0x02   //completed entry hardware addr valid

#define ATF_PERM       0x04   // permanent entry

#define ATF_PUBL 0x08   // published entry (respond for other host )

 

Ioctl的第三個必須指向某個arpreq結構,操縱ARP高速緩存的ioctl請求有以下三個:

 

SIOCSARP:           把一個新的表項加入ARP告訴緩存中區,或者修改其中已經存在的一個表項,其中arp_pa是一個含有IP地址的網際套接口地址結構,arp_ha則是一個通用套接口地址結構,他的sa_family值爲AF_unspec,sa_data中含有硬件地址(例如6直接的以太網地址)。ATF_PERMATF_PUBL這兩個標誌也可以由應用進程指定。另外兩個標誌(ATF_INUSEATF_COM)則由內核設置。

SIOCDARP        ARP告訴緩存中刪除一個表項。調用者指定要刪除表項的網際地址。

SIOCGARP        ARP高速緩存中獲取一個表項。調用者指定網際地址,相應的硬件地址(例外以太網地址)隨標誌一起返回。

 

只有超級用戶才能增加或刪除表項。這三個請求通常由arp程序發出。

注意ioctl沒有辦法列出ARP高速緩存中的所有表項。當指定-a標誌執行arp命令時,大多

數版本的arp程序通過讀取內核的內存( /dev/kmem )獲得ARP高速緩存的當前內容。

 

路由表操作 

有些系統提供2個用於操縱路由表的ioctl請求。這2個請求要求ioctl的第三個參數是指向

某個rtentry結構的一個指針,該結構定義在<net/route.h>頭文件中。這些請求通常由route

程序發出。只有超級用戶才能發出這些請求。

SIOCADDRT:往路由表中增加一個表項

SIOCDELRT:從路由表中刪除一個表項

Ioctl沒有辦法列出路由表中的所有表項。這個操作通常由netstat程序在指定-r標誌自行四

完成。netstat程序通過讀取內核的內存  /dev/kmem)獲得整個路由表。用sysctl同樣可

以做到。

 

 

如下是chinaunix論壇飛灰橙給出的一個例子。

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <net/if.h>

#include <net/if_arp.h>

#include <sys/ioctl.h>

#include <netinet/in.h>

void err_sys(const char *errmsg);

int main(void)

{               
int i, sockfd;
struct ifreq ifr;
struct arpreq arpr;

strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));

if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
err_sys("socket");      

/* get ip address */
if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1)      
err_sys("1-ioctl");

/* get hardware address */
ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == -1)
err_sys("2-ioctl");

/* output hardware address */
for (i = 0; i < 6; i++) {
unsigned char *mac = (unsigned char *) ifr.ifr_hwaddr.sa_data;
printf("%x", (int) mac[i]);
if (i != 5)
printf("%c", ':');
}

exit(0);
}

void err_sys(const char *errmsg)
{
perror(errmsg);

exit(1);
}

發佈了7 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章