Chinaunix首页 | 论坛 | 博客
  • 博客访问: 565183
  • 博文数量: 127
  • 博客积分: 1169
  • 博客等级: 少尉
  • 技术积分: 1298
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-16 14:29
个人简介

空白

文章分类

全部博文(127)

分类: LINUX

2014-12-09 15:31:42

ioctl函数

本函数影响由fd参数引用的一个打开的文件。

#include

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,头文件中定义。举例来说,这些标志指示接口是否处于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结构,定义在

 

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结构的一个指针,该结构定义在头文件中。这些请求通常由route

程序发出。只有超级用户才能发出这些请求。

SIOCADDRT:往路由表中增加一个表项

SIOCDELRT:从路由表中删除一个表项

Ioctl没有办法列出路由表中的所有表项。这个操作通常由netstat程序在指定-r标志自行四

完成。netstat程序通过读取内核的内存  /dev/kmem)获得整个路由表。用sysctl同样可

以做到。

 

 

如下是chinaunix论坛给出的一个例子。

 

#include

#include

#include

#include

#include

#include

#include

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

阅读(1884) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~