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.