将小型 UDP 数据包从 Linux 内核发送到 LOOPBACK

Posted

技术标签:

【中文标题】将小型 UDP 数据包从 Linux 内核发送到 LOOPBACK【英文标题】:Sending small UDP packets from the Linux Kernel to LOOPBACK 【发布时间】:2013-08-22 17:56:35 【问题描述】:

情况:我的代码基本上被入侵到 Linux 内核的驱动程序中。我想在用户空间中的应用程序被触发到主系统之前通知它们值得注意的原始事件。

解决方案的步骤:我在这里找到了一个从内核空间发送 UDP 数据包的好例子:http://kernelnewbies.org/Simple_UDP_Server。他们使用 INADDR_LOOPBACK 作为目标地址,这正是我想要的。

由于这是中断上下文,我决定使用工作队列来发送数据包(我得到了 BUG:在没有它的情况下进行原子调度)。所以我的发送代码是基于封装在工作队列结构中的 kernelnewbies 代码,该工作队列结构在主进程上由 INIT_WORK 和 schedule_work 触发。我没有声明我自己的工作队列。

我没有使用 Netpoll API,因为this Question 表明无法从本地主机发送数据或向本地主机发送数据。 “你不能发送给自己”

问题: 从内核发送的数据和从我的 UDP 接收器接收的数据很少匹配。我不知道为什么会这样。

用于测试的虚拟数据代码,包括工作队列结构的定义:

static struct socket *sock_send;
static struct sockaddr_in addr_send;

static struct ksocket_workmessage 
    unsigned char *buf;
    int len;
    struct work_struct workmessage;
 workmsg;


unsigned char testmsg[] = 'T', 'e', 's', 't', 'i', 'n', 'g', 'm', 's', 'g', '\0';
workmsg.buf = testmsg;
workmsg.len = 11;
INIT_WORK(&workmsg.workmessage, handle_workmessage);
schedule_work(&workmsg.workmessage);

发送实际数据包类似于 kernelnewbies 示例中的“int ksocket_send”。唯一的区别是我的 send_socket 是静态的,我必须从工作队列中获取带有 container_of 的 buf 和 len。我在一个完全静态的环境中工作。我的 handle_workmessage 方法也是静态的:

static void handle_workmessage(struct work_struct *work)

        struct msghdr msg;
        struct iovec iov;
        mm_segment_t oldfs;
        int size = 0;

        struct ksocket_workmessage *workmsg = container_of(work, struct ksocket_workmessage, workmessage);


        if (sock_send->sk==NULL)
             return;

        iov.iov_base = workmsg->buf;
        iov.iov_len = workmsg->len;

        msg.msg_flags = 0;
        msg.msg_name = &addr_send;
        msg.msg_namelen  = sizeof(struct sockaddr_in);
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;

        oldfs = get_fs();
        set_fs(KERNEL_DS);
        size = sock_sendmsg(sock_send,&msg,workmsg->len);
        set_fs(oldfs);

接收端如下所示:

int main(int argc, char**argv)

int sockfd,n;
struct sockaddr_in servaddr;
socklen_t len;
unsigned char mesg[1000];

sockfd=socket(AF_INET,SOCK_DGRAM,0);

bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(REC_PORT);
bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

for (;;)

  n = recv(sockfd,mesg,1000,0);
  printf("-------------------------------------------------------\n");
  mesg[n] = 0;
  printf("Received the following: %d bytes\n", n);
  printf("%s",mesg);
  printf("%c",mesg[0]);
  printf(",%c",mesg[1]);
  printf(",%c",mesg[2]);
  printf(",%c",mesg[3]);
  printf(",%c",mesg[4]);
  printf(",%c",mesg[5]);
  printf(",%c",mesg[6]);
  printf(",%c",mesg[7]);
  printf(",%c",mesg[8]);
  printf(",%c\n",mesg[9]);
  //printf("%c\n",mesg[0]);
  printf("-------------------------------------------------------\n");
  memset(mesg, 0, sizeof(mesg));
 

输出看起来已损坏,尽管我总是发送完全相同的消息用于测试目的:

-------------------------------------------------------
Received the following: 11 bytes
�|�ingmsg�,,|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
����d����,�,�,�,d,�,�,�,,
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�|�ingmsg�,,|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
,�,�,�,�,2,k,�,�,�
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�<����,<,�,�,�,,,,
                    ,=
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�|�ingmsg�,,|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�|�ingmsg�,,|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes


,,%,�,,,,,,
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
TestingmsgT,e,s,t,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�|�ingmsg�,,|,�,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
 ����Vk��1k ,�,�,�,�,V,k,�,�,1
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
TestingmsgT,e,s,t,i,n,g,m,s,g
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
,,,,,�,,�,,
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
,,
  ,�,,,,,�,<
-------------------------------------------------------
-------------------------------------------------------
Received the following: 11 bytes
�|�ingmsg�,,|,�,i,n,g,m,s,g
-------------------------------------------------------

这可能是什么原因?由于它有时适用于预期的输出“TestingmsgT,e,s,t,i,n,g,m,s,g”,因此它不应该是技术限制。数据包碎片也不应该发生,因为我只发送 11 个字节。也没有丢包。每次我发送数据包时,它也会收到。

更新:它可以工作.. 但我不知道为什么 首先,感谢alk 的评论,我忘记了这一点。在发送数据之前记录。我在调用 schedule_work 之前做了记录。现在我直接登录我的发送方法 workmsg->buf,甚至在存储到来自 iov 的 void * 指针之前。那里的数据已经损坏了。

结构体 ksocket_workmessage 有一个 char *,我的数据是 char [] 并被分配给结构体的指针。

我现在所做的是更改我的 struct ksocket_workmessage 中的数据类型:

struct ksocket_workmessage 
        unsigned char buf[11];
        int len;
        struct work_struct workmessage;
 workmsg;

由于没有指针,无法创建 unsigned char testmsg[],所以直接分配 buf:

workmsg.buf[0] = 'T';
workmsg.buf[1] = 'e';
workmsg.buf[2] = 's';
workmsg.buf[3] = 't';
workmsg.buf[4] = 'i';
workmsg.buf[5] = 'n';
workmsg.buf[6] = 'g';
workmsg.buf[7] = 'm';
workmsg.buf[8] = 's';
workmsg.buf[9] = 'g';
workmsg.buf[10] = '\0';

如果有人能告诉我我最初的方法在哪里失败,我很乐意接受它作为正确答案。

【问题讨论】:

请向我们展示您的 ksocket_send。 memset(&amp;mesg, 0, sizeof(mesg)); 虽然没关系真的应该是memset(mesg,0,sizeof(mesg)); 感谢您的评论。我已经添加了发送的代码。如前所述,它与 kernelnewbies 中的示例代码几乎相同,只是采用了工作队列机制 对不起,我现在只是在黑暗中刺伤。我想确认container_of kmallocs 一个 ksocket_workmessage 并返回指针。 做一个ksocket_workmessage varname并返回&amp;varname 也许问题出在其余的驱动程序代码中?您能否在将消息文本发送到套接字之前记录它以查看它是否仍然正常? 【参考方案1】:

由于它有时有效,有时无效,我建议问题在于您正在查看已释放()的内存。因此,内容有时是正确的,有时是错误的。由于您的本地缓冲区很好,因此必须在将其复制到本地内存之前在内核中进行。

unsigned char testmsg[] 确实声明为局部变量?

由于没有立即发送消息,因此您传递的 testmsg 地址在堆栈中。如果有后续的功能调用,那么它们将在发送消息之前覆盖消息的内容。然后您有时会看到正确的消息,有时则不会。视工作安排而定。

【讨论】:

.. 是的,它是一个局部变量。你的解释听起来很合理。由于使用 schedule_work 发送消息是异步的,因此局部变量可能已经在方法退出时被清除。我首先想到了这一点,但忽略了它,因为即使是第一次发送也失败了(我怀疑地址重写,没有想到自动释放)无论如何我的问题现在可以被认为是一个小教程,提供 Netpoll API 的替代方案;-) 进一步解释问题是如何产生的:kernelnewbies.org/Simple_UDP_Server 他们还将他们的 buf 声明为局部变量,但由于他们没有使用工作队列,所以他们没有问题。

以上是关于将小型 UDP 数据包从 Linux 内核发送到 LOOPBACK的主要内容,如果未能解决你的问题,请参考以下文章

Linux内核网络udp数据包发送

Linux内核网络udp数据包发送

uevent 从内核发送到用户空间 (udev)

Linux网络包接收过程的监控与调优

Linux内核分析_UDP协议中数据包的收发处理过程

Linux内核网络UDP数据包发送——IP协议层分析