Отправка UDP-пакетов из ядра Linux

Даже если подобная тема уже существует, я заметил, что она датируется двумя годами, поэтому, я думаю, более уместно открыть новую...

Я пытаюсь выяснить, как отправлять UDP-пакеты из ядра Linux (3.3.4), чтобы отслеживать поведение генератора случайных чисел (/drivers/ char/random.c). До сих пор мне удалось контролировать несколько вещей благодаря функциям sock_create и sock_sendmsg. Вы можете найти типичный фрагмент кода, который я использую в конце этого сообщения. (Вы также можете загрузить полный измененный файл random.c здесь.)

Вставив этот код в соответствующие функции random.c, я могу отправить UDP-пакет для каждого доступа к /dev/random и/dev/urandom, а каждое событие клавиатуры/мыши, используемое генератором случайных чисел для сбора энтропии. Однако, когда я пытаюсь контролировать события диска, это вообще не работает, он генерирует панику ядра во время загрузки.

Следовательно, здесь мой главный вопрос: есть ли у вас какие-либо идеи, почему мой код вызывает столько проблем при вставке в функцию событий диска? (Add_disk_randomness)

В качестве альтернативы, я прочитал об API netpoll, который должен обрабатывать такие проблемы UDP-в-ядре. К сожалению, я не нашел никакой соответствующей документации, кроме довольно интересной, но устаревшей презентации Red Hat с 2005 года. Как вы думаете, я должен использовать этот API? Если да, есть ли у вас какой-нибудь пример?

Любая помощь будет оценена по достоинству. Спасибо заранее.

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

Ответы

Ответ 1

Я решил свою проблему несколько месяцев назад. Вот решение, которое я использовал.

Стандартный API отправки пакетов (sock_create, connect,...) не может использоваться в нескольких контекстах (прерывания). Использование его в неправильном месте приводит к КП.

API netpoll более "низкоуровневый" и работает в любом контексте. Однако существует несколько условий:

  • Ethernet-устройства
  • IP-сеть
  • Только UDP (без TCP)
  • Различные компьютеры для отправки и получения пакетов (вы не можете отправить себе.)

Обязательно соблюдайте их, потому что вы не получите сообщение об ошибке, если есть проблема. Он будет просто терпеть неудачу:) Вот немного кода.

Заявление

#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;

Инициализация

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;

Использование

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

Надеюсь, это может помочь кому-то.

Ответ 2

Паника во время загрузки может быть вызвана тем, что вы пытаетесь использовать что-то, еще не инициализированное. Глядя на стек стека может помочь выяснить, что на самом деле произошло.

Что касается проблемы, я думаю, что вы пытаетесь сделать простую вещь, так почему бы не использовать простые инструменты?;) printks может быть плохой идеей, но дать trace_printk a go. trace_printk является частью инфраструктуры Ftrace.

Раздел Использование trace_printk() в следующей статье должно научить вас всему, что вам нужно знать: http://lwn.net/Articles/365835/