Fixed UDPLite SENDING: Checksum was always generated too short and also was generated wrong if checksum coverage != tot_len.

This commit is contained in:
goldsimon 2007-10-07 20:19:23 +00:00
parent 911ee4d9f9
commit 2ca113a218
4 changed files with 82 additions and 8 deletions

View File

@ -424,6 +424,10 @@ HISTORY
++ Bug fixes: ++ 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 2007-10-07 Frédéric Bernon
* sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential
crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT: crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT:

View File

@ -294,6 +294,69 @@ inet_chksum_pseudo(struct pbuf *p,
return (u16_t)~(acc & 0xffffUL); 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: /* inet_chksum:
* *
* Calculates the Internet checksum over a portion of memory. Used primarily for IP * Calculates the Internet checksum over a portion of memory. Used primarily for IP

View File

@ -39,10 +39,13 @@
/* udp.c /* 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" #include "lwip/opt.h"
#if LWIP_UDP /* don't build if not configured for use in lwipopts.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 #if LWIP_UDPLITE
/* UDP Lite protocol? */ /* UDP Lite protocol? */
if (pcb->flags & UDP_FLAGS_UDPLITE) { 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)); LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
/* set UDP message length in UDP header */ /* set UDP message length in UDP header */
chklen = pcb->chksum_len_rx; chklen_hdr = chklen = pcb->chksum_len_tx;
if (chklen < sizeof(struct udp_hdr)) { if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
if (chklen != 0) { if (chklen != 0) {
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); 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 checksum, therefore, if chksum_len has an illegal
value, we generate the checksum over the complete value, we generate the checksum over the complete
packet to be safe. */ 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 */ /* calculate checksum */
#if CHECKSUM_GEN_UDP #if CHECKSUM_GEN_UDP
udphdr->chksum = inet_chksum_pseudo(q, src_ip, &(pcb->remote_ip), udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, &(pcb->remote_ip),
IP_PROTO_UDP, chklen); IP_PROTO_UDPLITE, q->tot_len, chklen);
/* chksum zero must become 0xffff, as zero means 'no checksum' */ /* chksum zero must become 0xffff, as zero means 'no checksum' */
if (udphdr->chksum == 0x0000) if (udphdr->chksum == 0x0000)
udphdr->chksum = 0xffff; udphdr->chksum = 0xffff;

View File

@ -49,6 +49,9 @@ u16_t inet_chksum_pbuf(struct pbuf *p);
u16_t inet_chksum_pseudo(struct pbuf *p, u16_t inet_chksum_pseudo(struct pbuf *p,
struct ip_addr *src, struct ip_addr *dest, struct ip_addr *src, struct ip_addr *dest,
u8_t proto, u16_t proto_len); 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); u32_t inet_addr(const char *cp);
int inet_aton(const char *cp, struct in_addr *addr); int inet_aton(const char *cp, struct in_addr *addr);