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 图标中的 ...
随机推荐
- Fragment控件初始化
代码改变世界 Fragment控件初始化 @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup ...
- i++ 和++i 的理解 以防面试
根本原理: //模拟 a++ function afterAdd(){ var temp = a; a = a+1; return temp; } //模拟++a; function beforeAd ...
- sqlmap post注入两种方式
1. 使用抓包工具抓取数据包,sqlmap加载数据包 POST /SME/static/orderFind/orderIntfaceph HTTP/1.1 Host: abc.com User-Age ...
- Error:Cannot find module 'stylus'
在webpack 里面用了 stylus-loader,但npm instatll 没有正确安装,出现error: Cannot find module ‘stylus’. 解决办法: 重新npm i ...
- jquery - 设置/获取内容和属性
一般我们会遇到给某个元素添加或更改原有的文字: 1. 设置/获取内容 - text().html() 以及 val() 设置内容常用的三个方法: text() - 设置或返回所选元素的文本内容 htm ...
- UltraEdit快捷键大全-UltraEdit常用快捷键大全
UltraEdit快捷键大全-UltraEdit常用快捷键大全 UltraEdit是一套功能强大的文本编辑器,可以编辑文本.十六进制.ASCII码,可以取代记事本,内建英文单字检查.C++及VB指令突 ...
- 1180: [CROATIAN2009]OTOCI
1180: [CROATIAN2009]OTOCI Time Limit: 50 Sec Memory Limit: 162 MBSubmit: 1032 Solved: 638[Submit][ ...
- LeetCode OJ-- Valid Sudoku
https://oj.leetcode.com/problems/valid-sudoku/ 给出数独的一部分来,验证给出的这一部分是否为 valid 行上无重复的,且是从 ‘1’ 到‘9’ 列上无重 ...
- 慎用lodash的cloneDeep函数
lodash的cloneDeep函数能够很方便的拷贝对象,但是一旦拷贝一些很复杂的对象就有可能报错.比如用cloneDeep克隆一个vue实例,就有可能包key.charAt is not a Fun ...
- NLP--edit distance
基本思想 通过插入(insert).删除(delete)和替换(substitute)个操作将一个字符串s1变换到另一个字符串s2的最少步骤数distacnce,用(1-distance/length ...