Sending small UDP packets from the Linux kernel to LOOPBACK will not work reliably

Situation: My code is mostly hacked into the Linux kernel driver. I want to notify the application in user space of noteworthy unprocessed events before they are launched into the main system.

Steps to solve: I found a good example for sending UDP packets from kernel space: http://kernelnewbies.org/Simple_UDP_Server They use INADDR_LOOPBACK as the destination address, which is exactly what I want.

Since this is an interrupt context, I decided to use a work queue to send packets (I got BUG: Scheduling while atomic without it). Thus, my send code is based on the kernelwew kernel code, which is completed in the work queue structure launched with INIT_WORK and sched_work in the main process. I do not announce my own work queue.

I do not use the Netpoll API as this question , which does not allow sending data from and to localhost. "You cannot send yourself"

Problem: Data sent from the kernel and received from my UDP receiver rarely matches. I have no idea why this is happening.

Code for dummy data for testing, including defining the structure for the work queue:

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

Passing the actual package is similar to "int ksocket_send" from the kernelnewbies example. The only difference is that my send_socket is static and I need to get buf and len with container_of from the work queue. I work in a completely static context. My handle_workmessage method is also static:

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

The end result is as follows:

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

The result looks distorted, although I always send the same message for testing purposes:

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

What could be the reason for this? Since this sometimes works with the expected output of TestingmsgT, e, s, t, i, n, g, m, s, g, this should not be a technical limitation. Packet fragmentation should not happen either, since I only send 11 bytes. Also there is no packet loss. Every time I send a package, it is also accepted.

UPDATE: THIS WORKS .. but I don’t know why firstly, thanks for the comment from alk that I forgot the obvious. To register immediately before sending data. I recorded before calling schedule_work. Now I register directly in my send method workmsg-> buf before it is stored in the void * pointer from iov. This data has already been processed.

Struct ksocket_workmessage had char *, my data was char [] and assigned to the structure pointer.

Now I have changed the data type in my ksocket_workmessage structure:

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

Since I no longer have a pointer, I could not create my unsigned char testmsg [], so I went for the buf assignment directly:

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

If someone tells me where my initial approach failed, I gladly accept it as the correct answer.

+4
source share
1 answer

Since this sometimes works, and sometimes I do not suggest that the problem is that you are looking at a memory that was free () d. Thus, the content is sometimes correct, and sometimes they are distorted. Since your local buffer is in order, this should happen in the kernel before it is copied to local memory.

Is unsigned char testmsg[] declared as a local variable?

Since the message is not sent immediately, the testmsg address you pass is on the stack. If there are subsequent functional calls, they overwrite the contents of the message before sending it. Then you sometimes see the correct message, and sometimes not. Depending on the work schedule.

+2
source

Source: https://habr.com/ru/post/1498028/


All Articles