从 Linux 内核发送 UDP 数据包

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10499865/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 06:13:51  来源:igfitidea点击:

Sending UDP packets from the Linux Kernel

linuxlinux-kerneludpkernel

提问by tvuillemin

Even if a similar topic already exists, I noticed that it dates back two years, thus I guess it's more appropriate to open a fresh one...

即使已经有类似的话题,我注意到它可以追溯到两年前,所以我想开一个新的更合适......

I'm trying to figure out how to send UDP packets from the Linux Kernel (3.3.4), in order to monitor the behavior of the random number generator (/drivers/char/random.c). So far, I've managed to monitor a few things owing to the sock_create and sock_sendmsg functions. You can find the typical piece of code I use at the end of this message. (You might also want to download the complete modified random.c file here.)

我试图弄清楚如何从 Linux 内核 (3.3.4) 发送 UDP 数据包,以监视随机数生成器 (/drivers/char/random.c) 的行为。到目前为止,由于 sock_create 和 sock_sendmsg 函数,我已经设法监控了一些事情。您可以在此消息的末尾找到我使用的典型代码段。(您可能还想在此处下载完整的修改后的 random.c 文件。)

By inserting this code inside the appropriate random.c functions, I'm able to send a UDP packet for each access to /dev/random and /dev/urandom, and each keyboard/mouse events used by the random number generator to harvest entropy. However it doesn't work at all when I try to monitor the disk events: it generates a kernel panic during boot.

通过在适当的 random.c 函数中插入此代码,我能够为每次访问 /dev/random 和 /dev/urandom 以及随机数生成器用来收集熵的每个键盘/鼠标事件发送一个 UDP 数据包. 但是,当我尝试监视磁盘事件时它根本不起作用:它在启动期间会产生内核恐慌。

Consequently, here's my main question: Have you any idea why my code causes so much trouble when inserted in the disk events function?(add_disk_randomness)

因此,这是我的主要问题:您知道为什么我的代码在插入磁盘事件函数时会引起如此多的麻烦吗?(add_disk_randomness)

Alternatively, I've read about the netpoll API, which is supposed to handle this kind of UDP-in-kernel problems. Unfortunately I haven't found any relevant documentation apart from an quite interesting but outdated Red Hat presentation from 2005. Do you think I should rather use this API?If yes, have you got any example?

或者,我已经阅读了有关 netpoll API 的内容,该 API 应该可以处理此类 UDP-in-kernel 问题。不幸的是,除了 2005 年相当有趣但过时的 Red Hat 演示文稿之外,我没有找到任何相关文档。您认为我应该使用这个 API 吗?如果是,你有什么例子吗?

Any help would be appreciated. Thanks in advance.

任何帮助,将不胜感激。提前致谢。

PS: It's my first question here, so please don't hesitate to tell me if I'm doing something wrong, I'll keep it in mind for future :)

PS:这是我在这里的第一个问题,所以如果我做错了什么,请不要犹豫告诉我,我会记住它以备将来:)



#include <linux/net.h>
#include <linux/in.h>
#include <linux/netpoll.h>
#define MESSAGE_SIZE 1024
#define INADDR_SEND ((unsigned long int)0x0a00020f) //10.0.2.15
static bool sock_init;
static struct socket *sock;
static struct sockaddr_in sin;
static struct msghdr msg;
static struct iovec iov;

[...]

int error, len;
mm_segment_t old_fs;
char message[MESSAGE_SIZE];

if (sock_init == false)
{
  /* Creating socket */
  error = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
  if (error<0)
    printk(KERN_DEBUG "Can't create socket. Error %d\n",error);

  /* Connecting the socket */
  sin.sin_family = AF_INET;
  sin.sin_port = htons(1764);
  sin.sin_addr.s_addr = htonl(INADDR_SEND);
  error = sock->ops->connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr), 0);
  if (error<0)
    printk(KERN_DEBUG "Can't connect socket. Error %d\n",error);

  /* Preparing message header */
  msg.msg_flags = 0;
  msg.msg_name = &sin;
  msg.msg_namelen  = sizeof(struct sockaddr_in);
  msg.msg_control = NULL;
  msg.msg_controllen = 0;
  msg.msg_iov = &iov;
  msg.msg_control = NULL;
  sock_init = true;
}

/* Sending a message */
sprintf(message,"EXTRACT / Time: %llu / InputPool: %4d / BlockingPool: %4d / NonblockingPool: %4d / Request: %4d\n",
  get_cycles(),
  input_pool.entropy_count,
  blocking_pool.entropy_count,
  nonblocking_pool.entropy_count,
  nbytes*8);
iov.iov_base = message;
len = strlen(message);
iov.iov_len = len;
msg.msg_iovlen = len;
old_fs = get_fs();
set_fs(KERNEL_DS);
error = sock_sendmsg(sock,&msg,len);
set_fs(old_fs);

采纳答案by tvuillemin

I solved my problem a few months ago. Here's the solution I used.

几个月前我解决了我的问题。这是我使用的解决方案。

The standard packet-sending API (sock_create, connect, ...) cannot be used in a few contexts (interruptions). Using it in the wrong place leads to a KP.

标准的数据包发送 API(sock_create、connect、...)不能在少数情况下(中断)使用。在错误的地方使用它会导致 KP。

The netpoll API is more "low-level" and works in every context. However, there are several conditions :

netpoll API 更“低级”并且适用于所有上下文。但是,有几个条件:

  • Ethernet devices
  • IP network
  • UDP only (no TCP)
  • Different computers for sending and receiving packets (You can't send to yourself.)
  • 以太网设备
  • IP网络
  • 仅 UDP(无 TCP)
  • 发送和接收数据包的不同计算机(您不能发送给自己。)

Make sure to respect them, because you won't get any error message if there's a problem. It will just silently fail :) Here's a bit of code.

确保尊重它们,因为如果出现问题,您将不会收到任何错误消息。它只会默默地失败:) 这里有一些代码。

Declaration

宣言

#include <linux/netpoll.h>
#define MESSAGE_SIZE 1024
#define INADDR_LOCAL ((unsigned long int)0xc0a80a54) //192.168.10.84
#define INADDR_SEND ((unsigned long int)0xc0a80a55) //192.168.10.85
static struct netpoll* np = NULL;
static struct netpoll np_t;

Initialization

初始化

np_t.name = "LRNG";
strlcpy(np_t.dev_name, "eth0", IFNAMSIZ);
np_t.local_ip = htonl(INADDR_LOCAL);
np_t.remote_ip = htonl(INADDR_SEND);
np_t.local_port = 6665;
np_t.remote_port = 6666;
memset(np_t.remote_mac, 0xff, ETH_ALEN);
netpoll_print_options(&np_t);
netpoll_setup(&np_t);
np = &np_t;

Use

char message[MESSAGE_SIZE];
sprintf(message,"%d\n",42);
int len = strlen(message);
netpoll_send_udp(np,message,len);

Hope it can help someone.

希望它可以帮助某人。

回答by moorray

Panic during boot might be caused by you trying to use something which wasn't initialized yet. Looking at stack trace might help figuring out what actually happened.

启动期间的恐慌可能是由于您尝试使用尚未初始化的东西。查看堆栈跟踪可能有助于弄清楚实际发生了什么。

As for you problem, I think you are trying to do a simple thing, so why not stick with simple tools? ;) printks might be bad idea indeed, but give trace_printk a go. trace_printk is part of Ftrace infrastructure.

至于你的问题,我认为你正在尝试做一件简单的事情,那么为什么不坚持使用简单的工具呢?;) printks 确实可能是个坏主意,但不妨试试 trace_printk。trace_printk 是 Ftrace 基础设施的一部分。

Section Using trace_printk() in following article should teach you everything you need to know: http://lwn.net/Articles/365835/

以下文章中的使用 trace_printk()部分应该教您需要知道的一切:http: //lwn.net/Articles/365835/