From f83ace20348dc01f099ba9e0d6a9075ac82545c7 Mon Sep 17 00:00:00 2001 From: goldsimon Date: Sun, 14 Mar 2010 11:26:05 +0000 Subject: [PATCH] task #6849: Calculate checksum when creating TCP segments, not when (re-)transmitting them. --- CHANGELOG | 4 ++ src/core/tcp_out.c | 126 ++++++++++++++++++++++++++++++++++-- src/include/lwip/tcp_impl.h | 14 +++- 3 files changed, 135 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 640b4f0a..eab1b0d8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,10 @@ HISTORY ++ New features: + 2010-03-14: Simon Goldschmidt + * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum + when creating TCP segments, not when (re-)transmitting them. + 2010-03-07: Simon Goldschmidt * sockets.c: bug #28775 (select/event_callback: only check select_cb_list on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code. diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c index 432f0ef0..cb4c1e9c 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -55,6 +55,25 @@ #include +/* Define some copy-macros for checksum-on-copy so that the code looks + nicer by preventing too many ifdef's. */ +#if TCP_CHECKSUM_ON_COPY +#define TCP_DATA_COPY(dst, src, len, seg) do { \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \ + len, &seg->chksum, &seg->chksum_swapped); \ + seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \ + tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped); +#else /* TCP_CHECKSUM_ON_COPY*/ +#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len) +#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len) +#endif /* TCP_CHECKSUM_ON_COPY*/ + +/** Define this to 1 for an extra check that the output checksum is valid + * (usefule when the checksum is generated by the application, not the stack) */ +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0 +#endif /* Forward declarations.*/ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); @@ -152,6 +171,13 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, #if TCP_OVERSIZE_DBGCHECK seg->oversize_left = 0; #endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = 0; + seg->chksum_swapped = 0; + /* check optflags */ + LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", + (optflags & TF_SEG_DATA_CHECKSUMMED) == 0); +#endif /* TCP_CHECKSUM_ON_COPY */ /* build TCP header */ if (pbuf_header(p, TCP_HLEN)) { @@ -229,6 +255,24 @@ tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, #define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM) #endif /* TCP_OVERSIZE */ +#if TCP_CHECKSUM_ON_COPY +/** Add a checksum of newly added data to the segment */ +static void +tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum, + u8_t *seg_chksum_swapped) +{ + u32_t helper; + /* add chksum to old chksum and fold to u16_t */ + helper = chksum + *seg_chksum; + chksum = FOLD_U32T(helper); + if ((len & 1) != 0) { + *seg_chksum_swapped = 1 - *seg_chksum_swapped; + chksum = SWAP_BYTES_IN_WORD(chksum); + } + *seg_chksum = chksum; +} +#endif /* TCP_CHECKSUM_ON_COPY */ + /** * Write data for sending (but does not send it immediately). * @@ -258,6 +302,11 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) u16_t oversize = 0; u16_t oversize_used = 0; #endif /* TCP_OVERSIZE */ +#if TCP_CHECKSUM_ON_COPY + u16_t concat_chksum = 0; + u8_t concat_chksum_swapped = 0; + u16_t concat_chksummed = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ #if LWIP_NETIF_TX_SINGLE_PBUF /* Always copy to try to create single pbufs for TX */ @@ -405,7 +454,10 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) #if TCP_OVERSIZE_DBGCHECK last_unsent->oversize_left = oversize; #endif /* TCP_OVERSIZE_DBGCHECK */ - MEMCPY(concat_p->payload, (u8_t*)arg + pos, seglen); + TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped); +#if TCP_CHECKSUM_ON_COPY + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ } else { /* Data is not copied */ if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { @@ -413,6 +465,12 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) ("tcp_write: could not allocate memory for zero-copy pbuf\n")); goto memerr; } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen, + &concat_chksum, &concat_chksum_swapped); + concat_chksummed += seglen; +#endif /* TCP_CHECKSUM_ON_COPY */ /* reference the non-volatile payload data */ concat_p->payload = (u8_t*)arg + pos; } @@ -438,6 +496,10 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) u16_t left = len - pos; u16_t max_len = pcb->mss - optlen; u16_t seglen = left > max_len ? max_len : left; +#if TCP_CHECKSUM_ON_COPY + u16_t chksum = 0; + u8_t chksum_swapped = 0; +#endif /* TCP_CHECKSUM_ON_COPY */ if (apiflags & TCP_WRITE_FLAG_COPY) { /* If copy is set, memory should be allocated and data copied @@ -448,7 +510,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) } LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen", (p->len >= seglen)); - MEMCPY((char *)p->payload + optlen, (u8_t*)arg + pos, seglen); + TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped); } else { /* Copy is not set: First allocate a pbuf for holding the data. * Since the referenced data is available at least until it is @@ -463,6 +525,10 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n")); goto memerr; } +#if TCP_CHECKSUM_ON_COPY + /* calculate the checksum of nocopy-data */ + chksum = ~inet_chksum((u8_t*)arg + pos, seglen); +#endif /* TCP_CHECKSUM_ON_COPY */ /* reference the non-volatile payload data */ p2->payload = (u8_t*)arg + pos; @@ -495,6 +561,11 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) #if TCP_OVERSIZE_DBGCHECK seg->oversize_left = oversize; #endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + seg->chksum = chksum; + seg->chksum_swapped = chksum_swapped; + seg->flags |= TF_SEG_DATA_CHECKSUMMED; +#endif /* TCP_CHECKSUM_ON_COPY */ /* Fix dataptr for the nocopy case */ if ((apiflags & TCP_WRITE_FLAG_COPY) == 0) { seg->dataptr = (u8_t*)arg + pos; @@ -534,7 +605,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) for (p = last_unsent->p; p; p = p->next) { p->tot_len += oversize_used; if (p->next == NULL) { - MEMCPY((u8_t *)p->payload + p->len, arg, oversize_used); + TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent); p->len += oversize_used; } } @@ -554,6 +625,13 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags) (last_unsent != NULL)); pbuf_cat(last_unsent->p, concat_p); last_unsent->len += concat_p->tot_len; +#if TCP_CHECKSUM_ON_COPY + if (concat_chksummed) { + tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum, + &last_unsent->chksum_swapped); + last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED; + } +#endif /* TCP_CHECKSUM_ON_COPY */ } /* @@ -1013,11 +1091,45 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) seg->tcphdr->chksum = 0; #if CHECKSUM_GEN_TCP - seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, - &(pcb->local_ip), +#if TCP_CHECKSUM_ON_COPY + { + u32_t acc; +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + u16_t chksum_slow = inet_chksum_pseudo(seg->p, &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) { + LWIP_ASSERT("data included but not checksummed", + seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4)); + } + + /* rebuild TCP header checksum (TCP header changes for retransmissions!) */ + acc = inet_chksum_pseudo_partial(seg->p, &(pcb->local_ip), &(pcb->remote_ip), - IP_PROTO_TCP, seg->p->tot_len); -#endif + IP_PROTO_TCP, seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4); + /* add payload checksum */ + if (seg->chksum_swapped) { + seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum); + seg->chksum_swapped = 0; + } + acc += (u16_t)~(seg->chksum); + seg->tcphdr->chksum = FOLD_U32T(acc); +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + if (chksum_slow != seg->tcphdr->chksum) { + LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n", + seg->tcphdr->chksum, chksum_slow)); + seg->tcphdr->chksum = chksum_slow; + } +#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */ + } +#else /* TCP_CHECKSUM_ON_COPY */ + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif /* TCP_CHECKSUM_ON_COPY */ +#endif /* CHECKSUM_GEN_TCP */ TCP_STATS_INC(tcp.xmit); #if LWIP_NETIF_HWADDRHINT diff --git a/src/include/lwip/tcp_impl.h b/src/include/lwip/tcp_impl.h index 20592141..730f71e9 100644 --- a/src/include/lwip/tcp_impl.h +++ b/src/include/lwip/tcp_impl.h @@ -253,12 +253,16 @@ PACK_STRUCT_END #endif /* LWIP_EVENT_API */ +/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */ #if TCP_OVERSIZE && defined(LWIP_DEBUG) #define TCP_OVERSIZE_DBGCHECK 1 #else #define TCP_OVERSIZE_DBGCHECK 0 #endif +/** Don't generate chceksum on copy if CHECKSUM_GEN_TCP is disabled */ +#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP) + /* This structure represents a TCP segment on the unsent, unacked and ooseq queues */ struct tcp_seg { struct tcp_seg *next; /* used when putting segements on a queue */ @@ -270,9 +274,15 @@ struct tcp_seg { pbuf in unsent (used for asserting vs. tcp_pcb.unsent_oversized only) */ #endif /* TCP_OVERSIZE_DBGCHECK */ +#if TCP_CHECKSUM_ON_COPY + u16_t chksum; + u8_t chksum_swapped; +#endif /* TCP_CHECKSUM_ON_COPY */ u8_t flags; -#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */ -#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */ +#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */ +#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */ +#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is + checksummed into 'chksum' */ struct tcp_hdr *tcphdr; /* the TCP header */ };