16

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.)

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.

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)

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?

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 :)


#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);
Eugene
  • 5,977
  • 2
  • 29
  • 46
tvuillemin
  • 1,148
  • 3
  • 10
  • 25
  • 4
    Generally, it is preferable that you don't do anything inthe kernel you could do in userspace - it would probably be better to expose the information to userspace via logging mechanisms or sysfs and then have a daemon send it to the remote system. – Chris Stratton May 08 '12 at 13:49
  • When a similar topic already exists, link to it. You did a reasonable job explaining why you think the existing question isn't good enough (I might have said something about newer kernel version, etc.). But having the existing question easily available makes it possible for answers to focus on what changed since then. – Ben Voigt May 08 '12 at 14:47
  • @BenVoigt Thx for your advice. Here's the [previous topic](http://stackoverflow.com/questions/1814485/sending-udp-packet-in-linux-kernel). – tvuillemin May 08 '12 at 15:36
  • @ChrisStratton The reason why I do such a thing in kernel space is that I absolutely want to influence the generator as few as possible while I'm watching it. For instance, it would be far simpler to use some printk to monitor everything; but it would generate disk events, that would produce entropy gathered by the generator... Since network events are not watched by the generator in recent kernel versions, I decided to use UDP packets rather than printk. But if you've got a better idea, I'll be happy to try it :) – tvuillemin May 08 '12 at 15:55
  • Does access to sysfs nodes or ramfs count as disk access? – Chris Stratton May 08 '12 at 16:42
  • Can you use other interfaces like e.g. the lpt port? That's almost trivial to write to and will have very little overhead. – Klaas van Gend May 09 '12 at 08:01
  • Thank you for your ideas! I'm gonna give them a try. If I find anything interesting, I shall report here. – tvuillemin May 09 '12 at 12:04
  • Possible duplicate of [Sending UDP packets from the Linux Kernel](https://stackoverflow.com/q/10499865/608639), [Sending UDP packet in Linux Kernel](https://stackoverflow.com/q/1814485/608639), [Sending a UDP packet within a kernel module](https://stackoverflow.com/q/21925879/608639), [Sending small UDP packets from the Linux Kernel to LOOPBACK](https://stackoverflow.com/q/18339174/608639), etc. – jww May 13 '18 at 02:40

2 Answers2

15

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.

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

  • Ethernet devices
  • IP network
  • UDP only (no TCP)
  • Different computers for sending and receiving packets (You can't send to yourself.)

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.

tvuillemin
  • 1,148
  • 3
  • 10
  • 25
  • as raised [here](http://stackoverflow.com/questions/35880786/why-do-i-get-this-error), kernel version 3.9 (April 2013) made a breaking change to `linux/netpoll.h` so this code no longer compiles – M.M Mar 09 '16 at 00:43
2

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.

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

moorray
  • 577
  • 3
  • 9