On 6/21/18 4:57 PM, Jan Kiszka wrote:
> On 2018-06-21 15:41, Jan Kiszka wrote:
>> On 2018-06-21 13:55, Jan Kiszka wrote:
>>> On 2018-06-21 13:20, Pintu Kumar wrote:
>>>> Dear Jan, Greg,
>>>>
>>>> Is there any pointer about this issue?
>>>> This is blocking my next work..
>>>
>>> Does this solve the issue AND still generate valid UDP checksums (please
>>> check with wireshark or against a normal networking stack?
>>>
>>> diff --git a/kernel/drivers/net/stack/ipv4/udp/udp.c 
>>> b/kernel/drivers/net/stack/ipv4/udp/udp.c
>>> index 8e80d3e0b..bb0b0fc12 100644
>>> --- a/kernel/drivers/net/stack/ipv4/udp/udp.c
>>> +++ b/kernel/drivers/net/stack/ipv4/udp/udp.c
>>> @@ -556,18 +556,17 @@ static int rt_udp_getfrag(const void *p, unsigned 
>>> char *to,
>>>      if (offset)
>>>         return rtnet_read_from_iov(ufh->fd, ufh->iov, ufh->iovlen, to, 
>>> fraglen);
>>>  
>>> -    /* Checksum of the complete data part of the UDP message: */
>>> -    for (i = 0; i < ufh->iovlen; i++) {
>>> -            ufh->wcheck = csum_partial(ufh->iov[i].iov_base, 
>>> ufh->iov[i].iov_len,
>>> -                                       ufh->wcheck);
>>> -    }
>>> -
>>>      ret = rtnet_read_from_iov(ufh->fd, ufh->iov, ufh->iovlen,
>>>                           to + sizeof(struct udphdr),
>>>                           fraglen - sizeof(struct udphdr));
>>>      if (ret)
>>>         return ret;
>>>  
>>> +    /* Checksum of the complete data part of the UDP message: */
>>> +    ufh->wcheck = csum_partial(to + sizeof(struct udphdr),
>>> +                          fraglen - sizeof(struct udphdr),
>>> +                          ufh->wcheck);
>>> +
>>>      /* Checksum of the udp header: */
>>>      ufh->wcheck = csum_partial((unsigned char *)ufh,
>>>                            sizeof(struct udphdr), ufh->wcheck);
>>>
>>
>> This was definitely wrong. Here is another try:
>>
>> diff --git a/kernel/drivers/net/stack/ipv4/udp/udp.c 
>> b/kernel/drivers/net/stack/ipv4/udp/udp.c
>> index 8e80d3e0b..6cf1d369e 100644
>> --- a/kernel/drivers/net/stack/ipv4/udp/udp.c
>> +++ b/kernel/drivers/net/stack/ipv4/udp/udp.c
>> @@ -549,6 +549,7 @@ static int rt_udp_getfrag(const void *p, unsigned char 
>> *to,
>>                            unsigned int offset, unsigned int fraglen)
>>  {
>>      struct udpfakehdr *ufh = (struct udpfakehdr *)p;
>> +    unsigned int datalen = 0;
>>      int i, ret;
>>  
>>  
>> @@ -556,18 +557,18 @@ static int rt_udp_getfrag(const void *p, unsigned char 
>> *to,
>>      if (offset)
>>          return rtnet_read_from_iov(ufh->fd, ufh->iov, ufh->iovlen, to, 
>> fraglen);
>>  
>> -    /* Checksum of the complete data part of the UDP message: */
>> -    for (i = 0; i < ufh->iovlen; i++) {
>> -            ufh->wcheck = csum_partial(ufh->iov[i].iov_base, 
>> ufh->iov[i].iov_len,
>> -                                       ufh->wcheck);
>> -    }
>> -
>>      ret = rtnet_read_from_iov(ufh->fd, ufh->iov, ufh->iovlen,
>>                            to + sizeof(struct udphdr),
>>                            fraglen - sizeof(struct udphdr));
>>      if (ret)
>>          return ret;
>>  
>> +    /* Checksum of the complete data part of the UDP message: */
>> +    for (i = 0; i < ufh->iovlen; i++)
>> +        datalen += ufh->iov[i].iov_len;
>> +    ufh->wcheck = csum_partial(to + sizeof(struct udphdr), datalen,
>> +                           ufh->wcheck);
>> +
>>      /* Checksum of the udp header: */
>>      ufh->wcheck = csum_partial((unsigned char *)ufh,
>>                             sizeof(struct udphdr), ufh->wcheck);
>>
> 
> OK, this won't work either if fraglen < datalen. We have to rework this
> more fundamentally.
> 
> As a workaround, run the kernel with nosmap.
> 
> Jan
> 

There is no point in debugging the splash with UDP until this one is addressed 
since running with nosmap won't be acceptable in many cases, so I took a stab 
at fixing this issue first. This fix is somewhat ugly, but a clean solution 
would involve reworking the entire handling of fragmented packets, so let's see 
if the logic is right first. Comments and testing welcome:

diff --git a/kernel/drivers/net/stack/ipv4/udp/udp.c 
b/kernel/drivers/net/stack/ipv4/udp/udp.c
index 8e80d3e0b..44616269f 100644
--- a/kernel/drivers/net/stack/ipv4/udp/udp.c
+++ b/kernel/drivers/net/stack/ipv4/udp/udp.c
@@ -538,6 +538,7 @@ struct udpfakehdr
     struct iovec *iov;
     int iovlen;
     u32 wcheck;
+    size_t rawlen;
 };
 
 
@@ -549,24 +550,42 @@ static int rt_udp_getfrag(const void *p, unsigned char 
*to,
                           unsigned int offset, unsigned int fraglen)
 {
     struct udpfakehdr *ufh = (struct udpfakehdr *)p;
-    int i, ret;
-
+    void *ckdata, *tmp = NULL;
+    size_t cklen, len, iovsz;
+    struct iovec *tmpiov;
+    int ret;
 
     // We should optimize this function a bit (copy+csum...)!
     if (offset)
-           return rtnet_read_from_iov(ufh->fd, ufh->iov, ufh->iovlen, to, 
fraglen);
-
-    /* Checksum of the complete data part of the UDP message: */
-    for (i = 0; i < ufh->iovlen; i++) {
-            ufh->wcheck = csum_partial(ufh->iov[i].iov_base, 
ufh->iov[i].iov_len,
-                                       ufh->wcheck);
+           return rtnet_read_from_iov(ufh->fd, ufh->iov, ufh->iovlen,
+                                      to, fraglen);
+
+    len = fraglen - sizeof(struct udphdr);
+    if (len < ufh->rawlen) {
+           iovsz = sizeof(*tmpiov) * ufh->iovlen;
+           tmp = xnmalloc(ufh->rawlen + iovsz);
+           if (tmp == NULL)
+                   return -ENOMEM;
+           tmpiov = tmp;
+           memcpy(tmpiov, ufh->iov, iovsz);
+           ckdata = tmp + iovsz;
+           cklen = ufh->rawlen;
+           ret = rtnet_read_from_iov(ufh->fd, tmpiov, ufh->iovlen,
+                                     ckdata, cklen);
+           if (ret)
+                   goto out;
+           memcpy(to + sizeof(struct udphdr), ckdata, len);
+    } else {
+           ckdata = to + sizeof(struct udphdr);
+           cklen = len;
+           ret = rtnet_read_from_iov(ufh->fd, ufh->iov, ufh->iovlen,
+                                     ckdata, cklen);
+           if (ret)
+                   goto out;
     }
 
-    ret = rtnet_read_from_iov(ufh->fd, ufh->iov, ufh->iovlen,
-                             to + sizeof(struct udphdr),
-                             fraglen - sizeof(struct udphdr));
-    if (ret)
-           return ret;
+    /* Checksum of the complete data part of the UDP message: */
+    ufh->wcheck = csum_partial(ckdata, cklen, ufh->wcheck);
 
     /* Checksum of the udp header: */
     ufh->wcheck = csum_partial((unsigned char *)ufh,
@@ -580,7 +599,11 @@ static int rt_udp_getfrag(const void *p, unsigned char *to,
 
     memcpy(to, ufh, sizeof(struct udphdr));
 
-    return 0;
+out:
+    if (tmp)
+           xnfree(tmp);
+
+    return ret;
 }
 
 
@@ -682,9 +705,10 @@ ssize_t rt_udp_sendmsg(struct rtdm_fd *fd, const struct 
user_msghdr *msg, int ms
     ufh.uh.len    = htons(ulen);
     ufh.uh.check  = 0;
     ufh.fd        = fd;
-    ufh.iov       = msg->msg_iov;
+    ufh.iov       = iov;
     ufh.iovlen    = msg->msg_iovlen;
     ufh.wcheck    = 0;
+    ufh.rawlen    = len;
 
     err = rt_ip_build_xmit(sock, rt_udp_getfrag, &ufh, ulen, &rt, msg_flags);


-- 
Philippe.

Reply via email to