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

时间:2013-08-20 15:32:43

标签: c sockets linux-kernel udp localhost

情况:我的代码基本上被黑客入侵了Linux内核的驱动程序。我想在用户空间中通知一个关于值得注意的原始事件的应用程序,然后才能将它们发送到主系统。

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

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

我没有使用Netpoll API,因为this Question表示无法从localhost发送数据。 “你不能发给自己”

问题: 从内核发送并从我的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是静态的,我必须从工作队列中获取buf和len与container_of。我在一个完全静态的环境中工作。我的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之前做了日志。现在我直接在我的send方法workmsg-&gt; buf中登录,甚至在从iov存储到void *指针之前。数据已经在那里被破坏了。

struct 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';

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

1 个答案:

答案 0 :(得分:2)

因为它有时会起作用,有时候我不会建议问题是你正在寻找已经免费的内存()d。因此,内容有时是正确的,有时它们会被破坏。由于本地缓冲区很好,因此必须在内核中将其复制到本地内存之前。

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

由于消息未立即发送,因此您传递的testmsg地址位于堆栈中。如果存在后续的功能调用,则它们将在发送之前重写消息的内容。然后,您有时会看到正确的信息,有时则看不到。取决于工作的安排。