Netlink是Linux内核提供的一种用于内核与用户空间进程之间通信的机制。

本文主要是介绍Netlink是Linux内核提供的一种用于内核与用户空间进程之间通信的机制。,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Netlink是Linux内核提供的一种用于内核与用户空间进程之间通信的机制。它允许内核向用户空间发送消息,同时也可以接收用户空间的请求并做出相应的响应。

Netlink的主要功能包括:
内核通知:
当网络事件发生时,例如接口状态变化、路由变化等,内核可以通过Netlink向用户空间发送通知。这样,用户空间的应用程序可以及时了解网络状态的变化,并做出相应的处理。

#include <linux/netlink.h>
#include <linux/rtnetlink.h>/* 假设我们有一个网络接口的回调函数 */
void network_interface_change(struct net_device *dev) {struct sk_buff *skb;struct nlmsghdr *nlh;struct ifinfomsg *ifi;char *data;/* 创建Netlink消息 */skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);if (!skb) {printk(KERN_ERR "Failed to allocate Netlink socket buffer\n");return;}data = skb_put(skb, NLMSG_SPACE(sizeof(*ifi)));nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*ifi), 0);if (!nlh) {printk(KERN_ERR "Failed to create Netlink message\n");kfree_skb(skb);return;}ifi = nlmsg_data(nlh);memset(ifi, 0, sizeof(*ifi));ifi->ifi_family = AF_PACKET;ifi->ifi_change = IFF_UP | IFF_RUNNING; // 设置网络接口状态变化为启用状态memcpy(ifi->ifi_name, dev->name, IFNAMSIZ); // 复制网络接口名称到消息中/* 通过Netlink发送消息到用户空间 */netlink_broadcast(netlink_socket, skb, 0, RTNLGRP_LINK, GFP_KERNEL);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>#define NETLINK_USER 27struct msg_data {struct nlmsghdr nlh;char ifname[IFNAMSIZ]; // 接口名称char status[10]; // 接口状态
};int main(void) {int sockfd;struct sockaddr_nl addr;struct msg_data msg;int status;// 创建Netlink套接字sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);if (sockfd < 0) {perror("socket");exit(EXIT_FAILURE);}// 设置接收数据包的Netlink地址结构体memset(&addr, 0, sizeof(addr));addr.nl_family = AF_NETLINK;addr.nl_pid = getpid(); // 设置接收消息的进程ID为当前进程IDaddr.nl_groups = RTMGRP_LINK; // 设置接收的数据包类型为链接相关的数据包// 绑定套接字到Netlink地址结构体if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("bind");exit(EXIT_FAILURE);}// 接收来自内核的通知消息while (1) {status = recvfrom(sockfd, &msg, sizeof(msg), MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr)); // 接收消息if (status < 0) {perror("recvfrom");exit(EXIT_FAILURE);}if (msg.nlh.nlmsg_type == RTM_NEWLINK) { // 判断消息类型为接口状态变化通知printf("Interface status changed: %s %s\n", msg.ifname, msg.status); // 输出接口名称和状态变化信息// 在这里可以根据接口状态变化进行相应的处理逻辑} else {printf("Received other message type: %d\n", msg.nlh.nlmsg_type); // 处理其他类型的消息}}return 0;
}

用户空间请求:
用户空间的应用程序可以通过Netlink向内核发送请求,例如配置网络接口、修改路由表等。这些请求会被内核接收并处理,然后内核通过Netlink将处理结果返回给用户空间的应用程序。

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <unistd.h>int main() {int sockfd;struct nl_msg *msg;struct nlattr *tb[IFLA_MAX + 1];struct rtmsg *rtm;char buf[4096];ssize_t n;int status;// 创建Netlink套接字sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);if (sockfd < 0) {perror("Failed to create Netlink socket");exit(EXIT_FAILURE);}// 创建Netlink消息msg = nlmsg_alloc();if (!msg) {perror("Failed to allocate Netlink message");exit(EXIT_FAILURE);}// 设置消息类型和标志rtm = (struct rtmsg *)nlmsg_data(nlmsg_hdr(msg));rtm->rtm_family = AF_PACKET;rtm->rtm_ifindex = 0; // 设置要配置的网络接口的索引rtm->rtm_type = RTM_NEWLINK; // 设置请求类型为配置网络接口rtm->rtm_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; // 设置标志为请求、创建和排他性rtm->rtm_msglen = NLMSG_LENGTH(sizeof(struct rtmsg)); // 设置消息长度// 填充消息数据tb[IFLA_IFNAME] = nla_alloc(); // 接口名称属性nla_put_string(tb[IFLA_IFNAME], "eth0"); // 设置接口名称为"eth0"nlmsg_end(msg, tb); // 结束消息结构体// 发送消息到内核status = nl_sendto(sockfd, msg, nlmsg_len(nlmsg_hdr(msg)));if (status < 0) {perror("Failed to send Netlink message");exit(EXIT_FAILURE);}// 接收内核的处理结果while ((n = recv(sockfd, buf, sizeof(buf), 0)) > 0) {status = nlmsg_parse(buf, n, tb, IFLA_MAX, NULL); // 解析消息结构体if (status < 0) {perror("Failed to parse Netlink message");exit(EXIT_FAILURE);}if (tb[IFLA_IFNAME]) { // 检查接口名称属性是否存在printf("Interface %s has been configured.\n", nla_get_string(tb[IFLA_IFNAME])); // 打印配置结果} else {printf("Failed to configure the interface.\n"); // 打印配置失败信息}}close(sockfd); // 关闭Netlink套接字return 0;
}

内核与用户空间的交互:
Netlink提供了一种机制,使得内核和用户空间可以交互地传递数据。例如,当一个网络包到达时,内核可以通过Netlink向用户空间发送一个包含该包数据的消息。用户空间的应用程序可以接收这个消息,并对其进行处理。
内核代码:

#include <linux/netlink.h>
#include <linux/skbuff.h>/* 内核中的代码 */
struct sk_buff *skb;
struct nlmsghdr *nlh;/* 创建Netlink消息 */
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {printk(KERN_ERR "Failed to allocate Netlink socket buffer\n");return;
}nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, 0, 0);
if (!nlh) {printk(KERN_ERR "Failed to create Netlink message\n");kfree_skb(skb);return;
}/* 通过Netlink发送消息到用户空间 */
netlink_send(netlink_socket, skb);

用户空间代码:
在用户空间中,首先需要创建一个Netlink套接字,然后使用recvfrom()函数接收来自内核的消息。以下是一个简单的用户空间代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <string.h>int main() {int sockfd;struct sockaddr_nl addr;struct nlmsghdr *nlh;char buffer[4096];int len;int ret;/* 创建Netlink套接字 */sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);if (sockfd < 0) {perror("Failed to create Netlink socket");exit(EXIT_FAILURE);}/* 配置目标地址 */memset(&addr, 0, sizeof(addr));addr.nl_family = AF_NETLINK;addr.nl_pid = 0; // 设置目标进程ID为0,表示接收来自任意进程的消息。根据需要可以设置特定的PID。addr.nl_groups = RTNLGRP_LINK; // 设置消息组别为链接组别,接收与链接相关的消息。根据需要可以设置其他组别。/* 接收消息 */len = recvfrom(sockfd, buffer, sizeof(buffer), MSG_WAITALL, (struct sockaddr *)&addr, &addr.nl_len);if (len < 0) {perror("Failed to receive Netlink message");exit(EXIT_FAILURE);}/* 处理接收到的消息 */nlh = (struct nlmsghdr *)buffer;while (nlmsg_ok(nlh, len)) {// 处理消息...(根据消息类型和内容进行相应的处理)nlh = nlmsg_next(nlh, &len);}
}

Netlink的使用场景非常广泛,包括但不限于以下几个方面:
网络管理工具:
如ifconfig、ip等命令行工具,它们使用Netlink与内核进行通信,以获取或修改网络接口的状态和配置。

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <unistd.h>int main() {int sockfd;struct nl_msg *msg;struct nlattr *tb[IFLA_MAX + 1];struct rtmsg *rtm;char buf[4096];ssize_t n;int status;// 创建Netlink套接字sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);if (sockfd < 0) {perror("Failed to create Netlink socket");exit(EXIT_FAILURE);}// 创建Netlink消息msg = nlmsg_alloc();if (!msg) {perror("Failed to allocate Netlink message");exit(EXIT_FAILURE);}// 设置消息类型和标志rtm = (struct rtmsg *)nlmsg_data(nlmsg_hdr(msg));rtm->rtm_family = AF_PACKET;rtm->rtm_ifindex = 0; // 设置要配置的网络接口的索引rtm->rtm_type = RTM_NEWLINK; // 设置请求类型为配置网络接口rtm->rtm_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; // 设置标志为请求、创建和排他性rtm->rtm_msglen = NLMSG_LENGTH(sizeof(struct rtmsg)); // 设置消息长度// 填充消息数据tb[IFLA_IFNAME] = nla_alloc(); // 接口名称属性nla_put_string(tb[IFLA_IFNAME], "eth0"); // 设置接口名称为"eth0"nlmsg_end(msg, tb); // 结束消息结构体// 发送消息到内核status = nl_sendto(sockfd, msg, nlmsg_len(nlmsg_hdr(msg)));if (status < 0) {perror("Failed to send Netlink message");exit(EXIT_FAILURE);}// 接收内核的处理结果while ((n = recv(sockfd, buf, sizeof(buf), 0)) > 0) {status = nlmsg_parse(buf, n, tb, IFLA_MAX, NULL); // 解析消息结构体if (status < 0) {perror("Failed to parse Netlink message");exit(EXIT_FAILURE);}if (tb[IFLA_IFNAME]) { // 检查接口名称属性是否存在printf("Interface %s has been configured.\n", nla_get_string(tb[IFLA_IFNAME])); // 打印配置结果} else {printf("Failed to configure the interface.\n"); // 打印配置失败信息}}close(sockfd); // 关闭Netlink套接字return 0;
}

网络监控工具:
如tcpdump、wireshark等,它们使用Netlink接收内核发送的网络包数据,以进行网络流量分析和监控。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>#define BUFFER_SIZE 4096int main() {int sockfd;char buffer[BUFFER_SIZE];struct sockaddr_nl addr;struct nlmsghdr *nlh;struct rtattr *tb[IFLA_MAX + 1];int status;// 创建Netlink套接字sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);if (sockfd < 0) {perror("socket");exit(EXIT_FAILURE);}// 设置接收数据包的Netlink地址结构体memset(&addr, 0, sizeof(addr));addr.nl_family = AF_NETLINK;addr.nl_pid = getpid(); // 设置发送消息的进程IDaddr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; // 设置接收的数据包类型,这里接收链接和IPv4地址相关的数据包// 绑定套接字到Netlink地址结构体if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("bind");exit(EXIT_FAILURE);}// 接收网络包数据while (1) {status = recvfrom(sockfd, buffer, BUFFER_SIZE, MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr));if (status < 0) {perror("recvfrom");exit(EXIT_FAILURE);}// 解析网络包数据,这里只简单地打印消息类型和消息长度,实际应用中需要根据具体需求进行解析和处理nlh = (struct nlmsghdr *)buffer;while (NLMSG_OK(nlh, status)) {printf("Message type: %d\n", nlh->nlmsg_type);printf("Message length: %d\n", nlh->nlmsg_len);nlh = NLMSG_NEXT(nlh, status);}}// 关闭套接字close(sockfd);return 0;
}

网络服务守护进程:
如路由守护进程(routed)和动态主机配置协议(DHCP)服务器等,它们使用Netlink与内核进行通信,以处理网络路由和配置相关的请求。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>#define BUFFER_SIZE 4096int main() {int sockfd;char buffer[BUFFER_SIZE];struct sockaddr_nl addr;struct nlmsghdr *nlh;struct rtmsg *rtm;int status;// 创建Netlink套接字sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);if (sockfd < 0) {perror("socket");exit(EXIT_FAILURE);}// 设置接收数据包的Netlink地址结构体memset(&addr, 0, sizeof(addr));addr.nl_family = AF_NETLINK;addr.nl_pid = getpid(); // 设置发送消息的进程IDaddr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE; // 设置接收的数据包类型,这里接收链接和IPv4路由相关的数据包// 绑定套接字到Netlink地址结构体if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("bind");exit(EXIT_FAILURE);}// 发送路由信息到内核nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(sizeof(*rtm)));memset(nlh, 0, NLMSG_SPACE(sizeof(*rtm)));nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*rtm));nlh->nlmsg_type = RTM_NEWROUTE; // 消息类型为添加路由nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE; // 设置消息标志为请求、创建和替换路由nlh->nlmsg_seq = 1; // 消息序列号rtm = NLMSG_DATA(nlh); // 路由信息结构体指针memset(rtm, 0, sizeof(*rtm));rtm->rtm_family = AF_INET; // 地址族为IPv4rtm->rtm_dst_len = 32; // 目标地址长度为32位(IPv4地址长度)rtm->rtm_src_len = 0; // 源地址长度为0(不指定源地址)rtm->rtm_tos = 0; // 服务类型为0(默认值)rtm->rtm_table = RT_TABLEID(RT_TABLEID_MAIN); // 路由表ID为主表(默认值)rtm->rtm_protocol = RTPROT_BOOT; // 路由协议为BOOTPROTO(默认值)rtm->rtm_scope = RT_SCOPE_UNIVERSE; // 作用域为UNIVERSE(默认值)rtm->rtm_type = RTN_UNICAST; // 路由类型为单播(默认值)strncpy(rtm->rtm_dst, "192.168.1.0", RTA_LEN-1); // 设置目标地址为192.168.1.0/24网段(示例)status = sendto(sockfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); // 发送消息到内核if (status < 0) {perror("sendto");exit(EXIT_FAILURE);}// 接收来自内核的路由信息响应消息while (1) {status = recvfrom(sockfd, buffer, BUFFER_SIZE, MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr)); // 接收消息if (status < 0) {perror("recvfrom");exit(EXIT_FAILURE);}nlh = (struct nlmsghdr *)buffer; // 从接收到的消息中获取NLMSGHDR结构体指针while (NLMSG_OK(nlh, status)) {// 解析消息switch (nlh->nlmsg_type) {case RTM_NEWROUTE:// 处理路由信息printf("Received RTM_NEWROUTE message\n");break;case RTM_DELROUTE:// 处理删除路由信息printf("Received RTM_DELROUTE message\n");break;default:// 处理其他消息类型printf("Received other message type: %d\n", nlh->nlmsg_type);break;}// 移动到下一个消息nlh = NLMSG_NEXT(nlh, status);
}

自定义应用程序:
开发者可以使用Netlink来编写自定义的网络应用程序,以实现特定的网络功能或服务。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>#define NETLINK_USER 27struct msg_data {struct nlmsghdr nlh;char msg[10];
};int main(void) {int sockfd;struct sockaddr_nl addr;struct msg_data msg;int status;// 创建Netlink套接字sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);if (sockfd < 0) {perror("socket");exit(EXIT_FAILURE);}// 设置接收数据包的Netlink地址结构体memset(&addr, 0, sizeof(addr));addr.nl_family = AF_NETLINK;addr.nl_pid = 0; // 设置接收消息的进程ID为0,表示接收来自内核的消息addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE; // 设置接收的数据包类型,这里接收链接和IPv4路由相关的数据包// 绑定套接字到Netlink地址结构体if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("bind");exit(EXIT_FAILURE);}// 发送消息到内核memset(&msg, 0, sizeof(msg));msg.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(msg));msg.nlh.nlmsg_type = NLMSG_USER; // 消息类型为自定义用户消息类型msg.nlh.nlmsg_flags = NLM_F_REQUEST; // 设置消息标志为请求消息msg.nlh.nlmsg_seq = 1; // 消息序列号strncpy(msg.msg, "Hello Netlink", sizeof(msg.msg)); // 设置消息内容为"Hello Netlink"status = sendto(sockfd, &msg, msg.nlh.nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); // 发送消息到内核if (status < 0) {perror("sendto");exit(EXIT_FAILURE);}// 接收来自内核的响应消息status = recvfrom(sockfd, &msg, sizeof(msg), MSG_DONTWAIT, (struct sockaddr *)&addr, sizeof(addr)); // 接收消息if (status < 0) {perror("recvfrom");exit(EXIT_FAILURE);}printf("Received message from kernel: %s\n", msg.msg); // 输出接收到的消息内容return 0;
}

这篇关于Netlink是Linux内核提供的一种用于内核与用户空间进程之间通信的机制。的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/495259

相关文章

数据库oracle用户密码过期查询及解决方案

《数据库oracle用户密码过期查询及解决方案》:本文主要介绍如何处理ORACLE数据库用户密码过期和修改密码期限的问题,包括创建用户、赋予权限、修改密码、解锁用户和设置密码期限,文中通过代码介绍... 目录前言一、创建用户、赋予权限、修改密码、解锁用户和设置期限二、查询用户密码期限和过期后的修改1.查询用

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

MySQL中的锁和MVCC机制解读

《MySQL中的锁和MVCC机制解读》MySQL事务、锁和MVCC机制是确保数据库操作原子性、一致性和隔离性的关键,事务必须遵循ACID原则,锁的类型包括表级锁、行级锁和意向锁,MVCC通过非锁定读和... 目录mysql的锁和MVCC机制事务的概念与ACID特性锁的类型及其工作机制锁的粒度与性能影响多版本

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面