From 2ca113a218b6220331a985ca2cf0dacad2c2da0c Mon Sep 17 00:00:00 2001 From: goldsimon Date: Sun, 7 Oct 2007 20:19:23 +0000 Subject: [PATCH] Fixed UDPLite SENDING: Checksum was always generated too short and also was generated wrong if checksum coverage != tot_len. --- CHANGELOG | 4 +++ src/core/inet.c | 63 ++++++++++++++++++++++++++++++++++++ src/core/udp.c | 20 +++++++----- src/include/ipv4/lwip/inet.h | 3 ++ 4 files changed, 82 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6bd40390..fc854970 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -424,6 +424,10 @@ HISTORY ++ Bug fixes: + 2007-10-07 Simon Goldschmidt + * udp.c, inet.c, inet.h: Fixed UDPLite SENDING: Checksum was always generated + too short and also was generated wrong if checksum coverage != tot_len. + 2007-10-07 Frédéric Bernon * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT: diff --git a/src/core/inet.c b/src/core/inet.c index 76fa356f..38a686a1 100644 --- a/src/core/inet.c +++ b/src/core/inet.c @@ -294,6 +294,69 @@ inet_chksum_pseudo(struct pbuf *p, return (u16_t)~(acc & 0xffffUL); } +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + * IP addresses are expected to be in network byte order. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ip address (used for checksum of pseudo header) + * @param dst destination ip address (used for checksum of pseudo header) + * @param proto ip protocol (used for checksum of pseudo header) + * @param proto_len length of the ip data part (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + u16_t chklen; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + chklen = q->len; + if (chklen > chksum_len) { + chklen = chksum_len; + } + acc += LWIP_CHKSUM(q->payload, chklen); + chksum_len -= chklen; + LWIP_ASSERT("delete me", chksum_len < 0x7fff); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + while ((acc >> 16) != 0) { + acc = (acc & 0xffffUL) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); + acc += (dest->addr & 0xffffUL); + acc += ((dest->addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + while ((acc >> 16) != 0) { + acc = (acc & 0xffffUL) + (acc >> 16); + } + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + /* inet_chksum: * * Calculates the Internet checksum over a portion of memory. Used primarily for IP diff --git a/src/core/udp.c b/src/core/udp.c index 4b102b87..e0c472b8 100644 --- a/src/core/udp.c +++ b/src/core/udp.c @@ -39,10 +39,13 @@ /* udp.c * - * The code for the User Datagram Protocol UDP. + * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). * */ +/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! + */ + #include "lwip/opt.h" #if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ @@ -425,11 +428,11 @@ udp_send(struct udp_pcb *pcb, struct pbuf *p) #if LWIP_UDPLITE /* UDP Lite protocol? */ if (pcb->flags & UDP_FLAGS_UDPLITE) { - u16_t chklen; + u16_t chklen, chklen_hdr; LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); /* set UDP message length in UDP header */ - chklen = pcb->chksum_len_rx; - if (chklen < sizeof(struct udp_hdr)) { + chklen_hdr = chklen = pcb->chksum_len_tx; + if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { if (chklen != 0) { LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); } @@ -439,13 +442,14 @@ udp_send(struct udp_pcb *pcb, struct pbuf *p) checksum, therefore, if chksum_len has an illegal value, we generate the checksum over the complete packet to be safe. */ - chklen = p->tot_len; + chklen_hdr = 0; + chklen = q->tot_len; } - udphdr->len = htons(chklen); + udphdr->len = htons(chklen_hdr); /* calculate checksum */ #if CHECKSUM_GEN_UDP - udphdr->chksum = inet_chksum_pseudo(q, src_ip, &(pcb->remote_ip), - IP_PROTO_UDP, chklen); + udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, &(pcb->remote_ip), + IP_PROTO_UDPLITE, q->tot_len, chklen); /* chksum zero must become 0xffff, as zero means 'no checksum' */ if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; diff --git a/src/include/ipv4/lwip/inet.h b/src/include/ipv4/lwip/inet.h index 6dfad143..ccf69588 100644 --- a/src/include/ipv4/lwip/inet.h +++ b/src/include/ipv4/lwip/inet.h @@ -49,6 +49,9 @@ u16_t inet_chksum_pbuf(struct pbuf *p); u16_t inet_chksum_pseudo(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t proto, u16_t proto_len); +u16_t inet_chksum_pseudo_partial(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len); u32_t inet_addr(const char *cp); int inet_aton(const char *cp, struct in_addr *addr);