Linux netlink用户态和内核态间通信

news/2024/11/24 11:56:32/

文章目录

  • 前言
  • 一、内核态编程
  • 二、用户态编程
  • 三、代码测试结果

前言

请参考:Linux 网络之netlink 简介

一、内核态编程

用了 Netlink Socket 库中的函数和数据结构,通过 Netlink Socket 接收消息并将消息发送回用户空间进程。

代码说明:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/sock.h>#define NETLINK_TEST 25
#define MSG_SIZE 100static struct sock *nl_sk = NULL;int send_nl_message(struct sock *sock, int pid, char *message, int message_len) 
{struct sk_buff *skb_out;struct nlmsghdr *nlh_out;// Allocate a new skb// 创建一个 sk_buff 缓冲区skb_out = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);if (!skb_out) {return -ENOMEM;}// Initialize the netlink message headernlh_out = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, message_len, 0);if (!nlh_out) {nlmsg_free(skb_out);return -ENOMEM;}// Set the destination PID and copy the message dataNETLINK_CB(skb_out).dst_group = 0; /* unicast */memcpy(NLMSG_DATA(nlh_out), message, message_len);// Send the message to the specified PID// 向指定的 Netlink Socket 发送消息if (nlmsg_unicast(sock, skb_out, pid) < 0) {nlmsg_free(skb_out);return -EFAULT;}return 0;
}static void nl_recv_msg(struct sk_buff *__skb)
{struct sk_buff *skb;char str[MSG_SIZE] = {0};struct nlmsghdr *nlh;if (__skb == NULL) {return;}skb = skb_get(__skb);if (skb->len < NLMSG_SPACE(0)) {return;}nlh = nlmsg_hdr(skb);memset(str, 0, sizeof(str));memcpy(str, NLMSG_DATA(nlh), sizeof(str));printk(KERN_INFO "receive message (pid:%d):%s\n", nlh->nlmsg_pid, str);// Send message back to user space processif (send_nl_message(nl_sk, nlh->nlmsg_pid, str, strlen(str)) < 0) {printk(KERN_ERR "Failed to send message to user space process\n");}
}static int __init netlink_init(void)
{struct netlink_kernel_cfg cfg = {.input = nl_recv_msg,};nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);if (!nl_sk) {printk(KERN_ALERT "Failed to create netlink socket.\n");return -ENOMEM;}printk(KERN_INFO "Netlink socket created.\n");return 0;
}static void __exit netlink_exit(void)
{netlink_kernel_release(nl_sk);printk(KERN_INFO "Netlink socket released.\n");
}module_init(netlink_init);
module_exit(netlink_exit);MODULE_LICENSE("GPL");

(1)send_nl_message: 该函数用于向指定的 Netlink Socket 发送消息。它创建了一个 sk_buff 缓冲区,并使用 nlmsg_put 函数初始化了消息头部。然后,它将目标 PID 设置为指定的 PID,并将消息数据复制到消息数据缓冲区中。最后,它使用 nlmsg_unicast 函数将消息发送到指定的 Netlink Socket。

(2)nl_recv_msg: 该函数是 Netlink Socket 的回调函数,当内核收到消息时,会自动调用该函数。它接收到一个 sk_buff 缓冲区,并从中提取出消息数据。然后,它打印出消息数据和发送者的 PID,并使用 send_nl_message 函数将消息发送回用户空间进程。

(3)netlink_init: 该函数是内核模块的初始化函数。它使用 netlink_kernel_create 函数创建了一个 Netlink Socket,并将回调函数指定为 nl_recv_msg 函数。如果创建失败,该函数将返回一个负数错误码。

(4)netlink_exit: 该函数是内核模块的清理函数。它使用 netlink_kernel_release 函数释放了之前创建的 Netlink Socket。

在模块初始化时,我们使用 netlink_kernel_create 函数创建了一个 Netlink Socket。该函数接收一个 netlink_kernel_cfg 结构体参数,其中包含了一个回调函数指针。我们将回调函数指定为 nl_recv_msg 函数,以便在内核接收到消息时自动调用该函数。

在 nl_recv_msg 函数中,我们从 sk_buff 缓冲区中提取出消息数据,并将其打印到内核日志中。然后,我们使用 send_nl_message 函数将消息发送回用户空间进程。

在清理函数中,我们使用 netlink_kernel_release 函数释放之前创建的 Netlink Socket。

二、用户态编程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>#define NETLINK_TEST 25
#define MSG_SIZE 100#define MAX_PAYLOAD  1024int netlink_recv_message(int sock_fd, unsigned char *message, int *len)
{if(message == NULL|| len == NULL) {return -1;}//create messagestruct nlmsghdr *nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));if( !nlh ) {perror("malloc");return -1;}struct sockaddr_nl src_addr;socklen_t addrlen = sizeof(struct sockaddr_nl);memset(&src_addr, 0, addrlen);if(recvfrom(sock_fd, nlh, NLMSG_SPACE(MAX_PAYLOAD), 0, (struct sockaddr *)&src_addr, (socklen_t *)&addrlen) < 0 ) {printf("recvmsg error!\n");free(nlh);return -1;}*len = nlh->nlmsg_len - NLMSG_SPACE(0);memcpy(message, (unsigned char *)NLMSG_DATA(nlh), *len);free(nlh);return 0;
}int main() {int sock_fd, len;struct sockaddr_nl src_addr, dst_addr;struct nlmsghdr *nlh;char msg[MSG_SIZE] = {0};sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);if (sock_fd < 0) {perror("socket");exit(EXIT_FAILURE);}memset(&src_addr, 0, sizeof(struct sockaddr_nl));src_addr.nl_family = AF_NETLINK;src_addr.nl_pid = getpid();bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(struct sockaddr_nl));memset(&dst_addr, 0, sizeof(struct sockaddr_nl));dst_addr.nl_family = AF_NETLINK;dst_addr.nl_pid = 0; /* send to kernel */dst_addr.nl_groups = 0; /* unicast */nlh = (struct nlmsghdr*)malloc(NLMSG_SPACE(MSG_SIZE));memset(nlh, 0, NLMSG_SPACE(MSG_SIZE));nlh->nlmsg_len = NLMSG_SPACE(MSG_SIZE);nlh->nlmsg_pid = getpid();nlh->nlmsg_flags = 0;char buf[MAX_PAYLOAD] = {0};while(1){printf("Enter message: ");fgets(msg, MSG_SIZE, stdin);len = strlen(msg);msg[len-1] = '\0';if(strncmp(msg, "quit", 4) == 0){break;}memcpy(NLMSG_DATA(nlh), msg, len);if (sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr*)&dst_addr, sizeof(struct sockaddr_nl)) < 0) {perror("sendto");exit(EXIT_FAILURE);}if(netlink_recv_message(sock_fd, buf, &len) == 0 ) {printf("recv:%s len:%d\n", buf, len);}memset(buf, 0, MAX_PAYLOAD);}free(nlh);close(sock_fd);return 0;
}

它创建了一个 Netlink Socket,并使用 sendto 函数向内核发送消息。而且还使用 recvfrom 函数从内核接收消息。

在主函数中,我们使用 socket 函数创建了一个 AF_NETLINK 类型的套接字,并将其绑定到当前进程的 PID 上。然后,我们准备发送消息,定义了一个 nlmsghdr 结构体并将其初始化。将消息数据写入 nlmsghdr 结构体中,然后使用 sendto 函数向内核发送消息。

接下来,我们使用 recvfrom 函数从内核接收消息。该函数从套接字中读取一个 nlmsghdr 结构体,然后从 nlmsghdr 结构体中提取出消息数据。最后,将消息数据存储到指定的缓冲区中。

在循环中,我们读取用户输入的消息,并将其发送到内核。如果用户输入 quit,则退出循环。

三、代码测试结果

加载内核模块驱动,然后运行应用层程序:

# dmesg -w
[15492.193242] receive message (pid:5561):abcd
[15495.152386] receive message (pid:5561):sddffg
[15496.999417] receive message (pid:5561):qweqweqweq
[15499.649971] receive message (pid:5561):asdasdasd
[15505.720225] receive message (pid:5561):zsdqweq
# ./a.out
Enter message: abcd
recv:abcd len:4
Enter message: sddffg
recv:sddffg len:6
Enter message: qweqweqweq
recv:qweqweqweq len:10
Enter message: asdasdasd
recv:asdasdasd len:9
Enter message: zsdqweq
recv:zsdqweq len:7
Enter message: quit

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

相关文章

进制的前缀表示

二进制&#xff1a;0b或0B开头表示一个二进制数 例如&#xff1a;0b1001 的十进制为 9 八进制&#xff1a;0开头表示一个八进制数 例如&#xff1a;010 的十进制为 8 十六进制&#xff1a;0x或0X开头表示一个十六进制数 例如&#xff1a;0x1A 的十进制为 26

CAN2.0A 和CAN2.0B

CAN2.0A 是CAN协议的PART A部分&#xff0c;此部分定义了11bit的标识区 。 CAN2.0B 是CAN协议的扩展部分&#xff0c;也叫PART B&#xff0c;定义了29bit的标识区&#xff0c;其它部分与CAN2.0A一样。 CANOpen是基于CAN协议的应用层协议&#xff0c;可以理解为用户层&#xff…

C#代码中用UL 0B 0X等前后缀表示数字

代码中不同的数值表示法官方文档都有&#xff0c;但这东西不常用&#xff0c;要用时又要专门翻下文档&#xff0c;所以整个博客记录整理一下 数字表示 官方文档-整数&#xff1a;https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/builtin-types/integral-nu…

【NVMe2.0b 12】NVM 容量模型

NVM 容量模型 3.8NVM容量模型3.8.1概述3.8.2Media Unit的组织示例3.8.2.1简单的NVM子系统3.8.2.3横向组织的双NAND NVM子系统 3.8.3容量报告 3.8NVM容量模型 3.8.1概述 NVM 子系统可以报告 NVM 子系统内多个实体的容量相关信息。此容量报告模型包括 NVM subsystem、域&#x…

【NVMe2.0b 7】NVMe 基本队列数据结构

NVMe 队列数据结构与命令仲裁机制 3.3.3Queueing Data Structures3.3.3.1Submission Queue Entry3.3.3.2Common Completion Queue Entry3.3.3.2.1Status Field 定义3.3.3.2.1.1Generic Command Status Definition3.3.3.2.1.2Command Specific Status 定义3.3.3.2.1.3Media and …

【NVMe2.0b 16-1】Get Log Page

目录 5.16Get Log Page command5.16.1Log Specific Information 5.16Get Log Page command Get Log Page 命令返回包含请求的 log page 的数据缓冲区。Get Log Page 命令可能会受到 ANA 状态的影响&#xff08;请参阅第 8.1.4 节&#xff09;。 Get Log Page 命令使用 Data P…

【NVMe2.0b 6】NVMe 队列模型

NVMe 队列模型 3.3NVM Queue Models3.3.1基于内存的传输队列模型3.3.1.1队列设置与初始化3.3.1.2Queue的使用3.3.1.2.1Completion Queue Flow Control 3.3.1.3Queue Abort3.3.1.4Empty Queue3.3.1.5Full Queue 3.3.2基于消息的传输队列模型3.3.2.1胶囊和数据传输3.3.2.1.1Comma…

AB=0

一个很有趣的知识点。 ABO时&#xff1a;将B进行列分块&#xff0c;B(β1,β2,…,βn) ABA (β1,β2,…,βn)(O,O,O,O,…,O) 从而&#xff0c;AβiO, i1,2,…,n 即βi是方程组Ax0的解 则&#xff0c;向量组β1,β2,…,βn可由Ax0的基础解系线性表出。所以r( (β1,β2,…,β…