Loading

《Unix 网络编程》17:ioctl 操作

ioctl 操作

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式contact@mails.rencontact@mails.ren
原文标题《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园
原文地址https://www.cnblogs.com/lymtics/p/16366363.htmlhttps://www.cnblogs.com/lymtics/p/16366363.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

系列文章导航:《Unix 网络编程》笔记

概述

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式contact@mails.rencontact@mails.ren
原文标题《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园
原文地址https://www.cnblogs.com/lymtics/p/16366363.htmlhttps://www.cnblogs.com/lymtics/p/16366363.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

ioctl 简介

  • ioctl 函数传统上一直作为那些不适合归入其他精细定义类别的特性的系统接口
  • POSIX 在标准化过程中致力于创建一些特殊的函数以取代这个函数
  • 尽管如此,与网络编程相关的、且依赖于实现的特性保留的 ioctl 请求仍不在少数,它们用于获取接口信息、访问路由表、访问 ARP 告诉缓存等等。

用于网络编程的 ioctl 命令可以划分为六类:

  • 套接字操作(是否位于带外标记等)
  • 文件操作(设置或清除非阻塞标志等)
  • 接口操作(返回接口列表,获取广播地址等)
  • ARP 表操作(创建、修改、获取或删除)
  • 路由表操作(增加或删除)
  • 流系统(第 31 章才会讲到)

ioctl 函数

本函数影响由 fd 参数引用的一个打开的文件,函数原型如下:

#include <unistd.h>

// 成功返回 0 出错返回 -1
int ioctl(int fd, int request, ... /* void arg */);

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

不但某些 ioctl 操作和某些 fcntl 操作功能重叠,而且某些操作可以使用 ioctl 以不止一种方式指定,如下:

类别 Request 说明 数据类型
文件 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
struct ifreq
ARP SIOCSARP
SIOCGARP
SIOCDARP
创建/修改ARP表项
获取ARP表项
删除ARP表项
struct arpreq
struct arpreq
struct arpreq
路由 SIOCADDRT
SIOCDELRT
增加路径
删除路径
struct rtentry
struct rtentry
I_xxx 见 31 章

下面各节详细讲解这些请求。

注意:函数本身的返回值只能判断操作是否成功,下面都是通过第三个指针参数进行传递参数或获取返回值的!

套接字操作

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式contact@mails.rencontact@mails.ren
原文标题《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园
原文地址https://www.cnblogs.com/lymtics/p/16366363.htmlhttps://www.cnblogs.com/lymtics/p/16366363.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

这里,第三个参数指向一个 int 类型的变量

Request 详细说明 代替
SIOC.AT.MARK 如果本套接字的读指针当前位于带外标记,则返回一个非 0 值,否则返回一个 0 值 POSIX 以函数 sockatmark 替换本请求
SIOC.SP.GRP 设置套接字的进程ID或进程组ID,该ID指定本套接字的 SIGIO 或 SIGURG 信号的接收进程 fcntlF_SET.OWN
SIOC.GP.GRP 返回本套接字的进程ID或组ID,该ID指定针对本套接字的SIGIO和SIGURG 信号的接收进程 fcntlF_GET.OWN

文件操作

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式contact@mails.rencontact@mails.ren
原文标题《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园
原文地址https://www.cnblogs.com/lymtics/p/16366363.htmlhttps://www.cnblogs.com/lymtics/p/16366363.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

这一组请求以 FIO 打头,它们可能还适用于除了套接字外某些特定类型的文件。本节仅仅讨论适用于套接字的请求。

这里,第三个参数指向一个 int 类型的变量

Request 详细说明 代替
FIO.NBIO 根据第三个参数(1或0),设置或清除本套接字的非阻塞IO标志 O_NON.BLOCK 文件标志状态等效
可以通过 fcntlF_SET.FL 清除或设置该标志
FIO.ASYNC 根据第三个参数,设置或清除本套接字的信号驱动异步IO标志
决定是否受灾区针对本套接字的异步 IO 信号(SIGIO)
O_ASYNC 文件标志状态等效
可以通过 fcntlF_SET.FL 清除或设置该标志
FIO.NREAD 返回当前在本套接字接收缓冲区中的字节数
FIO.SET.OWN 对于套接字和 SIOC.SP.GRP 等效
FIO.GET.OWN 对于套接字和 SIOC.GP.GRP 等效

接口操作

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式contact@mails.rencontact@mails.ren
原文标题《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园
原文地址https://www.cnblogs.com/lymtics/p/16366363.htmlhttps://www.cnblogs.com/lymtics/p/16366363.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

这里的接口(interface)指的就是网络接口;下文中的 IF 一般都是其缩写。

结构

许多需要处理网络接口的程序的初始步骤之一就是从内核获取配置在系统中的所有接口,这个任务由 SIGC.G.IFCONF 请求完成,并要借助于一个结构:ifconf,而这个结构又包含了另一个结构 ifreq

我们在调用 ioctl 之前,要分配一个缓冲区和一个 ifconf 结构,然后初始化后者(如下图左侧),经过调用,该结构将被填充。

它们在调用前后的关系和变化如下:

下面是它们结构的代码:

get_ifi_info

目的:返回一个结构链表,每个结构对应一个处于 up 状态的接口

在原书中这段代码相当长,但是由于系统不一致等等原因,这段代码在我的机器上并不能如愿地运行起来,因此这里我只是看了一下代码的逻辑。下面部分只是对各个文件的功能做一个简述。

unpifi.h 头文件:

这个结构中保存了我们可能关注的信息,如接口名字、接口索引、MTU、硬件地址

prifinfo.c

作用:调用 get_ifi_info 函数(虽然我们还没有实现),并输出所有信息,可以看作是一个 mini 的 ifconfig 了

get_ifi_info.c

主要的逻辑部分,判断缓冲区大小是否足够(循环多次请求)、并根据相应的标志将数据保存到我们自定的结构中,最后还有一个用于释放空间的函数

操作

上面的 SIOC.G.IF.CONF 是获取每个已经配置的接口的名字和套接字地址结构,我们可以通过其他类似的请求以获取其他的信息。

get 通常由 netstat 程序发出,而 set 通常由 ifconfig 程序发出,后者需要超级用户权限。

request 说明 数据类型
SIOCGIFCONF 获取所有接口的清单 struct ifconf
SIOCSIFADDR 设置接口地址 struct ifreq
SIOCGIFADDR 获取接口地址 struct ifreq
SIOCSIFFLAGS 设置接口标志 struct ifreq
SIOCGIFFLAGS 获取接口标志 struct ifreq
SIOCSIFDSTADDR 设置点到点地址 struct ifreq
SIOCGIFDSTADDR 获取点到点地址 struct ifreq
SIOCGIFBRDADDR 获取广播地址 struct ifreq
SIOCSIFBRDADDR 设置广播地址 struct ifreq
SIOCGIFNETMASK 获取子网掩码 struct ifreq
SIOCSIFNETMASK 设置子网掩码 struct ifreq
SIOCGIFMETRIC 获取接口的测度 struct ifreq
SIOCSIFMETRIC 设置接口的测度 struct ifreq
SIOCGIFMTU 获取接口MTU struct ifreq
SIOCxxx (还有很多取决于系统的实现) struct ifreq

ARP 高速缓存操作

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式contact@mails.rencontact@mails.ren
原文标题《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园
原文地址https://www.cnblogs.com/lymtics/p/16366363.htmlhttps://www.cnblogs.com/lymtics/p/16366363.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

介绍

ARP 高速缓存也通过 ioctl 函数操纵,但是使用路由套接字(下一章)的系统往往改用路由套接字访问 ARP 告诉缓存。一些较新的系统只支持用路由套接字执行这些 ARP 操作。

这些请求都使用如下的结构:

#include <net/if_arp.h>

struct arpreq {
  struct sockaddr arp_pa;	// 协议地址
  struct sockaddr arp_ha;	// 硬件地址
  int arp_flags;	// 标志位
}

#define ATF_INUSE 0x01 // 使用中
#define ATF_COM 0x02	// 已经完成
#define ATF_PERM 0x04 // 永久记录
#define ATF_PUBL 0x08 // 已发布

这里,第三个参数指向一个 arpreq 类型的变量

Request 详细说明
SIOC.S.ARP 添加一个新的表项,或修改之;
其中 arp_pa 是一个含有 IP 地址的网络套接字地址结构
arp_ha 则是一个通用套接字地址结构,它的 sa_family 值为 AF_UNSPEC,sa_data 中含有硬件地址
ATF_PERM 和 ATF_PUBL 这两个标志也可以由应用程序指定
ATF_INUSE 和 ATF_COM 则由内核设置
SIOC.D.ARP 从 ARP 高速缓冲删除一个表项,调用者指定要删除表项的网络地址
SIOC.G.ARP 从 ARP 高速缓冲中获取一个表项,调用者指定网络地址,返回相应的硬件地址和标志

只有超级用户才能增加或删除表项,这三个请求通常由 arp 程序发出。

ioctl 没办法列出 ARP 高速缓存中的所有表项。当指定 -a 标志执行 arp 命令时,大多数版本的 arp 程序通过读取内核的内存 /dev/kmem 获得 ARP 告诉缓存的当前内容。下一章将用 sysctl 更容易地做到这一点。

代码

  • 目标:输出主机的硬件地址
  • 思路:使用上面的 get_ifi_info 函数返回一个主机的所有 IP 地址,然后对端每一个发送一个 SIOC.G.ARP 请求以显示它的硬件地址

代码如下:

#include <net/if_arp.h>
#include "unpifi.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, 0);
    // 调用 get_ifi_info 函数返回一个主机的所有 IP 地址
    for (ifi = get_ifi_info(AF_INET, 0); ifi != NULL; ifi = ifi->ifi_next) {

        // 使用 inet_ntop 显示 IP 地址
        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));

        // 然后对每一个 IP 地址发出一个 SIOCGARP 请求以获取他们的硬件地址
        if (ioctl(sockfd, SIOCGARP, &arpreq) < 0) {
            // 如果出错
            err_ret("ioctl SIOCGARP");
            continue;
        }

        // 打印信息
        ptr = &arpreq.arp_ha.sa_data[0];
        printf("%x:%x:%x:%x:%x:%x\n", *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3),
               *(ptr + 4), *(ptr + 5));
    }
    exit(0);
}

路由表操作

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式contact@mails.rencontact@mails.ren
原文标题《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园《Unix 网络编程》17:ioctl 操作 - 樵仙 - 博客园
原文地址https://www.cnblogs.com/lymtics/p/16366363.htmlhttps://www.cnblogs.com/lymtics/p/16366363.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

这里,第三个参数指向一个 rtentry 类型的变量

Request 详细说明 代替
SIOC.ADD.RT 往路由表添加一个表项 O_NON.BLOCK 文件标志状态等效
可以通过 fcntlF_SET.FL 清除或设置该标志
SIOC.DEL.RT 从路由表删除一个表项

这些请求通常由 route 程序发出,只有超级用户才能发出这些请求。

在支持路由域套接字的系统中,这些请求改由路由套接字而不是 ioctl 执行。

ioctl 没有办法列出路由表中的所有表项。这个操作通常由 netstat 程序在指定 -r 参数时完成。其通过读取内核的内存 /dev/kmem 获取整个路由表。

posted @ 2022-06-11 17:30  樵仙  阅读(254)  评论(0编辑  收藏  举报