gpt4 book ai didi

sockets - 内核模块可以主动通过netlink向用户空间发送消息吗?

转载 作者:行者123 更新时间:2023-12-02 10:05:14 26 4
gpt4 key购买 nike

我正在尝试运行以下代码,该代码是从 here 复制的。我做了一些更改以使用较旧的内核版本运行它。

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

我到底想做的是,

  • 内核模块创建一个套接字,无论用户空间中的任何进程如何
  • 内核模块向用户空间发送一些事件
  • 如果用户空间中的任何进程回复事件,内核模块进程将处理该回复

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

是否可以将第一条消息从内核模块发送到用户空间中的进程?我怎样才能做到这一点?

内核模块代码:

Makefile

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 tool (可能也想阅读 core guide ),然后执行如下操作:

内核:

#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进行编译,您的情况可能会有所不同):

#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 上测试。 (抱歉;这是我现在的最低水平。)

关于sockets - 内核模块可以主动通过netlink向用户空间发送消息吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35098943/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com