On 04.11.18 17:17, Philippe Gerum wrote:
> 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);
> 
> 

Nasty, very nasty. That dynamic allocation could easily cause a performance
regression for existing users.

For my memory: The smap-trigger is doing csum_partial on a userspace buffer,
right? So we would need a user-safe csum_partial. I think the kernel does 
that...

Jan

-- 
Siemens AG, Corporate Technology, CT RDA IOT SES-DE
Corporate Competence Center Embedded Linux

Reply via email to