UNP学习笔记(第十七章 ioctl操作)
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操作)的更多相关文章
- Android学习笔记(十七)——数据库操作(下)
//此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 这一次我们来试一试升级数据库,并进行数据库的CRUD操作,其中, C 代表添加(Create) ,R 代表查询 ...
- [汇编学习笔记][第十七章使用BIOS进行键盘输入和磁盘读写
第十七章 使用BIOS进行键盘输入和磁盘读写 17.1 int 9 中断例程对键盘输入的处理 17.2 int 16 读取键盘缓存区 mov ah,0 int 16h 结果:(ah)=扫描码,(al) ...
- UNP学习笔记(第二章:传输层)
本章的焦点是传输层,包括TCP.UDP和SCTP. 绝大多数客户/服务器网络应用使用TCP或UDP.SCTP是一个较新的协议. UDP是一个简单的.不可靠的数据报协议.而TCP是一个复杂.可靠的字节流 ...
- Python学习笔记 -- 第六章 文件操作
I/O编程 在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘,所以,读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这 ...
- iOS学习笔记(十七)——文件操作(NSFileManager)
iOS的沙盒机制,应用只能访问自己应用目录下的文件.iOS不像android,没有SD卡概念,不能直接访问图像.视频等内容.iOS应用产生的内容,如图像.文件.缓存内容等都必须存储在自己的沙盒内.默认 ...
- UNP学习笔记(第一章 简介)
环境搭建 1.下载解压unpv13e.tar.gz 2.进入目录执行 ./configurecd lib //进入lib目录make //执行make命令 3.将生成的libunp.a静态库复制到/u ...
- 【转】iOS学习笔记(十七)——文件操作(NSFileManager)
iOS的沙盒机制,应用只能访问自己应用目录下的文件.iOS不像android,没有SD卡概念,不能直接访问图像.视频等内容.iOS应用产生的内容,如图像.文件.缓存内容等都必须存储在自己的沙盒内.默认 ...
- java JDK8 学习笔记——第16章 整合数据库
第十六章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API中的接口会有数据库厂商操作,称为JDBC驱动程 ...
- Programming Entity Framework-dbContext 学习笔记第五章
### Programming Entity Framework-dbContext 学习笔记 第五章 将图表添加到Context中的方式及容易出现的错误 方法 结果 警告 Add Root 图标中的 ...
随机推荐
- arcgis engine10.1和arcObjects的一些问题
1.arcengine10.1只支持vs2010 2.10.1以后没有engine runtimes,改成engine了,以前的engine可以理解为Arcobject,就是我们可以只装AO
- ZigBee学习一 任务处理函数_ProcessEvent
ZigBee学习一 任务处理函数_ProcessEvent //任务处理函数UINT16 GenericApp_ProcessEvent( byte task_id, UINT16 events ){ ...
- HDU 5322 Hope ——NTT 分治 递推
发现可以推出递推式.(并不会) 然后化简一下,稍有常识的人都能看出这是一个NTT+分治的情况. 然而还有更巧妙的方法,直接化简一下递推就可以了. 太过巧妙,此处不表,建议大家找到那篇博客. 自行抄写 ...
- BZOJ3571 [Hnoi2014]画框 【分治 + KM算法】
题目链接 BZOJ3571 题解 如果知道最小乘积生成树,那么这种双权值乘积最小就是裸题了 将两权值和作为坐标,转化为二维坐标系下凸包上的点,然后不断划分分治就好了 这里求的是最小匹配值,每次找点套一 ...
- 将npm改成默认使用cnpm下载
淘宝的cnpm下载安装的命令为 npm install -g cnpm --registry=https://registry.npm.taobao.org 但是仅仅这样是不够的,这样只有主动去下载资 ...
- react-router 4.0版本学习笔记
Router 所有路由组件的底层接口,一般情况都不使用,而是使用更加高级的路由. 最常用的有两种<BrowserRouter>.<HashRouter> <Browser ...
- 【06】Vue 之 组件化开发
组件其实就是一个拥有样式.动画.js逻辑.HTML结构的综合块.前端组件化确实让大的前端团队更高效的开发前端项目.而作为前端比较流行的框架之一,Vue的组件和也做的非常彻底,而且有自己的特色.尤其是她 ...
- SQL游标的使用与语法
原文发布时间为:2010-08-07 -- 来源于本人的百度文章 [由搬家工具导入] 以[master].[dbo].[spt_values] 这个表为例子===declare @name nvarc ...
- Repeater用ul li,一行显示多条数据
原文发布时间为:2009-08-26 -- 来源于本人的百度文章 [由搬家工具导入] .rep { width:680px; float:left; l ...
- User Experience Collection
about a data driven system front end: 1. about succeeded requests: they do not want to see alerts ab ...