TASK9218: add support for TCP timestamp options

This commit is contained in:
kieranm 2009-03-31 14:13:32 +00:00
parent baf30f5eae
commit 4b14621208
7 changed files with 195 additions and 98 deletions

View File

@ -19,6 +19,11 @@ HISTORY
++ New features: ++ New features:
2009-03-31 Kieran Mansley
* tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for
TCP timestamp options, off by default. Rework tcp_enqueue() to
take option flags rather than specified option data
2009-02-18 Simon Goldschmidt 2009-02-18 Simon Goldschmidt
* cc.h: Added printf formatter for size_t: SZT_F * cc.h: Added printf formatter for size_t: SZT_F

View File

@ -488,7 +488,6 @@ err_t
tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port, tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err)) err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err))
{ {
u32_t optdata;
err_t ret; err_t ret;
u32_t iss; u32_t iss;
@ -529,10 +528,11 @@ tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
snmp_inc_tcpactiveopens(); snmp_inc_tcpactiveopens();
/* Build an MSS option */ ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS
optdata = TCP_BUILD_MSS_OPTION(); #if LWIP_TCP_TIMESTAMPS
| TF_SEG_OPTS_TS
ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (u8_t *)&optdata, 4); #endif
);
if (ret == ERR_OK) { if (ret == ERR_OK) {
tcp_output(pcb); tcp_output(pcb);
} }
@ -1268,7 +1268,9 @@ tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr)
mss_s = outif->mtu - IP_HLEN - TCP_HLEN; mss_s = outif->mtu - IP_HLEN - TCP_HLEN;
/* RFC 1122, chap 4.2.2.6: /* RFC 1122, chap 4.2.2.6:
* Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
* but we only send options with SYN and that is never filled with data! */ * We correct for TCP options in tcp_enqueue(), and don't support
* IP options
*/
sendmss = LWIP_MIN(sendmss, mss_s); sendmss = LWIP_MIN(sendmss, mss_s);
} }
return sendmss; return sendmss;

View File

@ -393,7 +393,6 @@ static err_t
tcp_listen_input(struct tcp_pcb_listen *pcb) tcp_listen_input(struct tcp_pcb_listen *pcb)
{ {
struct tcp_pcb *npcb; struct tcp_pcb *npcb;
u32_t optdata;
err_t rc; err_t rc;
/* In the LISTEN state, we check for incoming SYN segments, /* In the LISTEN state, we check for incoming SYN segments,
@ -452,10 +451,14 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
snmp_inc_tcppassiveopens(); snmp_inc_tcppassiveopens();
/* Build an MSS option. */
optdata = TCP_BUILD_MSS_OPTION();
/* Send a SYN|ACK together with the MSS option. */ /* Send a SYN|ACK together with the MSS option. */
rc = tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (u8_t *)&optdata, 4); #if LWIP_TCP_TIMESTAMPS
if (npcb->flags & TF_TIMESTAMP)
rc = tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0,
TF_SEG_OPTS_MSS | TF_SEG_OPTS_TS);
else
#endif
rc = tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, TF_SEG_OPTS_MSS);
if (rc != ERR_OK) { if (rc != ERR_OK) {
tcp_abandon(npcb, 0); tcp_abandon(npcb, 0);
return rc; return rc;
@ -546,6 +549,8 @@ tcp_process(struct tcp_pcb *pcb)
pcb->tmr = tcp_ticks; pcb->tmr = tcp_ticks;
pcb->keep_cnt_sent = 0; pcb->keep_cnt_sent = 0;
tcp_parseopt(pcb);
/* Do different things depending on the TCP state. */ /* Do different things depending on the TCP state. */
switch (pcb->state) { switch (pcb->state) {
case SYN_SENT: case SYN_SENT:
@ -561,9 +566,6 @@ tcp_process(struct tcp_pcb *pcb)
pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
pcb->state = ESTABLISHED; pcb->state = ESTABLISHED;
/* Parse any options in the SYNACK before using pcb->mss since that
* can be changed by the received options! */
tcp_parseopt(pcb);
#if TCP_CALCULATE_EFF_SEND_MSS #if TCP_CALCULATE_EFF_SEND_MSS
pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */ #endif /* TCP_CALCULATE_EFF_SEND_MSS */
@ -1310,8 +1312,7 @@ tcp_receive(struct tcp_pcb *pcb)
} }
/** /**
* Parses the options contained in the incoming segment. (Code taken * Parses the options contained in the incoming segment.
* from uIP with only small changes.)
* *
* Called from tcp_listen_input() and tcp_process(). * Called from tcp_listen_input() and tcp_process().
* Currently, only the MSS option is supported! * Currently, only the MSS option is supported!
@ -1321,36 +1322,72 @@ tcp_receive(struct tcp_pcb *pcb)
static void static void
tcp_parseopt(struct tcp_pcb *pcb) tcp_parseopt(struct tcp_pcb *pcb)
{ {
u16_t c; u16_t c, max_c;
u8_t *opts, opt;
u16_t mss; u16_t mss;
u8_t *opts, opt;
#if LWIP_TCP_TIMESTAMPS
u32_t tsval;
#endif
opts = (u8_t *)tcphdr + TCP_HLEN; opts = (u8_t *)tcphdr + TCP_HLEN;
/* Parse the TCP MSS option, if present. */ /* Parse the TCP MSS option, if present. */
if(TCPH_HDRLEN(tcphdr) > 0x5) { if(TCPH_HDRLEN(tcphdr) > 0x5) {
for(c = 0; c < ((TCPH_HDRLEN(tcphdr) - 5) << 2) ;) { max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
for (c = 0; c < max_c; ) {
opt = opts[c]; opt = opts[c];
if (opt == 0x00) { switch (opt) {
case 0x00:
/* End of options. */ /* End of options. */
break; LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
} else if (opt == 0x01) { return;
++c; case 0x01:
/* NOP option. */ /* NOP option. */
} else if (opt == 0x02 && ++c;
opts[c + 1] == 0x04) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
break;
case 0x02:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
/* Bad length */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
return;
}
/* An MSS option with the right option length. */ /* An MSS option with the right option length. */
mss = (opts[c + 2] << 8) | opts[c + 3]; mss = (opts[c + 2] << 8) | opts[c + 3];
/* Limit the mss to the configured TCP_MSS and prevent division by zero */ /* Limit the mss to the configured TCP_MSS and prevent division by zero */
pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
/* Advance to next option */
/* And we are done processing options. */ c += 0x04;
break; break;
} else { #if LWIP_TCP_TIMESTAMPS
case 0x08:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
/* Bad length */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
return;
}
/* TCP timestamp option with valid length */
tsval = (opts[c+2]) | (opts[c+3] << 8) |
(opts[c+4] << 16) | (opts[c+5] << 24);
if (flags & TCP_SYN) {
pcb->ts_recent = ntohl(tsval);
pcb->flags |= TF_TIMESTAMP;
} else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
pcb->ts_recent = ntohl(tsval);
}
/* Advance to next option */
c += 0x0A;
break;
#endif
default:
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
if (opts[c + 1] == 0) { if (opts[c + 1] == 0) {
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
/* If the length field is zero, the options are malformed /* If the length field is zero, the options are malformed
and we don't process them further. */ and we don't process them further. */
break; return;
} }
/* All other options have a length field, so that we easily /* All other options have a length field, so that we easily
can skip past them. */ can skip past them. */

View File

@ -69,8 +69,8 @@ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
err_t err_t
tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags) tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags)
{ {
/* no data, no length, flags, copy=1, no optdata, no optdatalen */ /* no data, no length, flags, copy=1, no optdata */
return tcp_enqueue(pcb, NULL, 0, flags, TCP_WRITE_FLAG_COPY, NULL, 0); return tcp_enqueue(pcb, NULL, 0, flags, TCP_WRITE_FLAG_COPY, 0);
} }
/** /**
@ -102,7 +102,12 @@ tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags)
pcb->state == SYN_SENT || pcb->state == SYN_SENT ||
pcb->state == SYN_RCVD) { pcb->state == SYN_RCVD) {
if (len > 0) { if (len > 0) {
return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, NULL, 0); #if LWIP_TCP_TIMESTAMPS
return tcp_enqueue(pcb, (void *)data, len, 0, apiflags,
pcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0);
#else
return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, 0);
#endif
} }
return ERR_OK; return ERR_OK;
} else { } else {
@ -112,7 +117,7 @@ tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags)
} }
/** /**
* Enqueue either data or TCP options (but not both) for tranmission * Enqueue data and/or TCP options for transmission
* *
* Called by tcp_connect(), tcp_listen_input(), tcp_send_ctrl() and tcp_write(). * Called by tcp_connect(), tcp_listen_input(), tcp_send_ctrl() and tcp_write().
* *
@ -128,8 +133,7 @@ tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags)
*/ */
err_t err_t
tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
u8_t flags, u8_t apiflags, u8_t flags, u8_t apiflags, u8_t optflags)
u8_t *optdata, u8_t optlen)
{ {
struct pbuf *p; struct pbuf *p;
struct tcp_seg *seg, *useg, *queue; struct tcp_seg *seg, *useg, *queue;
@ -137,13 +141,17 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
u16_t left, seglen; u16_t left, seglen;
void *ptr; void *ptr;
u16_t queuelen; u16_t queuelen;
u8_t optlen;
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", apiflags=%"U16_F")\n", LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", apiflags=%"U16_F")\n",
(void *)pcb, arg, len, (u16_t)flags, (u16_t)apiflags)); (void *)pcb, arg, len, (u16_t)flags, (u16_t)apiflags));
LWIP_ERROR("tcp_enqueue: len == 0 || optlen == 0 (programmer violates API)", LWIP_ERROR("tcp_enqueue: packet needs payload, options, or SYN/FIN (programmer violates API)",
((len == 0) || (optlen == 0)), return ERR_ARG;); ((len != 0) || (optflags != 0) || ((flags & (TCP_SYN | TCP_FIN)) != 0)),
LWIP_ERROR("tcp_enqueue: arg == NULL || optdata == NULL (programmer violates API)", return ERR_ARG;);
((arg == NULL) || (optdata == NULL)), return ERR_ARG;); LWIP_ERROR("tcp_enqueue: len != 0 || arg == NULL (programmer violates API)",
((len != 0) || (arg == NULL)), return ERR_ARG;);
/* fail on too much data */ /* fail on too much data */
if (len > pcb->snd_buf) { if (len > pcb->snd_buf) {
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf)); LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf));
@ -153,6 +161,8 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
left = len; left = len;
ptr = arg; ptr = arg;
optlen = LWIP_TCP_OPT_LENGTH(optflags);
/* seqno will be the sequence number of the first segment enqueued /* seqno will be the sequence number of the first segment enqueued
* by the call to this function. */ * by the call to this function. */
seqno = pcb->snd_lbb; seqno = pcb->snd_lbb;
@ -182,15 +192,14 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
useg = queue = seg = NULL; useg = queue = seg = NULL;
seglen = 0; seglen = 0;
while (queue == NULL || left > 0) { while (queue == NULL || left > 0) {
/* The segment length (including options) should be at most the MSS */
/* The segment length should be the MSS if the data to be enqueued seglen = left > (pcb->mss - optlen) ? (pcb->mss - optlen) : left;
* is larger than the MSS. */
seglen = left > pcb->mss? pcb->mss: left;
/* Allocate memory for tcp_seg, and fill in fields. */ /* Allocate memory for tcp_seg, and fill in fields. */
seg = memp_malloc(MEMP_TCP_SEG); seg = memp_malloc(MEMP_TCP_SEG);
if (seg == NULL) { if (seg == NULL) {
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for tcp_seg\n")); LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
("tcp_enqueue: could not allocate memory for tcp_seg\n"));
goto memerr; goto memerr;
} }
seg->next = NULL; seg->next = NULL;
@ -211,30 +220,18 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
/* If copy is set, memory should be allocated /* If copy is set, memory should be allocated
* and data copied into pbuf, otherwise data comes from * and data copied into pbuf, otherwise data comes from
* ROM or other static memory, and need not be copied. If * ROM or other static memory, and need not be copied. */
* optdata is != NULL, we have options instead of data. */ if (apiflags & TCP_WRITE_FLAG_COPY) {
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen + optlen, PBUF_RAM)) == NULL) {
/* options? */ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
if (optdata != NULL) { ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
goto memerr;
}
LWIP_ASSERT("check that first pbuf can hold optlen",
(seg->p->len >= optlen));
queuelen += pbuf_clen(seg->p);
seg->dataptr = seg->p->payload;
}
/* copy from volatile memory? */
else if (apiflags & TCP_WRITE_FLAG_COPY) {
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) {
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
goto memerr; goto memerr;
} }
LWIP_ASSERT("check that first pbuf can hold the complete seglen", LWIP_ASSERT("check that first pbuf can hold the complete seglen",
(seg->p->len >= seglen)); (seg->p->len >= seglen + optlen));
queuelen += pbuf_clen(seg->p); queuelen += pbuf_clen(seg->p);
if (arg != NULL) { if (arg != NULL) {
MEMCPY(seg->p->payload, ptr, seglen); MEMCPY((char *)seg->p->payload + optlen, ptr, seglen);
} }
seg->dataptr = seg->p->payload; seg->dataptr = seg->p->payload;
} }
@ -245,8 +242,9 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
* link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM
* instead of PBUF_REF here. * instead of PBUF_REF here.
*/ */
if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { if ((p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
("tcp_enqueue: could not allocate memory for zero-copy pbuf\n"));
goto memerr; goto memerr;
} }
++queuelen; ++queuelen;
@ -255,11 +253,12 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
seg->dataptr = ptr; seg->dataptr = ptr;
/* Second, allocate a pbuf for the headers. */ /* Second, allocate a pbuf for the headers. */
if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) { if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
/* If allocation fails, we have to deallocate the data pbuf as /* If allocation fails, we have to deallocate the data pbuf as
* well. */ * well. */
pbuf_free(p); pbuf_free(p);
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n")); LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
("tcp_enqueue: could not allocate memory for header pbuf\n"));
goto memerr; goto memerr;
} }
queuelen += pbuf_clen(seg->p); queuelen += pbuf_clen(seg->p);
@ -292,17 +291,10 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
TCPH_FLAGS_SET(seg->tcphdr, flags); TCPH_FLAGS_SET(seg->tcphdr, flags);
/* don't fill in tcphdr->ackno and tcphdr->wnd until later */ /* don't fill in tcphdr->ackno and tcphdr->wnd until later */
/* Copy the options into the header, if they are present. */ seg->flags = optflags;
if (optdata == NULL) {
TCPH_HDRLEN_SET(seg->tcphdr, 5); /* Set the length of the header */
}
else {
TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4)); TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4));
/* Copy options into data portion of segment.
Options can thus only be sent in non data carrying
segments such as SYN|ACK. */
SMEMCPY(seg->dataptr, optdata, optlen);
}
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
ntohl(seg->tcphdr->seqno), ntohl(seg->tcphdr->seqno),
ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
@ -401,6 +393,19 @@ memerr:
return ERR_MEM; return ERR_MEM;
} }
#if LWIP_TCP_TIMESTAMPS
static inline void tcp_build_timestamp_option(struct tcp_pcb *pcb,
u32_t *opts)
{
/* Pad with two NOP options to make everything nicely aligned */
opts[0] = htonl(0x0101080A);
opts[1] = htonl(sys_now());
opts[2] = htonl(pcb->ts_recent);
}
#endif
/** /**
* Find out what we can send and send it * Find out what we can send and send it
* *
@ -418,6 +423,7 @@ tcp_output(struct tcp_pcb *pcb)
#if TCP_CWND_DEBUG #if TCP_CWND_DEBUG
s16_t i = 0; s16_t i = 0;
#endif /* TCP_CWND_DEBUG */ #endif /* TCP_CWND_DEBUG */
u8_t optlen = 0;
/* First, check if we are invoked by the TCP input processing /* First, check if we are invoked by the TCP input processing
code. If so, we do not output anything. Instead, we rely on the code. If so, we do not output anything. Instead, we rely on the
@ -446,12 +452,17 @@ tcp_output(struct tcp_pcb *pcb)
if (pcb->flags & TF_ACK_NOW && if (pcb->flags & TF_ACK_NOW &&
(seg == NULL || (seg == NULL ||
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); #if LWIP_TCP_TIMESTAMPS
if (pcb->flags & TF_TIMESTAMP)
optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
#endif
p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen, PBUF_RAM);
if (p == NULL) { if (p == NULL) {
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
return ERR_BUF; return ERR_BUF;
} }
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
/* remove ACK flags from the PCB, as we send an empty ACK now */ /* remove ACK flags from the PCB, as we send an empty ACK now */
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
@ -463,7 +474,15 @@ tcp_output(struct tcp_pcb *pcb)
TCPH_FLAGS_SET(tcphdr, TCP_ACK); TCPH_FLAGS_SET(tcphdr, TCP_ACK);
tcphdr->wnd = htons(pcb->rcv_ann_wnd); tcphdr->wnd = htons(pcb->rcv_ann_wnd);
tcphdr->urgp = 0; tcphdr->urgp = 0;
TCPH_HDRLEN_SET(tcphdr, 5); TCPH_HDRLEN_SET(tcphdr, (5 + optlen / 4));
/* NB. MSS option is only sent on SYNs, so ignore it here */
#if LWIP_TCP_TIMESTAMPS
pcb->ts_lastacksent = pcb->rcv_nxt;
if (pcb->flags & TF_TIMESTAMP)
tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
#endif
tcphdr->chksum = 0; tcphdr->chksum = 0;
#if CHECKSUM_GEN_TCP #if CHECKSUM_GEN_TCP
@ -599,6 +618,7 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
{ {
u16_t len; u16_t len;
struct netif *netif; struct netif *netif;
u32_t *opts;
/** @bug Exclude retransmitted segments from this count. */ /** @bug Exclude retransmitted segments from this count. */
snmp_inc_tcpoutsegs(); snmp_inc_tcpoutsegs();
@ -610,6 +630,22 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
/* advertise our receive window size in this TCP segment */ /* advertise our receive window size in this TCP segment */
seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
/* Add any requested options. NB MSS option is only set on SYN
packets, so ignore it here */
opts = (u32_t *)(seg->tcphdr + 1);
if (seg->flags & TF_SEG_OPTS_MSS) {
TCP_BUILD_MSS_OPTION(*opts);
opts += 1;
}
#if LWIP_TCP_TIMESTAMPS
pcb->ts_lastacksent = pcb->rcv_nxt;
if (seg->flags & TF_SEG_OPTS_TS) {
tcp_build_timestamp_option(pcb, opts);
opts += 3;
}
#endif
/* If we don't have a local IP address, we get one by /* If we don't have a local IP address, we get one by
calling ip_route(). */ calling ip_route(). */
if (ip_addr_isany(&(pcb->local_ip))) { if (ip_addr_isany(&(pcb->local_ip))) {

View File

@ -773,6 +773,13 @@
#define TCP_DEFAULT_LISTEN_BACKLOG 0xff #define TCP_DEFAULT_LISTEN_BACKLOG 0xff
#endif #endif
/**
* LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option.
*/
#ifndef LWIP_TCP_TIMESTAMPS
#define LWIP_TCP_TIMESTAMPS 0
#endif
/** /**
* LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1.
* LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all

View File

@ -139,8 +139,6 @@ void sys_mbox_fetch(sys_mbox_t mbox, void **msg);
/* Thread functions. */ /* Thread functions. */
sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio); sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio);
/* The following functions are used only in Unix code, and
can be omitted when porting the stack. */
/* Returns the current time in microseconds. */ /* Returns the current time in microseconds. */
u32_t sys_now(void); u32_t sys_now(void);

View File

@ -139,12 +139,6 @@ void tcp_rexmit_rto (struct tcp_pcb *pcb);
#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK) #define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK)
/** This returns a TCP header option for MSS in an u32_t */
#define TCP_BUILD_MSS_OPTION() htonl(((u32_t)2 << 24) | \
((u32_t)4 << 16) | \
(((u32_t)TCP_MSS / 256) << 8) | \
(TCP_MSS & 255))
#define TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0) #define TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0)
#define TCP_SEQ_LEQ(a,b) ((s32_t)((a)-(b)) <= 0) #define TCP_SEQ_LEQ(a,b) ((s32_t)((a)-(b)) <= 0)
#define TCP_SEQ_GT(a,b) ((s32_t)((a)-(b)) > 0) #define TCP_SEQ_GT(a,b) ((s32_t)((a)-(b)) > 0)
@ -298,12 +292,13 @@ struct tcp_pcb {
u16_t remote_port; u16_t remote_port;
u8_t flags; u8_t flags;
#define TF_ACK_DELAY (u8_t)0x01U /* Delayed ACK. */ #define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */
#define TF_ACK_NOW (u8_t)0x02U /* Immediate ACK. */ #define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */
#define TF_INFR (u8_t)0x04U /* In fast recovery. */ #define TF_INFR ((u8_t)0x04U) /* In fast recovery. */
#define TF_FIN (u8_t)0x20U /* Connection was closed locally (FIN segment enqueued). */ #define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */
#define TF_NODELAY (u8_t)0x40U /* Disable Nagle algorithm */ #define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */
#define TF_NAGLEMEMERR (u8_t)0x80U /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ #define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */
#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
/* the rest of the fields are in host byte order /* the rest of the fields are in host byte order
as we have to do some math with them */ as we have to do some math with them */
@ -407,6 +402,11 @@ struct tcp_pcb {
void (* errf)(void *arg, err_t err); void (* errf)(void *arg, err_t err);
#endif /* LWIP_CALLBACK_API */ #endif /* LWIP_CALLBACK_API */
#if LWIP_TCP_TIMESTAMPS
u32_t ts_lastacksent;
u32_t ts_recent;
#endif /* LWIP_TCP_TIMESTAMPS */
/* idle time before KEEPALIVE is sent */ /* idle time before KEEPALIVE is sent */
u32_t keep_idle; u32_t keep_idle;
#if LWIP_TCP_KEEPALIVE #if LWIP_TCP_KEEPALIVE
@ -493,9 +493,22 @@ struct tcp_seg {
struct pbuf *p; /* buffer containing data + TCP header */ struct pbuf *p; /* buffer containing data + TCP header */
void *dataptr; /* pointer to the TCP data in the pbuf */ void *dataptr; /* pointer to the TCP data in the pbuf */
u16_t len; /* the TCP length of this segment */ u16_t len; /* the TCP length of this segment */
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. */
struct tcp_hdr *tcphdr; /* the TCP header */ struct tcp_hdr *tcphdr; /* the TCP header */
}; };
#define LWIP_TCP_OPT_LENGTH(flags) \
(flags & TF_SEG_OPTS_MSS ? 4 : 0) + \
(flags & TF_SEG_OPTS_TS ? 12 : 0)
/** This returns a TCP header option for MSS in an u32_t */
#define TCP_BUILD_MSS_OPTION(x) (x) = htonl(((u32_t)2 << 24) | \
((u32_t)4 << 16) | \
(((u32_t)TCP_MSS / 256) << 8) | \
(TCP_MSS & 255))
/* Internal functions and global variables: */ /* Internal functions and global variables: */
struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb);
void tcp_pcb_purge(struct tcp_pcb *pcb); void tcp_pcb_purge(struct tcp_pcb *pcb);
@ -518,8 +531,7 @@ struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg);
err_t tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags); err_t tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags);
err_t tcp_enqueue(struct tcp_pcb *pcb, void *dataptr, u16_t len, err_t tcp_enqueue(struct tcp_pcb *pcb, void *dataptr, u16_t len,
u8_t flags, u8_t apiflags, u8_t flags, u8_t apiflags, u8_t optflags);
u8_t *optdata, u8_t optlen);
void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg);