mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-10-01 04:12:07 +00:00
TASK9218: add support for TCP timestamp options
This commit is contained in:
parent
baf30f5eae
commit
4b14621208
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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. */
|
||||||
|
@ -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",
|
||||||
|
(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 */
|
/* 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 */
|
||||||
}
|
TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4));
|
||||||
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);
|
|
||||||
}
|
|
||||||
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))) {
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user