Linux网络编程- struct ifreq ioctl() 系统调用

news/2024/10/28 23:30:29/

struct ifreq

struct ifreq 是一个数据结构,用于各种与接口相关的输入/输出控制 (ioctl) 调用。它的主要用途是在网络编程中获取和设置网络接口的属性。这个结构体在 <net/if.h> 头文件中定义。

以下是 struct ifreq 的一些主要字段和它们的用途:

  1. ifr_name: 一个字符数组,表示接口的名称,如 “eth0”, “wlan0” 等。

  2. ifr_addr: 一个 struct sockaddr 类型的结构,表示接口的地址。

  3. ifr_netmask: 同样是一个 struct sockaddr 类型的结构,表示接口的网络掩码。

  4. ifr_broadaddr: 表示接口的广播地址。

  5. ifr_flags: 表示接口的标志,如 IFF_UP (接口正在运行)、IFF_BROADCAST (接口支持广播)、IFF_PROMISC (接口在混杂模式下)等。

  6. ifr_hwaddr: 表示接口的硬件(通常是 MAC)地址。

  7. ifr_mtu: 表示接口的最大传输单元 (MTU)。

还有其他一些字段,但上面列出的是最常用的。

当我们想查询或设置接口的特定属性时,会填充 struct ifreq 的适当字段,并使用 ioctl() 系统调用。例如,要设置接口为混杂模式,我们会填充 ifr_name 字段并设置 IFF_PROMISC 标志,然后调用 ioctl()

例如:

struct ifreq ifr;
int sockfd;// 创建一个套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {perror("socket");exit(EXIT_FAILURE);
}// 设置ifr_name为我们想查询或修改的接口名
strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);// 获取接口标志
if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {perror("ioctl");close(sockfd);exit(EXIT_FAILURE);
}// 打印是否处于混杂模式
if (ifr.ifr_flags & IFF_PROMISC) {printf("eth0 is in promiscuous mode\n");
} else {printf("eth0 is not in promiscuous mode\n");
}close(sockfd);

这是一个简单的示例,演示了如何检查网络接口(在这种情况下是 “eth0”)是否处于混杂模式。


ioctl() 系统调用

ioctl() 系统调用提供了一种通用的设备驱动程序或接口操作机制,可以用于各种设备特定的操作和控制。它通常用于那些不适合用常规系统调用(如 read(), write(), open() 等)表示的设备特定的操作。

函数原型:

int ioctl(int fd, unsigned long request, ...);

参数:

  1. fd: 是一个已经打开的文件或套接字的文件描述符。

  2. request: 是设备特定的请求码。这个请求码定义了我们想要执行的特定操作或命令。

  3. : 这是一个可变参数,取决于特定的 request。它可能是一个指向数据的指针,也可能是一个直接的值。

返回值:

  • ioctl() 的返回值取决于特定的操作。通常,成功返回非负值,失败返回 -1,并设置 errno

用途:

  1. 网络编程: ioctl() 经常用于查询和设置网络接口的参数。例如,获取或设置接口的 IP 地址,查询或设置接口的标志等。

  2. 设备控制: 对于各种设备(如终端、声音卡、图形卡),ioctl() 可以用于执行特定的操作或查询信息。

  3. 文件系统: 在某些情况下,ioctl() 也可用于文件系统操作。

一些例子:

  1. 获取网络接口的标志:

    struct ifreq ifr;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);
    ioctl(fd, SIOCGIFFLAGS, &ifr);
    
  2. 设置非阻塞套接字:

    int flags = fcntl(fd, F_GETFL, 0);
    ioctl(fd, FIONBIO, &flags);
    
  3. 查询音频设备属性:

    int volume;
    ioctl(fd, SOUND_MIXER_READ_VOLUME, &volume);
    

注意事项:

  1. 不是所有的设备都支持所有的 ioctl 操作。要知道设备支持哪些操作,需要查阅特定设备的文档或头文件。

  2. 错误处理:与其他系统调用一样,使用 ioctl() 时也应进行适当的错误处理。它可能会失败,并设置 errno,所以应该检查它的返回值并相应地处理错误。

  3. 可移植性ioctl() 是一个非常通用且强大的函数,但它经常与特定的设备或平台相关。这意味着在不同的操作系统或硬件平台上,可能需要不同的 ioctl 请求代码或参数。

总之,ioctl() 提供了一种与设备进行交互的方法,允许开发者执行不同的设备特定操作。然而,正确地使用它需要对特定设备的工作方式有深入的了解。


示例

下面让我们用一个简单的例子来展示如何使用 ioctl() 来获取和设置网络接口(例如 eth0)的 IP 地址。

1. 获取 IP 地址

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>int main() {int sockfd;struct ifreq ifr;// 创建一个 socketsockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("socket");exit(1);}// 指定要查询的接口名strncpy(ifr.ifr_name, "enp5s0", IFNAMSIZ);// 获取接口的 IP 地址if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) {perror("ioctl - SIOCGIFADDR ");exit(1);}struct sockaddr_in *ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;printf("IP address of enp5s0: %s\n", inet_ntoa(ipaddr->sin_addr));close(sockfd);return 0;
}

程序运行结果为:

majn@tiger:~/C_Project/network_project$ ./ioctl_setip 
IP address of enp5s0: 192.168.31.6

2. 设置 IP 地址(慎用!!!)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>int main() {int sockfd;struct ifreq ifr;struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;// 创建一个 socketsockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("socket");exit(1);}// 指定要设置的接口名strncpy(ifr.ifr_name, "eth0", IFNAMSIZ);// 设置新的 IP 地址sin->sin_family = AF_INET;inet_aton("192.168.1.100", &sin->sin_addr);if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {perror("ioctl - SIOCSIFADDR ");exit(1);}printf("IP address of eth0 set to: %s\n", inet_ntoa(sin->sin_addr));close(sockfd);return 0;
}

注意: 设置 IP 地址通常需要 root 权限,所以运行上述代码时可能需要使用 sudo

上述示例中,ioctl() 被用来查询或设置网络接口的 IP 地址。具体的操作由 SIOCGIFADDR(获取地址)和 SIOCSIFADDR(设置地址)指定。


inet_aton()

inet_aton() 函数用于将点分十进制格式的字符串(如 “192.168.1.1”)转换为一个网络字节序的 IPv4 地址。它在 <arpa/inet.h> 头文件中定义。

函数原型:

int inet_aton(const char *cp, struct in_addr *inp);

参数:

  • cp: 指向点分十进制格式的 IPv4 地址字符串的指针。
  • inp: 指向一个 struct in_addr 结构体的指针,该结构体用于存储转换后的地址。

返回值:

  • 成功时返回 1,失败时返回 0。

inet_addr() 相比,inet_aton() 函数提供了更清晰的错误检查机制,因为它明确返回成功或失败的状态,而不是使用特殊的地址值来表示错误。

示例:

#include <stdio.h>
#include <arpa/inet.h>int main() {const char *ip_string = "192.168.1.1";struct in_addr ip_addr;if (inet_aton(ip_string, &ip_addr)) {printf("Conversion successful. Network byte order value: 0x%x\n", ip_addr.s_addr);} else {printf("Conversion failed.\n");}return 0;
}

上述程序的运行结果为:

majn@tiger:~/C_Project/network_project$ ./inet_aton_demo 
Conversion successful. Network byte order value: 0x101a8c0

在此示例中,我们将点分十进制的 IP 地址字符串 “192.168.1.1” 使用 inet_aton() 转换为 struct in_addr 格式,并输出转换的结果。

注意:inet_aton()inet_ntoa() 是一对,一个用于将字符串转换为二进制格式,另一个用于进行相反的操作。如果您正在寻找一个线程安全的方法或需要处理 IPv6 地址,建议使用 inet_pton()inet_ntop() 函数代替。


inet_ntoa()

inet_ntoa() 是一个网络工具函数,用于将一个网络字节序的 IPv4 地址转换为点分十进制字符串格式。它在 <arpa/inet.h> 头文件中定义。

函数原型:

char *inet_ntoa(struct in_addr in);

参数:

  • in: 是一个 struct in_addr 结构体,包含一个 IPv4 地址(在网络字节序中)。

返回值:

  • 返回一个指向一个静态分配的字符串的指针,该字符串表示 IPv4 地址的点分十进制表示形式。因为返回的是一个静态字符串,所以之后的 inet_ntoa() 调用可能会覆盖之前的内容。

注意事项:

  1. inet_ntoa() 返回一个指向静态存储区的指针,这意味着每次调用都会覆盖前一次的结果。如果需要保留转换的地址,应该自行复制返回的字符串。
  2. 考虑到线程安全性,inet_ntoa() 在多线程环境中可能会导致问题。为了避免这种情况,可以考虑使用更现代、线程安全的函数,例如 inet_ntop()

示例:

#include <stdio.h>
#include <arpa/inet.h>int main() {struct in_addr ip_addr;ip_addr.s_addr = htonl(0xC0A80101);  // 0xC0A80101 = 192.168.1.1 in hexadecimalchar *ip_str = inet_ntoa(ip_addr);printf("IP address in dotted-decimal format: %s\n", ip_str);return 0;
}

在此示例中,我们创建了一个 struct in_addr 结构体,设置了 IP 地址(以网络字节序),然后使用 inet_ntoa() 将它转换为字符串。

上述程序的运行结果为:

majn@tiger:~/C_Project/network_project$ ./inet_ntoa_demo 
IP address in dotted-decimal format: 192.168.1.1

http://www.ppmy.cn/news/1134506.html

相关文章

凉鞋的 Unity 笔记 105. 第一个通识:编辑-测试 循环

105. 第一个通识&#xff1a;编辑-测试 循环 在这一篇&#xff0c;我们简单聊聊此教程中所涉及的一个非常重要的概念&#xff1a;循环。 我们在做任何事情都离不开某种循环&#xff0c;比如每天的 24 小时循环&#xff0c;一日三餐循环&#xff0c;清醒-睡觉循环。 在学习一…

【改进哈里鹰算法(NCHHO)】使用混沌和非线性控制参数来提高哈里鹰算法的优化性能,解决车联网相关的路由问题(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【强化算法专题一】双指针算法

【强化算法专题一】双指针算法 1.双指针算法--移动零2.双指针算法--复写零3.双指针算法--快乐数4.双指针算法--盛水最多的容器5.双指针算法--有效三角形的个数6.双指针算法--和为s的两个数7.双指针算法--三数之和8.双指针算法--四数之和 1.双指针算法–移动零 算法原理解析----…

基于Java的老年人体检管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

前端与后端:程序中两个不同的领域

前端和后端是构成一个完整的计算机应用系统的两个主要部分。它们分别负责不同的功能和任务&#xff0c;有以下几个方面的区别&#xff1a; 功能&#xff1a;前端主要负责用户界面的呈现和交互&#xff0c;包括网页的设计、布局、样式、动画效果和用户输入等。后端则处理网站或应…

常见变量命名方法:PascalCase, camelCase, hungarian_notation, kebab-case

PascalCase 帕斯卡拼写法( 也叫大骆驼拼写法), 它主要的特点是将描述变量作用所有单词的首字母大写&#xff0c;然后直接连接起来&#xff0c;单词之间没有连接符。 比如&#xff1a;Age, LastName, WinterOfDiscontent camelCase (驼峰命名法&#xff09;驼峰命名法第一个单…

Docker命令起别名

1.打开.bashrc文件 vi ~/.bashrc 2. 起别名 alias dpsdocker ps --format "table{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}" alias disdocker images 3. 文件生效 source ~/.bashrc 4.展示

Leecode121: 买卖股票的最佳时机1 2 3

第一个思路是从前往后进行遍历&#xff0c;记录一个最小值&#xff0c;然后往后维护一个最大值&#xff0c;不停的减去即可。 买卖股票的最佳时机2&#xff1a; prices [7,1,2,5,6,4] 在第二天买入&#xff0c;在第五天卖出&#xff0c;和不断买入卖出是一样的&#xff0c;所…