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 指定針對本套接口的SIGIO 或SIGURG 信號的接收進程。本請求和fcntl 的F_GETOWN 命令等效,POSIX 標准化的是fcntl 函數。
SIOCSPGRP : 把本套接口的進程ID 或者進程組ID 設置成第三個參數指向的整數,該ID 指定針對本套接口的SIGIO 或SIGURG 信號的接收進程,本請求和fcntl 的F_SETOWN 命令等效,POSIX 標准化的是fcntl 操作。
文件操作:
以下5 個請求都要求ioctl 的第三個參數指向一個整數。
FIONBIO : 根據ioctl 的第三個參數指向一個0 或非0 值分別清除或設置本套接口的非阻塞標志。本請求和O_NONBLOCK 文件狀態標志等效,而該標志通過fcntl 的F_SETFL 命令清除或設置。
FIOASYNC : 根據iocl 的第三個參數指向一個0 值或非0 值分別清除或設置針對本套接口的信號驅動異步I/O 標志,它決定是否收取針對本套接口的異步I/O 信號(SIGIO )。本請求和O_ASYNC 文件狀態標志等效,而該標志可以通過fcntl 的F_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 結構的初始化結構,其中緩沖區的大小為1024 ,ioctl 的第三個參數指向
這樣一個ifconf 結構。
ifc_len |
Ifc_buf |
1024
---------------------> 緩存
假設內核返回2 個ifreq 結構,ioctl 返回時通過同一個ifconf 結構緩沖區填入了那2 個ifreq 結構,ifconf 結構的ifc_len 成員也被更新,以反映存放在緩沖區中的信息量
• 改變路由表 (例如 SIOCADDRT, SIOCDELRT),
• 讀/更新 ARP/RARP 緩存(如:SIOCDARP, SIOCSRARP),
• 一般的與網絡接口有關的(例如 SIOCGIFNAME, SIOCSIFADDR 等等)
在 Gooodies目錄下有很多樣例程序展示了如何使用ioctl。當你看這些程序時,注意參數argstruct是與參數command相關的。例如,與 路由表相關的ioctl使用rtentry這種結構,rtentry定義在/usr/include/linux/route.h(參見例子 adddefault.c)。與ARP有關的ioctl調用使用arpreq結構,arpreq定義在/usr/include/linux /if_arp.h(參見例子arpread.c)
實例:
程序1:檢測接口的 .net_addr,netmask,broad_addr
程序2:檢查接口的物理連接是否正常
程序3:更簡單一點測試物理連接
程序4:調節音量
***************************程序1****************************************
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
static void usage(){
printf("usage : ipconfig interface \n");
exit(0);
}
int main(int argc,char **argv)
{
struct sockaddr_in *addr;
struct ifreq ifr;
char *name,*address;
int sockfd;
if(argc != 2)
usage();
else
name = argv[1]; 內容來自ltesting.net
sockfd = socket(AF_INET,SOCK_DGRAM,0);
strncpy(ifr.ifr_name,name,IFNAMSIZ-1);
if(ioctl(sockfd,SIOCGIFADDR,&ifr) == -1)
perror("ioctl error"),exit(1);
addr = (struct sockaddr_in *)&(ifr.ifr_addr);
address = inet_ntoa(addr->sin_addr);
printf("inet addr: %s ",address);
if(ioctl(sockfd,SIOCGIFBRDADDR,&ifr) == -1)
perror("ioctl error"),exit(1);
addr = (struct sockaddr_in *)&ifr.ifr_broadaddr;
address = inet_ntoa(addr->sin_addr);
printf("broad addr: %s ",address);
if(ioctl(sockfd,SIOCGIFNETMASK,&ifr) == -1)
perror("ioctl error"),exit(1);
addr = (struct sockaddr_in *)&ifr.ifr_addr;
address = inet_ntoa(addr->sin_addr);
printf("inet mask: %s ",address);
printf("\n");
exit(0);
}
******************************** 程序2*****************************************************
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdlib.h>
#include <unistd.h>
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned char u8;
#include <linux/ethtool.h>
#include <linux/sockios.h>
int detect_mii(int skfd, char *ifname)
{
struct ifreq ifr;
u16 *data, mii_val;
unsigned phy_id;
/* Get the vitals from the interface. */
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(skfd, SIOCG MIIPHY, &ifr) < 0)
{
fprintf(stderr, "SIOCGMIIPHY on %s failed: %s\n", ifname,
strerror(errno));
(void) close(skfd);
return 2;
}
data = (u16 *)(&ifr.ifr_data);
phy_id = data[0];
data[1] = 1;
if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0)
{
fprintf(stderr, "SIOCGMIIREG on %s failed: %s\n", ifr.ifr_name, 領測軟件測試網http://www.ltesting.net
strerror(errno));
return 2;
}
mii_val = data[3];
return(((mii_val & 0x0016) == 0x0004) ? 0 : 1);
}
int detect_ethtool(int skfd, char *ifname)
{
struct ifreq ifr;
struct ethtool_value edata;
memset(&ifr, 0, sizeof(ifr));
edata.cmd = ETHTOOL_GLINK;
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1);
ifr.ifr_data = (char *) &edata;
if (ioctl(skfd, SIOCETHTOOL, &ifr) == -1)
{
printf("ETHTOOL_GLINK failed: %s\n", strerror(errno));
return 2;
}
return (edata.data ? 0 : 1);
}
int main(int argc, char **argv)
{
int skfd = -1;
char *ifname;
int retval;
if( argv[1] )
ifname = argv[1];
else
ifname = "eth0";
/* Open a socket. */
if (( skfd = socket( AF_INET, SOCK_DGRAM, 0 ) ) < 0 )
{
printf("socket error\n");
exit(-1);
}
retval = detect_ethtool(skfd, ifname);
if (retval == 2)
retval = detect_mii(skfd, ifname);
close(skfd);
if (retval == 2)
printf("Could not determine status\n");
if (retval == 1)
printf("Link down\n");
if (retval == 0)
printf("Link up\n");
return retval;
}
*******************************程序3*****************************************************
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#define LINKTEST_GLINK 0x0000000a
struct linktest_value {
unsigned int cmd;
unsigned int data;
};
static
void
usage(const char * pname)
{
fprintf(stderr, "usage: %s <device>\n", pname);
fprintf(stderr, "returns: \n");
fprintf(stderr, "\t 0: link detected\n");
fprintf(stderr, "\t%d: %s\n", ENODEV, strerror(ENODEV));
fprintf(stderr, "\t%d: %s\n", ENONET, strerror(ENONET));
fprintf(stderr, "\t%d: %s\n", EOPNOTSUPP, strerror(EOPNOTSUPP));
exit(EXIT_FAILURE);
}
static
int
linktest(const char * devname)
{
struct ifreq ifr;
struct linktest_value edata;
int fd;
/* setup our control structures. */
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, devname);
/* open control socket. */
fd=socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0 ) {
return -ECOMM;
}
errno=0;
edata.cmd = LINKTEST_GLINK;
ifr.ifr_data = (caddr_t)&edata;
if(!ioctl(fd, SIOCETHTOOL, &ifr)) {
if(edata.data) { ltesting.net
fprintf(stdout, "link detected on %s\n", devname);
return 0;
} else {
errno=ENONET;
}
}
perror("linktest");
return errno;
}
int
main(int argc, char *argv[])
{
if(argc != 2) {
usage(argv[0]);
}
return linktest(argv[1]);
}
*************************************程序4*********************************************************
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#define BASE_VALUE 257
int main(int argc,char *argv[])
{
int mixer_fd=0;
char *names[SOUND_MIXER_NRDEVICES]=SOUND_DEVICE_LABELS;
int value,i;
printf("\nusage:%s dev_no.[0..24] value[0..100]\n\n",argv[0]);
printf("eg. %s 0 100\n",argv[0]);
printf(" will change the volume to MAX volume.\n\n");
printf("The dev_no. are as below:\n");
for (i=0;i<SOUND_MIXER_NRDEVICES;i++){
if (i%3==0) printf("\n");
printf("%s:%d\t\t",names[i],i);
}
printf("\n\n");
if (argc<3)
exit(1);
if ((mixer_fd = open("/dev/mixer",O_RDWR))){
printf("Mixer opened successfully,working...\n");
value=BASE_VALUE*atoi(argv[2]);
if (ioctl(mixer_fd,MIXER_WRITE(atoi(argv[1])),&value)==0)
printf("successfully.....");
else printf("unsuccessfully.....");
printf("done.\n");
}else
printf("can't open /dev/mixer error....\n"); ltesting.net
exit(0);
}