Linux内核 -- Netlink多播组消息处理技术
Netlink多播组消息处理技术文档
概述
Netlink是一种用户态与内核态之间通信的机制,支持单播和多播模式。多播组允许多个用户态进程接收同一组的广播消息,广泛应用于网络事件、系统通知等场景。
本文将详细介绍如何在内核态发送多播组消息,以及用户态如何接收并处理这些消息。
内核态:发送多播组消息
实现步骤
- 创建Netlink套接字:通过
netlink_kernel_create
创建一个Netlink套接字。 - 构建消息:填充
nlmsghdr
头部和数据部分。 - 发送消息:使用
netlink_broadcast
将消息广播到指定多播组。
示例代码
#include <linux/module.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <linux/skbuff.h>
#define NETLINK_USER 31
#define MULTICAST_GROUP1 (1 << 0)
#define MULTICAST_GROUP2 (1 << 1)
static struct sock *nl_sock = NULL;
// 初始化Netlink套接字
static int __init netlink_init(void) {
struct netlink_kernel_cfg cfg = {
.groups = MULTICAST_GROUP1 | MULTICAST_GROUP2, // 支持的多播组
.input = NULL, // 无需接收消息
};
nl_sock = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
if (!nl_sock) {
printk(KERN_ERR "Failed to create Netlink socket\n");
return -ENOMEM;
}
printk(KERN_INFO "Netlink socket created\n");
return 0;
}
// 向多播组发送消息
static void send_multicast_message(const char *message, uint32_t group) {
struct sk_buff *skb;
struct nlmsghdr *nlh;
int msg_size = strlen(message) + 1;
skb = nlmsg_new(msg_size, GFP_KERNEL);
if (!skb) {
printk(KERN_ERR "Failed to allocate sk_buff\n");
return;
}
nlh = nlmsg_put(skb, 0, 0, NLMSG_DONE, msg_size, 0);
strcpy(nlmsg_data(nlh), message);
netlink_broadcast(nl_sock, skb, 0, group, GFP_KERNEL);
printk(KERN_INFO "Message sent to group %u: %s\n", group, message);
}
// 模块加载函数
static int __init netlink_multicast_init(void) {
int ret = netlink_init();
if (ret) return ret;
send_multicast_message("Hello Group 1", MULTICAST_GROUP1);
send_multicast_message("Hello Group 2", MULTICAST_GROUP2);
return 0;
}
// 模块卸载函数
static void __exit netlink_multicast_exit(void) {
if (nl_sock) {
netlink_kernel_release(nl_sock);
printk(KERN_INFO "Netlink socket released\n");
}
}
module_init(netlink_multicast_init);
module_exit(netlink_multicast_exit);
MODULE_LICENSE("GPL");
用户态:接收多播组消息
实现步骤
- 创建Netlink套接字:使用
socket()
创建用户态的Netlink套接字。 - 订阅多播组:通过
setsockopt()
将套接字加入一个或多个多播组。 - 接收消息:使用
recvmsg()
从套接字中接收消息。 - 处理消息:解析
nlmsghdr
头部并处理数据。
示例代码
#include <linux/netlink.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#define NETLINK_USER 31
#define MULTICAST_GROUP1 (1 << 0)
#define MULTICAST_GROUP2 (1 << 1)
#define MAX_PAYLOAD 1024
struct nl_msg {
struct nlmsghdr hdr;
char data[MAX_PAYLOAD];
};
// 初始化Netlink套接字并订阅多播组
int init_netlink_socket(int *sock_fd, uint32_t groups) {
struct sockaddr_nl src_addr;
*sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
if (*sock_fd < 0) {
perror("socket");
return -1;
}
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); // 当前进程PID
src_addr.nl_groups = groups;
if (bind(*sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
perror("bind");
close(*sock_fd);
return -1;
}
if (setsockopt(*sock_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &groups, sizeof(groups)) < 0) {
perror("setsockopt NETLINK_ADD_MEMBERSHIP");
close(*sock_fd);
return -1;
}
return 0;
}
// 动态加入多播组
int join_multicast_group(int sock_fd, uint32_t group) {
if (setsockopt(sock_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
perror("setsockopt NETLINK_ADD_MEMBERSHIP");
return -1;
}
printf("Joined multicast group: %u\n", group);
return 0;
}
// 动态退出多播组
int leave_multicast_group(int sock_fd, uint32_t group) {
if (setsockopt(sock_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group)) < 0) {
perror("setsockopt NETLINK_DROP_MEMBERSHIP");
return -1;
}
printf("Left multicast group: %u\n", group);
return 0;
}
// 接收并处理消息
void receive_messages(int sock_fd) {
struct nl_msg msg;
struct sockaddr_nl src_addr;
struct iovec iov;
struct msghdr msghdr;
while (1) {
memset(&msg, 0, sizeof(msg));
memset(&src_addr, 0, sizeof(src_addr));
memset(&msghdr, 0, sizeof(msghdr));
iov.iov_base = &msg;
iov.iov_len = sizeof(msg);
msghdr.msg_name = &src_addr;
msghdr.msg_namelen = sizeof(src_addr);
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
int ret = recvmsg(sock_fd, &msghdr, 0);
if (ret < 0) {
perror("recvmsg");
break;
}
printf("Received message from group: %u\n", src_addr.nl_groups);
printf("Message: %s\n", msg.data);
}
}
int main() {
int sock_fd;
uint32_t groups = MULTICAST_GROUP1; // 初始订阅组
if (init_netlink_socket(&sock_fd, groups) < 0) {
return -1;
}
// 动态加入组2
join_multicast_group(sock_fd, MULTICAST_GROUP2);
printf("Listening for multicast messages...\n");
receive_messages(sock_fd);
// 动态退出组2
leave_multicast_group(sock_fd, MULTICAST_GROUP2);
close(sock_fd);
return 0;
}
总结
通过以上步骤,内核态可以发送Netlink多播组消息,用户态可以接收并处理这些消息,同时支持动态加入和退出多播组的功能。本文提供的代码可以用于实际开发中,帮助实现高效的内核态与用户态通信。