本文介绍了内核模块可以通过netlink主动向用户空间发送消息吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试运行以下代码,该代码是从此处复制的.我进行了一些更改,以在较早的内核版本中运行它.

I am trying to run following code, which was copied from here. I have made few changes to run it with older kernel versions.

当我插入内核模块时,nlmsg_multicast()失败,并在/var/log/messages中记录为nlmsg_multicast() error: -3.运行用户空间程序时,socket()失败.

When I insert kernel module, nlmsg_multicast() fails and logs as nlmsg_multicast() error: -3 in /var/log/messages.While running user space program, socket() fails.

我到底想做什么,

  • 内核模块创建套接字,而不考虑用户空间中的任何进程
  • 内核模块向用户空间发送一些事件
  • 如果用户空间中有任何进程答复事件,则内核模块根据该答复进行处理

因此,可能会发生用户空间中没有可用于事件响应的进程,即使在这种情况下,模块也必须发送事件并等待一段时间才能响应.

Since, It may happen that no process in user space available to reply on event, even in that case module must send event and wait for a while for response.

是否可以将第一条消息从内核模块发送到用户空间中的进程?我该怎么办?

Is it possible to send first message from kernel module to a process in user space? How can I do this?

内核模块代码:

制作文件

obj-m   := foo.o

KDIR    := /lib/modules/$(shell uname -r)/build
PWD    := $(shell pwd)

default:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean

foo.c

#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 MYPROTO NETLINK_USERSOCK
/* Multicast group, 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, 0, 1, NLMSG_DONE, msg_size + 1, 0);
        strcpy(nlmsg_data(nlh), msg);

        pr_info("Sending skb.\n");
        res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_KERNEL);
        if (res < 0)
                pr_info("nlmsg_multicast() error: %d\n", 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);
        nl_sk = netlink_kernel_create(&init_net, MYPROTO, 0, NULL, NULL, THIS_MODULE);
        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");

用户空间程序:(与gcc somename.c一起编译)

#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, SOCK_RAW, MYPROTO);
        if (sock < 0) {
                printf("sock < 0.\n");
                return sock;
        }

        memset((void *) &addr, 0, 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, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
                printf("setsockopt < 0\n");
                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, &msg, 0);
        if (ret < 0)
                printf("ret < 0.\n");
        else
                printf("Received message payload: %s\n", 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;
}

谢谢您的时间!

推荐答案

这看起来像是糟糕的设计(因为上层应该依赖于下层,而不是相反).但是,如果您确信内核无法闲置或使用默认配置运行,直到用户空间可以获取信息,那么请首先安装此工具(可能想阅读核心指南),然后执行以下操作:

This looks like bad design (because upper layers should depend on lower layers, not the other way around). But if you're convinced the kernel cannot sit idle or operate using default configuration until userspace can fetch info, then first install this tool (might want to read the core guide too), and then do something like this:

内核:

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

#define MYPROTO NETLINK_USERSOCK
#define MYGRP 22

static struct sock *nl_sk;
static struct timer_list timer;

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

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

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

    pr_info("Sending multicast.\n");
    res = nlmsg_multicast(nl_sk, skb, 0, MYGRP, GFP_ATOMIC);
    if (res < 0) {
        pr_info("nlmsg_multicast() error: %d. Will try again later.\n", res);
        /* Wait 1 second. */
        mod_timer(&timer, jiffies + msecs_to_jiffies(1000));
    } else {
        pr_info("Success.\n");
    }
}

static int handle_netlink_message(struct sk_buff *skb_in, struct nlmsghdr *nl_hdr)
{
    char *hello;
    hello = NLMSG_DATA(nl_hdr);
    pr_info("Userspace says '%s.'\n", hello);
    return 0;
}

static void receive_answer(struct sk_buff *skb)
{
    netlink_rcv_skb(skb, &handle_netlink_message);
}

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

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

    init_timer(&timer);
    timer.function = try_send;
    timer.expires = jiffies + 1000;
    timer.data = 0;
    add_timer(&timer);

    return 0;
}

static void __exit hello_exit(void)
{
    del_timer_sync(&timer);
    netlink_kernel_release(nl_sk);
    pr_info("Exiting module.\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

用户(我正在使用gcc usr.c -I/usr/include/libnl3 -lnl-3 -Wall进行编译,您的行驶里程可能会有所不同):

User (I'm compiling using gcc usr.c -I/usr/include/libnl3 -lnl-3 -Wall, your mileage may vary):

#include <netlink/netlink.h>
#include <netlink/msg.h>

#define MYPROTO NETLINK_USERSOCK
#define MYMGRP 22

struct nl_sock *sk;

void respond_to_kernel(void)
{
    char *response = "foo bar";
    int error;

    error = nl_send_simple(sk, 12345, NLMSG_DONE, response, strlen(response) + 1);
    if (error < 0) {
        printf("nl_send_simple() threw errcode %d.\n", error);
        printf("libnl's message: %s", nl_geterror(error));
    } else {
        printf("Responded %d bytes.\n", error);
    }
}

int receive_kernel_request(struct nl_msg *msg, void *arg)
{
    char *hello;

    hello = nlmsg_data(nlmsg_hdr(msg));
    printf("Kernel says '%s'.\n", hello);
    respond_to_kernel();

    return 0;
}

int prepare_socket(void)
{
    int error;

    sk = nl_socket_alloc();
    if (!sk) {
        printf("nl_socket_alloc() returned NULL.\n");
        return -1;
    }

    nl_socket_disable_seq_check(sk);

    error = nl_socket_modify_cb(sk, NL_CB_FINISH, NL_CB_CUSTOM, receive_kernel_request, NULL);
    if (error < 0) {
        printf("Could not register callback function. Errcode: %d\n", error);
        goto fail;
    }

    error = nl_connect(sk, MYPROTO);
    if (error < 0) {
        printf("Connection failed: %d\n", error);
        goto fail;
    }

    error = nl_socket_add_memberships(sk, MYMGRP, 0);
    if (error) {
        printf("Could not register to the multicast group. %d\n", error);
        goto fail;
    }

    return 0;

fail:
    printf("libnl's message: %s\n", nl_geterror(error));
    nl_socket_free(sk);
    return error;
}

int wait_for_kernel_message(void)
{
    int error;

    printf("Waiting for kernel request...\n");
    error = nl_recvmsgs_default(sk);
    if (error < 0) {
        printf("nl_send_simple() threw errcode %d.\n", error);
        printf("libnl's message: %s\n", nl_geterror(error));
        return error;
    }

    return 0;
}

void destroy_socket(void)
{
    nl_socket_free(sk);
}

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

    error = prepare_socket();
    if (error)
        return error;

    error = wait_for_kernel_message();
    destroy_socket();
    return error;
}

在内核3.2上测试. (对不起;这是我现在的最低价.)

Tested on kernel 3.2. (Sorry; that's the lowest I have right now.)

这篇关于内核模块可以通过netlink主动向用户空间发送消息吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!