diff --git a/CHANGELOG b/CHANGELOG index 522a9791..913b33eb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,11 @@ HISTORY ++ 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 * cc.h: Added printf formatter for size_t: SZT_F diff --git a/src/core/tcp.c b/src/core/tcp.c index f996b6b7..55e068c8 100644 --- a/src/core/tcp.c +++ b/src/core/tcp.c @@ -488,7 +488,6 @@ err_t 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)) { - u32_t optdata; err_t ret; u32_t iss; @@ -529,10 +528,11 @@ tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port, snmp_inc_tcpactiveopens(); - /* Build an MSS option */ - optdata = TCP_BUILD_MSS_OPTION(); - - ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (u8_t *)&optdata, 4); + ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS +#if LWIP_TCP_TIMESTAMPS + | TF_SEG_OPTS_TS +#endif + ); if (ret == ERR_OK) { 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; /* RFC 1122, chap 4.2.2.6: * 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); } return sendmss; diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c index ee83dac3..c966d81f 100644 --- a/src/core/tcp_in.c +++ b/src/core/tcp_in.c @@ -393,7 +393,6 @@ static err_t tcp_listen_input(struct tcp_pcb_listen *pcb) { struct tcp_pcb *npcb; - u32_t optdata; err_t rc; /* 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(); - /* Build an MSS option. */ - optdata = TCP_BUILD_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) { tcp_abandon(npcb, 0); return rc; @@ -546,6 +549,8 @@ tcp_process(struct tcp_pcb *pcb) pcb->tmr = tcp_ticks; pcb->keep_cnt_sent = 0; + tcp_parseopt(pcb); + /* Do different things depending on the TCP state. */ switch (pcb->state) { 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->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 pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip)); #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 - * from uIP with only small changes.) + * Parses the options contained in the incoming segment. * * Called from tcp_listen_input() and tcp_process(). * Currently, only the MSS option is supported! @@ -1321,36 +1322,72 @@ tcp_receive(struct tcp_pcb *pcb) static void tcp_parseopt(struct tcp_pcb *pcb) { - u16_t c; - u8_t *opts, opt; + u16_t c, max_c; u16_t mss; + u8_t *opts, opt; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif opts = (u8_t *)tcphdr + TCP_HLEN; /* Parse the TCP MSS option, if present. */ 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]; - if (opt == 0x00) { + switch (opt) { + case 0x00: /* End of options. */ - break; - } else if (opt == 0x01) { - ++c; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case 0x01: /* NOP option. */ - } else if (opt == 0x02 && - opts[c + 1] == 0x04) { + ++c; + 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. */ mss = (opts[c + 2] << 8) | opts[c + 3]; /* Limit the mss to the configured TCP_MSS and prevent division by zero */ pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; - - /* And we are done processing options. */ + /* Advance to next option */ + c += 0x04; 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) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); /* If the length field is zero, the options are malformed and we don't process them further. */ - break; + return; } /* All other options have a length field, so that we easily can skip past them. */ diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c index 4eb4086b..2dba5915 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -69,8 +69,8 @@ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); err_t tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags) { - /* no data, no length, flags, copy=1, no optdata, no optdatalen */ - return tcp_enqueue(pcb, NULL, 0, flags, TCP_WRITE_FLAG_COPY, NULL, 0); + /* no data, no length, flags, copy=1, no optdata */ + 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_RCVD) { 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; } 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(). * @@ -128,8 +133,7 @@ tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags) */ err_t tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, - u8_t flags, u8_t apiflags, - u8_t *optdata, u8_t optlen) + u8_t flags, u8_t apiflags, u8_t optflags) { struct pbuf *p; 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; void *ptr; 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", + (void *)pcb, arg, len, (u16_t)flags, (u16_t)apiflags)); + LWIP_ERROR("tcp_enqueue: packet needs payload, options, or SYN/FIN (programmer violates API)", + ((len != 0) || (optflags != 0) || ((flags & (TCP_SYN | TCP_FIN)) != 0)), + return ERR_ARG;); + LWIP_ERROR("tcp_enqueue: len != 0 || arg == NULL (programmer violates API)", + ((len != 0) || (arg == NULL)), return ERR_ARG;); - 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)); - LWIP_ERROR("tcp_enqueue: len == 0 || optlen == 0 (programmer violates API)", - ((len == 0) || (optlen == 0)), return ERR_ARG;); - LWIP_ERROR("tcp_enqueue: arg == NULL || optdata == NULL (programmer violates API)", - ((arg == NULL) || (optdata == NULL)), return ERR_ARG;); /* fail on too much data */ 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)); @@ -153,6 +161,8 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, left = len; ptr = arg; + optlen = LWIP_TCP_OPT_LENGTH(optflags); + /* seqno will be the sequence number of the first segment enqueued * by the call to this function. */ seqno = pcb->snd_lbb; @@ -182,15 +192,14 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, useg = queue = seg = NULL; seglen = 0; while (queue == NULL || left > 0) { - - /* The segment length should be the MSS if the data to be enqueued - * is larger than the MSS. */ - seglen = left > pcb->mss? pcb->mss: left; + /* The segment length (including options) should be at most the MSS */ + seglen = left > (pcb->mss - optlen) ? (pcb->mss - optlen) : left; /* Allocate memory for tcp_seg, and fill in fields. */ seg = memp_malloc(MEMP_TCP_SEG); 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; } 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 * and data copied into pbuf, otherwise data comes from - * ROM or other static memory, and need not be copied. If - * optdata is != NULL, we have options instead of data. */ - - /* options? */ - if (optdata != NULL) { - 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)); + * ROM or other static memory, and need not be copied. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen + optlen, 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; } 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); if (arg != NULL) { - MEMCPY(seg->p->payload, ptr, seglen); + MEMCPY((char *)seg->p->payload + optlen, ptr, seglen); } 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 * instead of PBUF_REF here. */ - if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { - LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); + 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")); goto memerr; } ++queuelen; @@ -255,11 +253,12 @@ tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, seg->dataptr = ptr; /* 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 * well. */ 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; } 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); /* don't fill in tcphdr->ackno and tcphdr->wnd until later */ - /* Copy the options into the header, if they are present. */ - if (optdata == NULL) { - TCPH_HDRLEN_SET(seg->tcphdr, 5); - } - else { - 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); - } + seg->flags = optflags; + + /* Set the length of the header */ + TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4)); 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) + TCP_TCPLEN(seg), @@ -401,6 +393,19 @@ memerr: 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 * @@ -418,6 +423,7 @@ tcp_output(struct tcp_pcb *pcb) #if TCP_CWND_DEBUG s16_t i = 0; #endif /* TCP_CWND_DEBUG */ + u8_t optlen = 0; /* First, check if we are invoked by the TCP input processing 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 && (seg == NULL || 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) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); 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 */ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); @@ -463,7 +474,15 @@ tcp_output(struct tcp_pcb *pcb) TCPH_FLAGS_SET(tcphdr, TCP_ACK); tcphdr->wnd = htons(pcb->rcv_ann_wnd); 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; #if CHECKSUM_GEN_TCP @@ -599,6 +618,7 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) { u16_t len; struct netif *netif; + u32_t *opts; /** @bug Exclude retransmitted segments from this count. */ 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 */ 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 calling ip_route(). */ if (ip_addr_isany(&(pcb->local_ip))) { diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index 17964f48..16807be5 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -773,6 +773,13 @@ #define TCP_DEFAULT_LISTEN_BACKLOG 0xff #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==1: The user defines lwip_tcp_event() to receive all diff --git a/src/include/lwip/sys.h b/src/include/lwip/sys.h index cfaed4e4..7f0ee891 100644 --- a/src/include/lwip/sys.h +++ b/src/include/lwip/sys.h @@ -139,8 +139,6 @@ void sys_mbox_fetch(sys_mbox_t mbox, void **msg); /* Thread functions. */ 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. */ u32_t sys_now(void); diff --git a/src/include/lwip/tcp.h b/src/include/lwip/tcp.h index 745054e6..83841571 100644 --- a/src/include/lwip/tcp.h +++ b/src/include/lwip/tcp.h @@ -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) -/** 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_LEQ(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; u8_t flags; -#define TF_ACK_DELAY (u8_t)0x01U /* Delayed ACK. */ -#define TF_ACK_NOW (u8_t)0x02U /* Immediate ACK. */ -#define TF_INFR (u8_t)0x04U /* In fast recovery. */ -#define TF_FIN (u8_t)0x20U /* Connection was closed locally (FIN segment enqueued). */ -#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 */ +#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */ +#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */ +#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */ +#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */ +#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */ +#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 as we have to do some math with them */ @@ -407,6 +402,11 @@ struct tcp_pcb { void (* errf)(void *arg, err_t err); #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 */ u32_t keep_idle; #if LWIP_TCP_KEEPALIVE @@ -493,9 +493,22 @@ struct tcp_seg { struct pbuf *p; /* buffer containing data + TCP header */ void *dataptr; /* pointer to the TCP data in the pbuf */ 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 */ }; +#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: */ struct tcp_pcb *tcp_pcb_copy(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_enqueue(struct tcp_pcb *pcb, void *dataptr, u16_t len, - u8_t flags, u8_t apiflags, - u8_t *optdata, u8_t optlen); + u8_t flags, u8_t apiflags, u8_t optflags); void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg);