task #6849: Calculate checksum when creating TCP segments, not when (re-)transmitting them.

This commit is contained in:
goldsimon 2010-03-14 11:26:05 +00:00
parent 84ed9de21a
commit f83ace2034
3 changed files with 135 additions and 9 deletions

View File

@ -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.

View File

@ -55,6 +55,25 @@
#include <string.h>
/* 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

View File

@ -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 */
};