加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

通过C中的Netlink从内核到用户空间的多播

发布时间:2020-12-16 09:51:23 所属栏目:百科 来源:网络整理
导读:我试图编写一个使用Netlink在内核和用户空间之间进行通信的简单程序.基本上这就是我想要实现的目标: 用户空间程序开始绑定到用户定义的多播组. 插入内核模块 内核模块向此多播组发送消息 用户空间程序接收消息 这是我的代码: ======用户空间程序====== #in
我试图编写一个使用Netlink在内核和用户空间之间进行通信的简单程序.基本上这就是我想要实现的目标:

>用户空间程序开始绑定到用户定义的多播组.
>插入内核模块
>内核模块向此多播组发送消息
>用户空间程序接收消息

这是我的代码:

======用户空间程序======

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<linux/netlink.h>
#include<sys/types.h>
#include<unistd.h>

#define MYPROTO NETLINK_USERSOCK
#define MYMGRP 0x21 //User defined group,consistent in both kernel prog and user prog

int open_netlink()
{
        int sock = socket(AF_NETLINK,SOCK_RAW,MYPROTO);
        struct sockaddr_nl addr;

        memset((void *)&addr,sizeof(addr));

        if (sock<0)
                return sock;
        addr.nl_family = AF_NETLINK;
        addr.nl_pid = getpid();
        addr.nl_groups = MYMGRP;
        if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0)
                return -1;
        return sock;
}

int read_event(int sock)
{
        struct sockaddr_nl nladdr;
        struct msghdr msg;
        struct iovec iov[2];
        struct nlmsghdr nlh;
        char buffer[65536];
        int ret;
        iov[0].iov_base = (void *)&nlh;
        iov[0].iov_len = sizeof(nlh);
        iov[1].iov_base = (void *)buffer;
        iov[1].iov_len = sizeof(buffer);
        msg.msg_name = (void *)&(nladdr);
        msg.msg_namelen = sizeof(nladdr);
        msg.msg_iov = iov;
        msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
        ret=recvmsg(sock,&msg,0);
        if (ret<0) {
                return ret;
        }
        printf("Received message payload: %sn",NLMSG_DATA(&nlh));
}

int main(int argc,char *argv[])
{
        int nls = open_netlink();
        if (nls<0) {
                err(1,"netlink");
        }

        while (1)
                read_event(nls);
        return 0;
}

======内核模块======

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <net/sock.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <linux/delay.h>

#define NETLINK_USER 31
#define MYGRP 0x21 //User defined group,consistent in both kernel prog and user prog

struct sock *nl_sk = NULL;

static void send_to_user() {
    struct sk_buff *skb_out;
    struct nlmsghdr *nlh;
    int msg_size;
    char *msg = "Hello from kernel";
    int res;

    printk(KERN_INFO "Entering: %sn",__FUNCTION__);
    msg_size = strlen(msg);
    skb_out = nlmsg_new(msg_size,0);

    if (!skb_out) {
        printk(KERN_ERR "Failed to allocate new skbn");
        return;
    }
    nlh = nlmsg_put(skb_out,1,NLMSG_DONE,msg_size,0);
    //NETLINK_CB(skb_out).dst_group = 1; /* Multicast to group 1,1<<0 */
    strncpy(nlmsg_data(nlh),msg,msg_size);

    res = nlmsg_multicast(nl_sk,skb_out,MYGRP,0);
    if (res < 0) {
        printk(KERN_INFO "Error while sending bak to user,err id: %dn",res);
    }
}

static int __init
hello_init(void) {

    struct netlink_kernel_cfg cfg = {
            .groups = MYGRP,};
    printk("Entering: %sn",__FUNCTION__);
    nl_sk = netlink_kernel_create(&init_net,NETLINK_USER,&cfg);
    if (!nl_sk) {
        printk(KERN_ALERT "Error creating socket.n");
        return -10;
    }

    send_to_user();

    return 0;
}

static void __exit
hello_exit(void) {

    printk(KERN_INFO "exiting hello modulen");
    netlink_kernel_release(nl_sk);
}

module_init(hello_init);
module_exit(hello_exit);

由于内核模块只会在初始化期间发送一次消息,因此我首先运行监听程序然后插入模块,尽管我总是遇到这个错误:

Error while sending bak to user,err id: -3

追踪到错误ID时,它反映在netlink / af_netlink.c中的这段代码中:

if (info.delivery_failure) {
    kfree_skb(info.skb2);
    return -ENOBUFS;
}
consume_skb(info.skb2);

if (info.delivered) {
    if (info.congested && (allocation & __GFP_WAIT))
    yield();
    return 0;
}
return -ESRCH;

我认为它不是delivery_failure,但由于某些原因仍未提供.

我指的是这个example,其中作者的程序一直在改变监听路线.虽然我想使用用户定义的多播组.

有任何想法吗?提前致谢!

解决方法

这些是我在代码中发现的两个关键问题:

>协议族和组播组都需要在内核prog和用户prog中保持一致.您的协议系列是用户空间中的NETLINK_USERSOCK(2)和内核空间中的NETLINK_USER(31).
> addr.nl_groups = MYMGRP;由于某种原因不起作用.但这确实如下:setsockopt(sock,270,NETLINK_ADD_MEMBERSHIP,& group,sizeof(group)).

不致命:

>在这种情况下,模块不会侦听组消息,因此您不需要在netlink_kernel_create()参数中包含多播组.

此外,并非真正与netlink相关,但有用:

> strlen()不包含null chara.在消息分配期间,您应该添加一个字节来弥补这一点.
>在这种情况下,NLMSG_DATA(& nlh)是未定义的行为.这是因为你的标题和数据都在单独的内存块中,不能保证被粘合,并且所有宏都在nlh之后访问内存块:

#define NLMSG_DATA(nlh)((void *)(((char *)nlh)NLMSG_LENGTH(0)))

这是我的代码版本:

用户空间程序:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>

/* Protocol family,consistent in both kernel prog and user prog. */
#define MYPROTO NETLINK_USERSOCK
/* Multicast group,consistent in both kernel prog and user prog. */
#define MYMGRP 21

int open_netlink(void)
{
    int sock;
    struct sockaddr_nl addr;
    int group = MYMGRP;

    sock = socket(AF_NETLINK,MYPROTO);
    if (sock < 0) {
        printf("sock < 0.n");
        return sock;
    }

    memset((void *) &addr,sizeof(addr));
    addr.nl_family = AF_NETLINK;
    addr.nl_pid = getpid();
    /* This doesn't work for some reason. See the setsockopt() below. */
    /* addr.nl_groups = MYMGRP; */

    if (bind(sock,(struct sockaddr *) &addr,sizeof(addr)) < 0) {
        printf("bind < 0.n");
        return -1;
    }

    /*
     * 270 is SOL_NETLINK. See
     * http://lxr.free-electrons.com/source/include/linux/socket.h?v=4.1#L314
     * and
     * https://stackoverflow.com/questions/17732044/
     */
    if (setsockopt(sock,&group,sizeof(group)) < 0) {
        printf("setsockopt < 0n");
        return -1;
    }

    return sock;
}

void read_event(int sock)
{
    struct sockaddr_nl nladdr;
    struct msghdr msg;
    struct iovec iov;
    char buffer[65536];
    int ret;

    iov.iov_base = (void *) buffer;
    iov.iov_len = sizeof(buffer);
    msg.msg_name = (void *) &(nladdr);
    msg.msg_namelen = sizeof(nladdr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    printf("Ok,listening.n");
    ret = recvmsg(sock,0);
    if (ret < 0)
        printf("ret < 0.n");
    else
        printf("Received message payload: %sn",NLMSG_DATA((struct nlmsghdr *) &buffer));
}

int main(int argc,char *argv[])
{
    int nls;

    nls = open_netlink();
    if (nls < 0)
        return nls;

    while (1)
        read_event(nls);

    return 0;
}

这是内核模块:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <net/netlink.h>
#include <net/net_namespace.h>

/* Protocol family,consistent in both kernel prog and user prog. */
#define MYGRP 21

static struct sock *nl_sk = NULL;

static void send_to_user(void)
{
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    char *msg = "Hello from kernel";
    int msg_size = strlen(msg) + 1;
    int res;

    pr_info("Creating skb.n");
    skb = nlmsg_new(NLMSG_ALIGN(msg_size + 1),GFP_KERNEL);
    if (!skb) {
        pr_err("Allocation failure.n");
        return;
    }

    nlh = nlmsg_put(skb,msg_size + 1,0);
    strcpy(nlmsg_data(nlh),msg);

    pr_info("Sending skb.n");
    res = nlmsg_multicast(nl_sk,skb,GFP_KERNEL);
    if (res < 0)
        pr_info("nlmsg_multicast() error: %dn",res);
    else
        pr_info("Success.n");
}

static int __init hello_init(void)
{
    pr_info("Inserting hello module.n");

    nl_sk = netlink_kernel_create(&init_net,MYPROTO,NULL);
    if (!nl_sk) {
        pr_err("Error creating socket.n");
        return -10;
    }

    send_to_user();

    netlink_kernel_release(nl_sk);
    return 0;
}

static void __exit hello_exit(void)
{
    pr_info("Exiting hello module.n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

在内核3.13中测试过.

(我可以建议人们使用libnl-3而不是原始套接字用于用户空间程序.它的多播Netlink文档是actually decent.)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读