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

Ubuntu Netlink 套接字使用介绍

Netlink 套接字 是 Linux 特有的一种 IPC(进程间通信)机制,用于用户态进程和内核模块之间的通信。它可以用来完成路由管理、设备通知、网络状态更新等任务。


1. Netlink 的基本工作原理

  • Netlink 是一种双向通信机制。
  • Netlink 消息分为请求和响应:
    • 用户态进程发送请求消息到内核。
    • 内核处理请求并返回响应消息到用户态进程。
    • 也可以由内核主动向用户态进程发送事件通知。

Netlink 的消息通常通过结构体 struct nlmsghdr 包装,消息正文是可选的数据内容。


2. Netlink 的常用 API

Netlink 通信使用的是普通的 BSD 套接字接口:

  • 创建套接字

    int socket(int domain, int type, int protocol);
    
    • domain 使用 AF_NETLINK
    • type 通常为 SOCK_RAWSOCK_DGRAM
    • protocol 指定 Netlink 子系统编号(例如 NETLINK_ROUTE)。
  • 绑定套接字

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
    • 使用 struct sockaddr_nl 作为地址。
  • 发送消息

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
    
  • 接收消息

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    

3. Netlink 示例代码

以下示例演示了如何使用 Netlink 套接字与内核进行简单的通信。

完整代码:用户态程序
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>

#define NETLINK_USER 31  // 自定义 Netlink 协议号(大于 31 时需内核支持)
#define MSG_LEN 1024     // 消息缓冲区大小

int main() {
    // 创建 Netlink 套接字
    int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (sock_fd < 0) {
        std::cerr << "Error creating Netlink socket\n";
        return -1;
    }

    // 本地地址配置
    struct sockaddr_nl src_addr;
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid(); // 绑定到当前进程
    src_addr.nl_groups = 0;     // 不订阅多播

    if (bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
        std::cerr << "Error binding Netlink socket\n";
        close(sock_fd);
        return -1;
    }

    // 目标地址配置(内核)
    struct sockaddr_nl dest_addr;
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;       // 发送到内核
    dest_addr.nl_groups = 0;    // 不订阅多播

    // 构造发送消息
    struct nlmsghdr *nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MSG_LEN));
    memset(nlh, 0, NLMSG_SPACE(MSG_LEN));
    nlh->nlmsg_len = NLMSG_SPACE(MSG_LEN); // 消息长度
    nlh->nlmsg_pid = getpid();             // 发送者 PID
    nlh->nlmsg_flags = 0;                  // 无特殊标志位

    strcpy((char *)NLMSG_DATA(nlh), "Hello from user space!"); // 消息内容

    // 发送消息到内核
    if (sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) {
        std::cerr << "Error sending message\n";
        free(nlh);
        close(sock_fd);
        return -1;
    }

    std::cout << "Message sent to kernel: " << (char *)NLMSG_DATA(nlh) << "\n";

    // 接收内核响应
    memset(nlh, 0, NLMSG_SPACE(MSG_LEN));
    if (recv(sock_fd, nlh, NLMSG_SPACE(MSG_LEN), 0) < 0) {
        std::cerr << "Error receiving message\n";
        free(nlh);
        close(sock_fd);
        return -1;
    }

    std::cout << "Message received from kernel: " << (char *)NLMSG_DATA(nlh) << "\n";

    // 清理资源
    free(nlh);
    close(sock_fd);
    return 0;
}

4. 编写内核模块以响应 Netlink 消息

内核模块代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <net/sock.h>

#define NETLINK_USER 31

struct sock *nl_sk = NULL;

static void netlink_recv_msg(struct sk_buff *skb) {
    struct nlmsghdr *nlh;
    int pid;
    struct sk_buff *skb_out;
    char *msg = "Hello from kernel!";
    int msg_size = strlen(msg);
    int res;

    // 获取 Netlink 消息头部
    nlh = (struct nlmsghdr *)skb->data;
    printk(KERN_INFO "Kernel received message: %s\n", (char *)NLMSG_DATA(nlh));

    pid = nlh->nlmsg_pid; // 获取用户进程 PID

    // 构造响应消息
    skb_out = nlmsg_new(msg_size, 0);
    if (!skb_out) {
        printk(KERN_ERR "Failed to allocate new skb\n");
        return;
    }

    nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
    strncpy(NLMSG_DATA(nlh), msg, msg_size);

    res = nlmsg_unicast(nl_sk, skb_out, pid); // 发送响应
    if (res < 0) {
        printk(KERN_INFO "Error sending message to user\n");
    }
}

static int __init netlink_init(void) {
    struct netlink_kernel_cfg cfg = {
        .input = netlink_recv_msg, // 注册消息接收回调
    };

    nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
    if (!nl_sk) {
        printk(KERN_ALERT "Error creating Netlink socket\n");
        return -10;
    }

    printk(KERN_INFO "Netlink module loaded\n");
    return 0;
}

static void __exit netlink_exit(void) {
    netlink_kernel_release(nl_sk);
    printk(KERN_INFO "Netlink module unloaded\n");
}

module_init(netlink_init);
module_exit(netlink_exit);

MODULE_LICENSE("GPL");

5. 编译和测试

编译内核模块
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
加载模块
sudo insmod netlink_test.ko
运行用户态程序
./user_netlink

6. 输出示例

  • 用户态程序:

    Message sent to kernel: Hello from user space!
    Message received from kernel: Hello from kernel!
    
  • 内核日志(dmesg):

    [INFO] Kernel received message: Hello from user space!
    

总结

通过以上代码,用户态程序和内核模块实现了简单的双向 Netlink 通信。根据实际需求,可以扩展消息内容和通信逻辑。


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

相关文章:

  • Linux中的 read() 函数的介绍及使用实例
  • 本机如何连接虚拟机MYSQL
  • 安装milvus以及向量库增删改操作
  • 跨站脚本攻击的多种方式——以XSS-Labs为例二十关详解解题思路
  • 基于字节大模型的论文翻译(含免费源码)
  • 【ETCD】【Linearizable Read OR Serializable Read】ETCD 数据读取:强一致性 vs 高性能,选择最适合的读取模式
  • Linux之进程相关命令
  • 版本更新导致前端网站资源加载失败:Failed to fetch dynamically imported module
  • 设计模式の享元模板代理模式
  • Redis 基本命令操作指南
  • 解决Ubuntu下蓝牙耳机连接成功但无声音输出问题
  • 开源轮子 - EasyExcel01(核心api)
  • 高超声速技术对于无人机的推进!
  • FFmpeg第二话:FFmpeg 主要结构体剖析
  • 洛谷 P1886:滑动窗口 ← 单调队列(STL queue)
  • 【计算机网络课程设计】校园网规划与设计
  • 【原生js案例】让你的移动页面实现自定义的上拉加载和下拉刷新
  • 贪心算法在背包问题上的运用(Python)
  • mysql免安装版配置教程
  • 数据结构 (数组和矩阵,初级动态规划)
  • Ubuntu 环境安装 之 RabbitMQ 快速入手
  • 【学习笔记】数据结构(八)
  • 三七互娱Java开发150道面试题及参考答案(下)
  • Spring Boot 启动后的初始化数据加载原理解析与实战应用
  • Springmvc,spring ,mybatis,整合,ssm
  • Reactor