当前位置: 首页 > article >正文

Linux内核 -- Netlink多播组消息处理技术

Netlink多播组消息处理技术文档

概述

Netlink是一种用户态与内核态之间通信的机制,支持单播和多播模式。多播组允许多个用户态进程接收同一组的广播消息,广泛应用于网络事件、系统通知等场景。

本文将详细介绍如何在内核态发送多播组消息,以及用户态如何接收并处理这些消息。


内核态:发送多播组消息

实现步骤

  1. 创建Netlink套接字:通过netlink_kernel_create创建一个Netlink套接字。
  2. 构建消息:填充nlmsghdr头部和数据部分。
  3. 发送消息:使用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");

用户态:接收多播组消息

实现步骤

  1. 创建Netlink套接字:使用socket()创建用户态的Netlink套接字。
  2. 订阅多播组:通过setsockopt()将套接字加入一个或多个多播组。
  3. 接收消息:使用recvmsg()从套接字中接收消息。
  4. 处理消息:解析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多播组消息,用户态可以接收并处理这些消息,同时支持动态加入和退出多播组的功能。本文提供的代码可以用于实际开发中,帮助实现高效的内核态与用户态通信。


http://www.kler.cn/a/465893.html

相关文章:

  • STM32-笔记30-编程实现esp8266联网功能
  • Unity-Mirror网络框架-从入门到精通之Benchmark示例
  • [python SQLAlchemy数据库操作入门]-19.使用复合条件构建复杂查询
  • 猴子吃桃.
  • Golang的并发编程实战经验
  • 【2024最新】基于Python+Mysql+Django+Vue网上商城的设计与实现Lw+PPT
  • AI 自动化编程:现状、挑战与未来发展
  • STM32 和 ESP32
  • 打开idea开发软件停留在加载弹出框页面进不去
  • 蛋白互作组学系列丨(三)IP-MS方案设计
  • 多层设计模式:可否设计各层之间公用的数据定义模块?
  • ubuntu24.04使用open-vm-tools无法在主机和虚拟机之间拖拽文件夹
  • 本地测试文件解析
  • Qt 5.14.2 学习记录 —— 사 信号与槽机制(1)
  • 黑马JavaWeb开发跟学(十四).SpringBootWeb原理
  • 基于Swarm的大模型应用:一个天气助手
  • uniapp Stripe 支付
  • 解决ubuntu服务器SSH连接卡顿,连上后命令输入也是卡顿以及如何在Ubuntu上杀死不断重启的进程。
  • 【连续3届JPCS出版,EI稳定检索!】第四届能源利用与自动化国际学术会议(ICEUA 2025)
  • [cg] android studio 无法调试cpp问题