ioctl相当于一个杂物箱,它一直作为那些不适合归入其他精细定义类别的特性的系统接口。

本章笔记先放着,到时候有需要再看

ioctl函数

#include <unistd.h>
int ioctl(int fd,int request,.../* void *arg */);

其中第三个参数总是一个指针,但指针的类型依赖于request参数。

我们可以把网络相关的请求(request)划分为6类:

1.套接字操作

2.文件操作

3.接口操作

4.ARP高速缓存操作

5.路由表操作

6.流系统

下图列出了网络相关ioctl请求的request参数以及arg地址必须指向的数据类型:

套接字操作

SIOCATMARK    如果本套接字的读指针当前位于带外标记,那就通过由第三个参数指向的整数返回一个非0值,否则返回一个0值

SIOCGPGRP       通过由第三个参数指向的整数返回本套接字的进程ID或进程组ID

SIOCSGRP         把本进程进程ID或进程组ID设置成由第三个参数指向的整数

文件操作

FIONBIO       根据ioctl的第三个参数指向一个0值或非0值,可清除或设置本套接字的非阻塞式I/O标志

FIOASYNC     根据ioctl的第三个参数指向一个0值或非0值,可清除或设置本套接字的信号驱动异步I/O标志,它决定是否收取针对本套接字的异步I/O信号(SIGIO)

FIONREAD     通过由ioctl的第三个参数指向的整数返回当前本套接字接收缓冲区中的字节数

FIOSETOWN   对于套接字和SIOCSPGRP等效

FIOGETOWN   对于套接字和SIOCGPGRP等效

接口配置

需处理网络接口的许多程序的初始步骤之一就是从内核获取配置在系统中的所有接口。本任务由SIOCGIFCONF请求完成,它使用ifconf结构,ifconf又使用ifreq结构。这两个结构定义如下:

struct ifconf
{
int ifc_len; /* size of buffer */
union
{
char *ifcu_buf; /* input from user->kernel*/
struct ifreq *ifcu_req; /* return from kernel->user*/
} ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
#define ifc_req ifc_ifcu.ifcu_req /* array of structures */ //ifreq用来保存某个接口的信息
//if.h
struct ifreq {
char ifr_name[IFNAMSIZ];
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
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
#define ifr_broadaddr ifr_ifru.ifru_broadaddr

在调用ioctl前我们先分配一个缓冲区和一个ifconf结构,然后初始化后者。下面展示这个ifconf结构的初始化结果,其中缓冲区的大小为1024字节

假设内核返回2个ifreq结构,在ioctl返回时通过同一个ifconf结构所返回的值如下图。缓冲区被填入两个ifreq结构,而且ifconf结构的ifc_len成员也被更新

get_ifi_info函数(暂时不看)

我们使用ioctl开发一个名为get_ifi_info的函数,它返回一个结构链表,其中每个结构对应一个当前处于“up”状态的接口。

头文件

 /* Our own header for the programs that need interface configuration info.
Include this file, instead of "unp.h". */ #ifndef __unp_ifi_h
#define __unp_ifi_h #include "unp.h"
#include <net/if.h> #define IFI_NAME 16 /* same as IFNAMSIZ in <net/if.h> */
#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */ struct ifi_info {
char ifi_name[IFI_NAME]; /* interface name, null-terminated */
short ifi_index; /* interface index */
short ifi_mtu; /* interface MTU */
u_char ifi_haddr[IFI_HADDR]; /* hardware address */
u_short ifi_hlen; /* # bytes in hardware address: 0, 6, 8 */
short ifi_flags; /* IFF_xxx constants from <net/if.h> */
short ifi_myflags; /* our own IFI_xxx flags */
struct sockaddr *ifi_addr; /* primary address */
struct sockaddr *ifi_brdaddr;/* broadcast address */
struct sockaddr *ifi_dstaddr;/* destination address */
struct ifi_info *ifi_next; /* next of these structures */
}; #define IFI_ALIAS 1 /* ifi_addr is an alias */ /* function prototypes */
struct ifi_info *get_ifi_info(int, int);
struct ifi_info *Get_ifi_info(int, int);
void free_ifi_info(struct ifi_info *); #endif /* __unp_ifi_h */

main函数

 #include    "unpifi.h"

 int
main(int argc, char **argv)
{
struct ifi_info *ifi, *ifihead;
struct sockaddr *sa;
u_char *ptr;
int i, family, doaliases; if (argc != )
err_quit("usage: prifinfo <inet4|inet6> <doaliases>"); if (strcmp(argv[], "inet4") == )
family = AF_INET;
#ifdef IPv6
else if (strcmp(argv[], "inet6") == )
family = AF_INET6;
#endif
else
err_quit("invalid <address-family>");
doaliases = atoi(argv[]); for (ifihead = ifi = Get_ifi_info(family, doaliases);
ifi != NULL; ifi = ifi->ifi_next) {
printf("%s: ", ifi->ifi_name);
if (ifi->ifi_index != )
printf("(%d) ", ifi->ifi_index);
printf("<");
/* *INDENT-OFF* */
if (ifi->ifi_flags & IFF_UP) printf("UP ");
if (ifi->ifi_flags & IFF_BROADCAST) printf("BCAST ");
if (ifi->ifi_flags & IFF_MULTICAST) printf("MCAST ");
if (ifi->ifi_flags & IFF_LOOPBACK) printf("LOOP ");
if (ifi->ifi_flags & IFF_POINTOPOINT) printf("P2P ");
printf(">\n");
/* *INDENT-ON* */ if ( (i = ifi->ifi_hlen) > ) {
ptr = ifi->ifi_haddr;
do {
printf("%s%x", (i == ifi->ifi_hlen) ? " " : ":", *ptr++);
} while (--i > );
printf("\n");
}
if (ifi->ifi_mtu != )
printf(" MTU: %d\n", ifi->ifi_mtu); if ( (sa = ifi->ifi_addr) != NULL)
printf(" IP addr: %s\n",
Sock_ntop_host(sa, sizeof(*sa)));
if ( (sa = ifi->ifi_brdaddr) != NULL)
printf(" broadcast addr: %s\n",
Sock_ntop_host(sa, sizeof(*sa)));
if ( (sa = ifi->ifi_dstaddr) != NULL)
printf(" destination addr: %s\n",
Sock_ntop_host(sa, sizeof(*sa)));
}
free_ifi_info(ifihead);
exit();
}

get_ifi_info.c

 /* include get_ifi_info1 */
#include "unpifi.h" struct ifi_info *
get_ifi_info(int family, int doaliases)
{
struct ifi_info *ifi, *ifihead, **ifipnext;
int sockfd, len, lastlen, flags, myflags, idx = , hlen = ;
char *ptr, *buf, lastname[IFNAMSIZ], *cptr, *haddr, *sdlname;
struct ifconf ifc;
struct ifreq *ifr, ifrcopy;
struct sockaddr_in *sinptr;
struct sockaddr_in6 *sin6ptr; sockfd = Socket(AF_INET, SOCK_DGRAM, ); lastlen = ;
len = * sizeof(struct ifreq); /* initial buffer size guess */
for ( ; ; ) {
buf = Malloc(len);
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) < ) {
if (errno != EINVAL || lastlen != )
err_sys("ioctl error");
} else {
if (ifc.ifc_len == lastlen)
break; /* success, len has not changed */
lastlen = ifc.ifc_len;
}
len += * sizeof(struct ifreq); /* increment */
free(buf);
}
ifihead = NULL;
ifipnext = &ifihead;
lastname[] = ;
sdlname = NULL;
/* end get_ifi_info1 */ /* include get_ifi_info2 */
for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
ifr = (struct ifreq *) ptr; #ifdef HAVE_SOCKADDR_SA_LEN
len = max(sizeof(struct sockaddr), ifr->ifr_addr.sa_len);
#else
switch (ifr->ifr_addr.sa_family) {
#ifdef IPV6
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
#endif
case AF_INET:
default:
len = sizeof(struct sockaddr);
break;
}
#endif /* HAVE_SOCKADDR_SA_LEN */
ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */ #ifdef HAVE_SOCKADDR_DL_STRUCT
/* assumes that AF_LINK precedes AF_INET or AF_INET6 */
if (ifr->ifr_addr.sa_family == AF_LINK) {
struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
sdlname = ifr->ifr_name;
idx = sdl->sdl_index;
haddr = sdl->sdl_data + sdl->sdl_nlen;
hlen = sdl->sdl_alen;
}
#endif if (ifr->ifr_addr.sa_family != family)
continue; /* ignore if not desired address family */ myflags = ;
if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL)
*cptr = ; /* replace colon with null */
if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == ) {
if (doaliases == )
continue; /* already processed this interface */
myflags = IFI_ALIAS;
}
memcpy(lastname, ifr->ifr_name, IFNAMSIZ); ifrcopy = *ifr;
Ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
flags = ifrcopy.ifr_flags;
if ((flags & IFF_UP) == )
continue; /* ignore if interface not up */
/* end get_ifi_info2 */ /* include get_ifi_info3 */
ifi = Calloc(, sizeof(struct ifi_info));
*ifipnext = ifi; /* prev points to this new one */
ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ ifi->ifi_flags = flags; /* IFF_xxx values */
ifi->ifi_myflags = myflags; /* IFI_xxx values */
#if defined(SIOCGIFMTU) && defined(HAVE_STRUCT_IFREQ_IFR_MTU)
Ioctl(sockfd, SIOCGIFMTU, &ifrcopy);
ifi->ifi_mtu = ifrcopy.ifr_mtu;
#else
ifi->ifi_mtu = ;
#endif
memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
ifi->ifi_name[IFI_NAME-] = '\0';
/* If the sockaddr_dl is from a different interface, ignore it */
if (sdlname == NULL || strcmp(sdlname, ifr->ifr_name) != )
idx = hlen = ;
ifi->ifi_index = idx;
ifi->ifi_hlen = hlen;
if (ifi->ifi_hlen > IFI_HADDR)
ifi->ifi_hlen = IFI_HADDR;
if (hlen)
memcpy(ifi->ifi_haddr, haddr, ifi->ifi_hlen);
/* end get_ifi_info3 */
/* include get_ifi_info4 */
switch (ifr->ifr_addr.sa_family) {
case AF_INET:
sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
ifi->ifi_addr = Calloc(, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); #ifdef SIOCGIFBRDADDR
if (flags & IFF_BROADCAST) {
Ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
ifi->ifi_brdaddr = Calloc(, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif #ifdef SIOCGIFDSTADDR
if (flags & IFF_POINTOPOINT) {
Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
ifi->ifi_dstaddr = Calloc(, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif
break; case AF_INET6:
sin6ptr = (struct sockaddr_in6 *) &ifr->ifr_addr;
ifi->ifi_addr = Calloc(, sizeof(struct sockaddr_in6));
memcpy(ifi->ifi_addr, sin6ptr, sizeof(struct sockaddr_in6)); #ifdef SIOCGIFDSTADDR
if (flags & IFF_POINTOPOINT) {
Ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
sin6ptr = (struct sockaddr_in6 *) &ifrcopy.ifr_dstaddr;
ifi->ifi_dstaddr = Calloc(, sizeof(struct sockaddr_in6));
memcpy(ifi->ifi_dstaddr, sin6ptr, sizeof(struct sockaddr_in6));
}
#endif
break; default:
break;
}
}
free(buf);
return(ifihead); /* pointer to first structure in linked list */
}
/* end get_ifi_info4 */ /* include free_ifi_info */
void
free_ifi_info(struct ifi_info *ifihead)
{
struct ifi_info *ifi, *ifinext; for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
if (ifi->ifi_addr != NULL)
free(ifi->ifi_addr);
if (ifi->ifi_brdaddr != NULL)
free(ifi->ifi_brdaddr);
if (ifi->ifi_dstaddr != NULL)
free(ifi->ifi_dstaddr);
ifinext = ifi->ifi_next; /* can't fetch ifi_next after free() */
free(ifi); /* the ifi_info{} itself */
}
}
/* end free_ifi_info */ struct ifi_info *
Get_ifi_info(int family, int doaliases)
{
struct ifi_info *ifi; if ( (ifi = get_ifi_info(family, doaliases)) == NULL)
err_quit("get_ifi_info error");
return(ifi);
}

运行情况

接口操作

SIOCGIFCONF请求为每个已配置的接口返回其明知以及一个套接字地址结构。我们接着可以发出多个接口类的其他请求以设置或获取每个接口的其他特性

这些请求的get版本(SIOCGxxx)通常由netstat程序发出,set版本(SIOCSxxx)通常由ifconfig程序发出

这些请求接受或返回一个ifreq结构中的信息,而这个结构的地址作为ioctl调用的第三个参数。

SIGCGIFADDR   在ifr_addr成员中返回单播地址

SIOCSIFADDR    用ifr_addr成员设置接口地址

SIOCGIFFLAGS   在ifr_flags成员中返回接口标志,如:是否处于在工状态(IFF_UP)

SIOCSIFFLAGS    用ifr_flags成员设置接口标志

SIOCGIFDSTADDR  在if_dstaddr成员中返回点到点地址

SIGCSIFDSTADDR  用if_dstaddr成员设置点到点地址

SIOCGIFBRDADDR  在ifr_broadaddr成员中返回广播地址

SIOCSIFBRDADDR   用ifr_broadaddr成员设置广播地址

SIOCGIFNETMASK    在ifr_addr成员中返回子网掩码

SIOCSIFNETMASK    用ifr_addr成员设置子网掩码

SIOCGIFMETRIC   在ifr_metric成员返回接口测度

SIGCSIFMETRIC   用ifr_metric成员设置接口测度

ARP高速缓存操作

这些请求使用如下所示的arpreq结构

struct arpreq{
struct sockaddr arp_pa; //协议地址
struct sockaddr arp_ha; //硬件地址
int arp_flags;//标志位
} #define ATR_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 (repond for other host) */

操纵ARP高速缓存的ioctl请求有以下3个

SIOCSARP    把一个新的表项加到ARP高速缓存,或者修改其中一个已经存在的表项

SIOCDARP    从ARP高速缓存中删除一个表项。调用者指定要删除的arp_pa

SIOCGARP    从ARP高速缓存中获取一个表项。调用者指定arp_pa,相应的硬件地址随标志一起返回

下面程序用于输出主机的硬件地址

 #include    "unpifi.h"
#include <net/if_arp.h> int
main(int argc, char **argv)
{
int sockfd;
struct ifi_info *ifi;
unsigned char *ptr;
struct arpreq arpreq;
struct sockaddr_in *sin; sockfd = Socket(AF_INET, SOCK_DGRAM, );
for (ifi = get_ifi_info(AF_INET, ); ifi != NULL; ifi = ifi->ifi_next) {
printf("%s: ", Sock_ntop(ifi->ifi_addr, sizeof(struct sockaddr_in))); sin = (struct sockaddr_in *) &arpreq.arp_pa;
memcpy(sin, ifi->ifi_addr, sizeof(struct sockaddr_in)); if (ioctl(sockfd, SIOCGARP, &arpreq) < ) {
err_ret("ioctl SIOCGARP");
continue;
} ptr = &arpreq.arp_ha.sa_data[];
printf("%x:%x:%x:%x:%x:%x\n", *ptr, *(ptr+),
*(ptr+), *(ptr+), *(ptr+), *(ptr+));
}
exit();
}

先调用get_ifi_info获取本机所有的IP地址,然后在一个循环中遍历每个地址

使用inet_ntop显示IP地址

使用ioctl调用返回硬件地址(SIOCGARP)

路由表操作

有些系统提供两个用于操纵路由表的ioctl请求,在支持路由域套接字的系统中,这些请求改由路由套接字完成

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

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

UNP学习笔记(第十七章 ioctl操作)的更多相关文章

  1. Android学习笔记(十七)——数据库操作(下)

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 这一次我们来试一试升级数据库,并进行数据库的CRUD操作,其中, C 代表添加(Create) ,R 代表查询 ...

  2. [汇编学习笔记][第十七章使用BIOS进行键盘输入和磁盘读写

    第十七章 使用BIOS进行键盘输入和磁盘读写 17.1 int 9 中断例程对键盘输入的处理 17.2 int 16 读取键盘缓存区 mov ah,0 int 16h 结果:(ah)=扫描码,(al) ...

  3. UNP学习笔记(第二章:传输层)

    本章的焦点是传输层,包括TCP.UDP和SCTP. 绝大多数客户/服务器网络应用使用TCP或UDP.SCTP是一个较新的协议. UDP是一个简单的.不可靠的数据报协议.而TCP是一个复杂.可靠的字节流 ...

  4. Python学习笔记 -- 第六章 文件操作

    I/O编程 在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这 ...

  5. iOS学习笔记(十七)——文件操作(NSFileManager)

    iOS的沙盒机制,应用只能访问自己应用目录下的文件.iOS不像android,没有SD卡概念,不能直接访问图像.视频等内容.iOS应用产生的内容,如图像.文件.缓存内容等都必须存储在自己的沙盒内.默认 ...

  6. UNP学习笔记(第一章 简介)

    环境搭建 1.下载解压unpv13e.tar.gz 2.进入目录执行 ./configurecd lib //进入lib目录make //执行make命令 3.将生成的libunp.a静态库复制到/u ...

  7. 【转】iOS学习笔记(十七)——文件操作(NSFileManager)

    iOS的沙盒机制,应用只能访问自己应用目录下的文件.iOS不像android,没有SD卡概念,不能直接访问图像.视频等内容.iOS应用产生的内容,如图像.文件.缓存内容等都必须存储在自己的沙盒内.默认 ...

  8. java JDK8 学习笔记——第16章 整合数据库

    第十六章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API中的接口会有数据库厂商操作,称为JDBC驱动程 ...

  9. Programming Entity Framework-dbContext 学习笔记第五章

    ### Programming Entity Framework-dbContext 学习笔记 第五章 将图表添加到Context中的方式及容易出现的错误 方法 结果 警告 Add Root 图标中的 ...

随机推荐

  1. HDU 2036 求任意多边形面积向量叉乘

    三角形的面积可以使用向量的叉积来求: 对于 三角形的面积 等于: [(x2 - x1)*(y3 - y1)- ( y2 - y1 ) * ( x3 - x1 )  ] / 2.0 但是面积是有方向的, ...

  2. BZOJ3309 DZY Loves Math 【莫比乌斯反演】

    题目 对于正整数n,定义f(n)为n所含质因子的最大幂指数.例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0. 给定正整数a,b,求sigma(si ...

  3. JS 中如何输出空格

    在写JS代码的时候,大家可以会发现这样现象: document.write(" 1 2 3 "); 结果: 无论在输出的内容中什么位置有多少个空格,显示的结果好像只有一个空格. 这 ...

  4. 阿里云将centos 7 自带的 php 5.4升级为 5.6

    1.php -v PHP 5.4.16 (cli) (built: Nov 6 2016 00:29:02) Copyright (c) 1997-2013 The PHP Group Zend En ...

  5. 【HDOJ5536】Chip Factory(Trie树)

    题意:给定n个数字,第i个数字为a[i],求max((a[i]+a[j])^a[k]),其中i,j,k互不相同 n<=1000,0<=a[i]<=1e9 思路:队友写的,抱大腿 先对 ...

  6. 转 C/C++内存分配方式与存储区

    C/C++内存分配方式与存储区 C/C++内存分配有三种方式:[1]从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量.[2]在栈 ...

  7. Android 设置图片倒影效果

    首先,贴出效果图: 1.布局文件main.xml <?xml version="1.0" encoding="utf-8"?> <Linear ...

  8. sql_mode引发的数据库问题

    前几天,在本地做完项目,测试完毕后,上传到线上服务器的时候,在做很多写入数据库的操作时,发现全部发生500报错,返回的报错信息是,某个字段没有默认值,写入的时候没有添加这个字段,该字段在数据表中是NO ...

  9. Jmeter(五十)_性能测试模拟真实场景下的用户操作

    概述 我们在做性能测试的时候,不同的视角看到的结果都不一样. 例如响应时间 用户通过客户端向服务端发出请求的时间为: T1服务端接收到请求,处理该请求的时间为:T2服务端返回数据给客户端时间为: T3 ...

  10. Careercup | Chapter 1

    1.1 Implement an algorithm to determine if a string has all unique characters. What if you cannot us ...