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 1024struct 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(); // 当前进程PIDsrc_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;}// 动态加入组2join_multicast_group(sock_fd, MULTICAST_GROUP2);printf("Listening for multicast messages...\n");receive_messages(sock_fd);// 动态退出组2leave_multicast_group(sock_fd, MULTICAST_GROUP2);close(sock_fd);return 0;
}
总结
通过以上步骤,内核态可以发送Netlink多播组消息,用户态可以接收并处理这些消息,同时支持动态加入和退出多播组的功能。本文提供的代码可以用于实际开发中,帮助实现高效的内核态与用户态通信。