请选择 进入手机版 | 继续访问电脑版
查看: 1148|回复: 0

[技术交流] 内核通信之 Netlink 源码分析和实例分析(4)

[复制链接]

185

主题

204

帖子

596

积分

利尔达员工

Rank: 9Rank: 9Rank: 9

积分
596
发表于 2020-7-29 16:28:24 | 显示全部楼层 |阅读模式
测试例子代码netlink 内核建立 socket 过程
内核的代码非常简单,这里给出了核心代码,就这么多,接收函数中直接打印了接收到的消息。
#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <net/sock.h>#include <asm/types.h>#include <linux/netlink.h>#include <linux/skbuff.h>
#define NETLINK_XUX           31       /* testing */  
static struct sock *xux_sock = NULL;

// 接收消息的回调函数,接收参数是 sk_buff
static void recv_netlink(struct sk_buff *skb)
{
    struct nlmsghdr *nlh;
    nlh = nlmsg_hdr(skb); // 取得消息体
    printk("receive data from user process: %s", (char *)NLMSG_DATA(nlh)); // 打印接收的数据内容

    ...
}

int __init init_link(void)
{
    struct netlink_kernel_cfg cfg = {
                .input = recv_netlink,
        };
    xux_sock = netlink_kernel_create(&init_net, NETLINK_XUX, &cfg); // 创建内核 socket
    if (!xux_sock){
        printk("cannot initialize netlink socket");
        return -1;
    }
   
    printk("Init OK!\n");
    return 0;
}
netlink 用户态建立链接和收发信息... // 上面的就省了
#define NETLINK_USER 31  //self defined
#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct msghdr msg;
struct iovec iov;
int sock_fd;

int main(int args, char *argv[])
{
    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER); // 建立 socket

    if(sock_fd < 0)
        return -1;

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid(); /* 当前进程的 pid */

    if(bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr))){ // 和指定协议进行 socket 绑定
        perror("bind() error\n");
        close(skfd);
        return -1;
    }

    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;       /* For Linux Kernel */
    dest_addr.nl_groups = 0;    /* unicast */

    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = getpid();  //self pid
    nlh->nlmsg_flags = 0;
    // 拷贝信息到发送缓冲中
    strcpy(NLMSG_DATA(nlh), "Hello this is a msg from userspace");
    // 构造发送消息体
    iov.iov_base = (void *)nlh;         //iov -> nlh
    iov.iov_len = nlh->nlmsg_len;
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;  // iov 中存放 netlink 消息头和消息数据
    msg.msg_iovlen = 1;

    printf("Sending message to kernel\n");

    int ret = sendmsg(sock_fd, &msg, 0);  // 发送消息到内核
    printf("send ret: %d\n", ret);

    printf("Waiting for message from kernel\n");

    /* 从内核接收消息 */
    recvmsg(sock_fd, &msg, 0);
    printf("Received message payload: %s\n", NLMSG_DATA(nlh));  // 打印接收到的消息

    close(sock_fd);
    return 0;
}
以上代码在我的个人仓库中都有,如果有兴趣可以 clone 下来自己测试玩耍一遍。代码仓库:https://github.com/helight/kerne ... master/netlink_test
总结
netlink 目前感觉还是一个比较好用的内核和用户空间的交互方式,但是也是有他的使用场景,适合用户空间和内核空间主动交互的场景。
但是在单机场景下,大多数的主动权在用户进程,用户进程写数据到内核,用户进程主动读取内核数据。这两种场景覆盖了内核的绝大多数场景。
在内核要主动的场景下,netlink 就比较适合。我能想到的就是内核数据审计,安全触发等,这类场景下内核可以实时的告知用户进程内核发生的情况。
我是在看 ipvs 的代码时候看到了里面有 netlink 的使用,发现早期 iptables 就是使用 netlink 来下发配置指令的,内核中 netfilter 和 iptables 中还有这部分的代码,今天也顺便下载大致走读了一遍,大家可以搜索 NETLINK 这个关键字来看。但是 iptables 后来的代码中没有使用这样的方式,而是采用了一个叫做 iptc 的库,其核心思路还是使用 setsockops 的方式,最终还是 copy_from_user。不过这种方式对于 iptables 这种配置下发的场景来说还是非常实用的。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表