mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-11-04 23:29:25 +00:00
task #6849: Calculate checksum when creating TCP segments, not when (re-)transmitting them.
This commit is contained in:
parent
84ed9de21a
commit
f83ace2034
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user