unix网络编程——ioctl 函数的用法详解

1.介绍

Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的信息,所以,我们有必要了解一下ioctl函数的具体实现.

2.函数说明

SYNOPSIS
       #include

       int ioctl(int d, int request, ...);

DESCRIPTION
       The  ioctl()  function  manipulates the underlying device parameters of
       special files.  In particular, many operating characteristics of  char-
       acter  special  files  (e.g., terminals) may be controlled with ioctl()
       requests.  The argument d must be an open file descriptor.

       The second argument is a  device-dependent  request  code.   The  third
       argument  is  an  untyped  pointer  to memory.  It’s traditionally char
       *argp (from the days before void * was valid C), and will be  so  named
       for this discussion.

       An  ioctl()  request  has  encoded  in it whether the argument is an in
       parameter or out parameter, and the size of the argument argp in bytes.
       Macros and defines used in specifying an ioctl() request are located in
       the file .

RETURN VALUE
       Usually, on success zero is returned.  A few ioctl() requests  use  the
       return  value as an output parameter and return a non-negative value on
       success.  On error, -1 is returned, and errno is set appropriately.

3.参数说明

类别

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)网络接口请求结构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;//设备协议设置 如果想获得网络接口的相关信息,就传入ifreq结构体.

(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];//查询网络接口的名称 }

5. 相关代码例子

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, char*argv[]) {
    int s,sv6;
    int err;
    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
        perror("socket error");
        return -1;
    }

//传入网络接口序号,获得网络接口的名称
    struct ifreq ifr;
    int ia = sizeof( struct ifreq );
    ia = sizeof(struct sockaddr);
   ia = 1;   err = 0;
   	bzero(&ifr, sizeof(struct ifreq));
	while (err != -1) {

		ifr.ifr_ifindex = ia++; //获得第2个网络接口的名称
		err = ioctl(s, SIOCGIFNAME, &ifr);
		if (-1 == err) {
			perror("index error");
		} else { //10111111 11111111 11101101 11101000
			char ipbuf[255];
			inet_ntop(AF_INET,
					&((struct sockaddr_in*) &(ifr.ifr_ifru.ifru_addr))->sin_addr,
					ipbuf, sizeof(ipbuf));
			printf("the %dst interface is:%s \t addr:%s\n", ifr.ifr_ifindex,
					ifr.ifr_name, ipbuf);
		}
	}
//传入网络接口名称,获得标志
    bzero(&ifr,sizeof(struct ifreq));
    memcpy(ifr.ifr_name, "eth0", 5);
    err = ioctl(s, SIOCGIFFLAGS, &ifr);
    if (!err) {
        printf("SIOCGIFFLAGS:%d\n", ifr.ifr_flags);
    }

//获得MTU和MAC
    bzero(&ifr,sizeof(struct ifreq));
    memcpy(ifr.ifr_name, "eth0", 5);
    err = ioctl(s, SIOCGIFMTU, &ifr);
    if (err!=-1) {
        printf("SIOCGIFMTU:%d\n", ifr.ifr_mtu);
    }else
    	perror("ioctl:");
//获得MAC地址
    bzero(&ifr,sizeof(struct ifreq));
    memcpy(ifr.ifr_name, "eth0", 5);
 //   ifr.ifr_ifindex = 1;
    err = ioctl(s, SIOCGIFHWADDR, &ifr);
    if (-1!=err) {
        unsigned char* hw = ifr.ifr_ifru.ifru_hwaddr.sa_data;
        printf("SIOCGIFHWADDR:%02x:%02x:%02x:%02x:%02x:%02x\n", hw[0], hw[1],
                hw[2], hw[3], hw[4], hw[5]);
    }else
    	perror("ioctl:");
//获得网卡映射参数 命令字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
    bzero(&ifr,sizeof(struct ifreq));
    memcpy(ifr.ifr_name, "eth0", 5);
 //   ifr.ifr_ifru.ifru_ivalue = 2;
    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);
    }
//查询子网掩码
    bzero(&ifr,sizeof(struct ifreq));
    memcpy(ifr.ifr_name, "eth0", 5);
    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);
}

 
  

6.内核主要函数调用框架

内核实现ioctl()函数的是sys_ioctl(),在内核中主要调用框架图如下,它清晰地给我们展示ioctl的控制传递框架,我们接下来的内容将根据此图向大家做详细的解释:



7.小结

      本文介绍了ioctrl 主要的用法,当然它还有很多用法,以后会继续追加的,该如函数的用法,主要就是其对应的request列表的用法,即是该函数的第二个参数的用法


参考: UNIX 网络编程

http://blog.163.com/jlz_325/blog/static/19174000920126893245653/

http://www.linuxidc.com/Linux/2007-12/9680.htm

http://linux.chinaunix.net/techdoc/develop/2007/09/05/967137.shtml


你可能感兴趣的:(2.2.,网络)