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 指定針對本套接口的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 成員也被更新,以反映存放在緩沖區中的信息量

一般來講ioctl在用戶程序中的調用是:
ioctl(int fd,int command, (char*)argstruct)
ioctl調用與網絡編程有關(本文只討論這一點),文件描述符fd實際上是由socket()系統調用返回的。參數command的取值由/usr/include/linux/sockios.h 所規定。這些command的由於功能的不同,可分為以下幾個小類:
• 改變路由表 (例如 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)
與網絡接口有關的ioctl調用使用的command參數通常看起來像SIOCxIFyyyy的形式,這里x要 么是S(設定set,寫write),要么是G(得到get,讀read)。在getifinfo.c程序中就使用了這種形式的command參數來讀 IP地址,硬件地址,廣播地址和得到與網絡接口有關的一些標志(flag)。在這些ioctl調用中,第三個參數是ifreq結構,它在/usr /include/linux/if.h中定義。在某些情況下, ioctrl調用可能會使用到在sockios.h之外的新的定義。
 

實例:

程序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);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM