fixed bug #37614 "Errors from ipX_output are not processed". Now tcp_output(_segment) checks for the return value of ipX_output and does not try to send more on error. A netif driver can call tcp_txnow() (from tcpip_thread!) to try to send again if TX buffers are available again.

This commit is contained in:
goldsimon 2015-02-17 08:02:34 +01:00
parent 90db821036
commit 5d13b5a2fb
5 changed files with 113 additions and 42 deletions

View File

@ -186,6 +186,13 @@ HISTORY
++ Bugfixes: ++ Bugfixes:
2015-02-17: Simon Goldschmidt
* tcp_impl.h, tcp_out.c, tcp.c, api_msg.c: fixed bug #37614 "Errors from
ipX_output are not processed". Now tcp_output(_segment) checks for the return
value of ipX_output and does not try to send more on error. A netif driver
can call tcp_txnow() (from tcpip_thread!) to try to send again if TX buffers
are available again.
2015-02-14: patches by Freddie Chopin 2015-02-14: patches by Freddie Chopin
* snmp*: made community writable, fixed some const pointers * snmp*: made community writable, fixed some const pointers

View File

@ -1482,6 +1482,7 @@ err_mem:
} }
if (err == ERR_OK) { if (err == ERR_OK) {
err_t out_err;
conn->write_offset += len; conn->write_offset += len;
if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) { if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
/* return sent length */ /* return sent length */
@ -1490,18 +1491,34 @@ err_mem:
write_finished = 1; write_finished = 1;
conn->write_offset = 0; conn->write_offset = 0;
} }
tcp_output(conn->pcb.tcp); out_err = tcp_output(conn->pcb.tcp);
if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
/* If tcp_output fails with fatal error or no route is found,
don't try writing any more but return the error
to the application thread. */
err = out_err;
write_finished = 1;
conn->current_msg->msg.w.len = 0;
}
} else if ((err == ERR_MEM) && !dontblock) { } else if ((err == ERR_MEM) && !dontblock) {
/* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
we do NOT return to the application thread, since ERR_MEM is we do NOT return to the application thread, since ERR_MEM is
only a temporary error! */ only a temporary error! */
/* tcp_write returned ERR_MEM, try tcp_output anyway */ /* tcp_write returned ERR_MEM, try tcp_output anyway */
tcp_output(conn->pcb.tcp); err_t out_err = tcp_output(conn->pcb.tcp);
if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
/* If tcp_output fails with fatal error or no route is found,
don't try writing any more but return the error
to the application thread. */
err = out_err;
write_finished = 1;
conn->current_msg->msg.w.len = 0;
} else {
#if LWIP_TCPIP_CORE_LOCKING #if LWIP_TCPIP_CORE_LOCKING
conn->flags |= NETCONN_FLAG_WRITE_DELAYED; conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
#endif #endif
}
} else { } else {
/* On errors != ERR_MEM, we don't try writing any more but return /* On errors != ERR_MEM, we don't try writing any more but return
the error to the application thread. */ the error to the application thread. */

View File

@ -879,13 +879,17 @@ tcp_slowtmr_start:
if (pcb->persist_backoff > 0) { if (pcb->persist_backoff > 0) {
/* If snd_wnd is zero, use persist timer to send 1 byte probes /* If snd_wnd is zero, use persist timer to send 1 byte probes
* instead of using the standard retransmission mechanism. */ * instead of using the standard retransmission mechanism. */
u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1];
if (pcb->persist_cnt < backoff_cnt) {
pcb->persist_cnt++; pcb->persist_cnt++;
if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { }
if (pcb->persist_cnt >= backoff_cnt) {
if (tcp_zero_window_probe(pcb) == ERR_OK) {
pcb->persist_cnt = 0; pcb->persist_cnt = 0;
if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
pcb->persist_backoff++; pcb->persist_backoff++;
} }
tcp_zero_window_probe(pcb); }
} }
} else { } else {
/* Increase the retransmission timer if it is running */ /* Increase the retransmission timer if it is running */
@ -957,10 +961,12 @@ tcp_slowtmr_start:
(pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
/ TCP_SLOW_INTERVAL) / TCP_SLOW_INTERVAL)
{ {
tcp_keepalive(pcb); err = tcp_keepalive(pcb);
if (err == ERR_OK) {
pcb->keep_cnt_sent++; pcb->keep_cnt_sent++;
} }
} }
}
/* If this PCB has queued out of sequence data, but has been /* If this PCB has queued out of sequence data, but has been
inactive for too long, will drop the data (it will eventually inactive for too long, will drop the data (it will eventually
@ -1130,6 +1136,19 @@ tcp_fasttmr_start:
} }
} }
/** Call tcp_output for all active pcbs that have TF_NAGLEMEMERR set */
void
tcp_txnow(void)
{
struct tcp_pcb *pcb;
for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
if (pcb->flags & TF_NAGLEMEMERR) {
tcp_output(pcb);
}
}
}
/** Pass pcb->refused_data to the recv callback */ /** Pass pcb->refused_data to the recv callback */
err_t err_t
tcp_process_refused_data(struct tcp_pcb *pcb) tcp_process_refused_data(struct tcp_pcb *pcb)

View File

@ -94,7 +94,7 @@
#endif #endif
/* Forward declarations.*/ /* Forward declarations.*/
static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
/** Allocate a pbuf and create a tcphdr at p->payload, used for output /** Allocate a pbuf and create a tcphdr at p->payload, used for output
* functions other than the default tcp_output -> tcp_output_segment * functions other than the default tcp_output -> tcp_output_segment
@ -895,6 +895,7 @@ tcp_build_wnd_scale_option(u32_t *opts)
err_t err_t
tcp_send_empty_ack(struct tcp_pcb *pcb) tcp_send_empty_ack(struct tcp_pcb *pcb)
{ {
err_t err;
struct pbuf *p; struct pbuf *p;
u8_t optlen = 0; u8_t optlen = 0;
#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP #if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
@ -909,6 +910,8 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt)); p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
if (p == NULL) { if (p == NULL) {
/* let tcp_fasttmr retry sending this ACK */
pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
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;
} }
@ -917,8 +920,6 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ #endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); ("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);
/* NB. MSS and window scale options are only sent on SYNs, so ignore them here */ /* NB. MSS and window scale options are only sent on SYNs, so ignore them here */
#if LWIP_TCP_TIMESTAMPS #if LWIP_TCP_TIMESTAMPS
@ -934,15 +935,23 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
&pcb->local_ip, &pcb->remote_ip); &pcb->local_ip, &pcb->remote_ip);
#endif #endif
#if LWIP_NETIF_HWADDRHINT #if LWIP_NETIF_HWADDRHINT
ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, err = ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
IP_PROTO_TCP, &pcb->addr_hint); IP_PROTO_TCP, &pcb->addr_hint);
#else /* LWIP_NETIF_HWADDRHINT*/ #else /* LWIP_NETIF_HWADDRHINT*/
ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, err = ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
IP_PROTO_TCP); IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/ #endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free(p); pbuf_free(p);
return ERR_OK; if (err != ERR_OK) {
/* let tcp_fasttmr retry sending this ACK */
pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
} else {
/* remove ACK flags from the PCB, as we sent an empty ACK now */
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
return err;
} }
/** /**
@ -957,6 +966,7 @@ tcp_output(struct tcp_pcb *pcb)
{ {
struct tcp_seg *seg, *useg; struct tcp_seg *seg, *useg;
u32_t wnd, snd_nxt; u32_t wnd, snd_nxt;
err_t err;
#if TCP_CWND_DEBUG #if TCP_CWND_DEBUG
s16_t i = 0; s16_t i = 0;
#endif /* TCP_CWND_DEBUG */ #endif /* TCP_CWND_DEBUG */
@ -1041,17 +1051,23 @@ tcp_output(struct tcp_pcb *pcb)
++i; ++i;
#endif /* TCP_CWND_DEBUG */ #endif /* TCP_CWND_DEBUG */
pcb->unsent = seg->next;
if (pcb->state != SYN_SENT) { if (pcb->state != SYN_SENT) {
TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
} }
#if TCP_OVERSIZE_DBGCHECK #if TCP_OVERSIZE_DBGCHECK
seg->oversize_left = 0; seg->oversize_left = 0;
#endif /* TCP_OVERSIZE_DBGCHECK */ #endif /* TCP_OVERSIZE_DBGCHECK */
tcp_output_segment(seg, pcb); err = tcp_output_segment(seg, pcb);
if (err != ERR_OK) {
/* segment could not be sent, for whatever reason */
pcb->flags |= TF_NAGLEMEMERR;
return err;
}
pcb->unsent = seg->next;
if (pcb->state != SYN_SENT) {
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
pcb->snd_nxt = snd_nxt; pcb->snd_nxt = snd_nxt;
@ -1106,9 +1122,10 @@ tcp_output(struct tcp_pcb *pcb)
* @param seg the tcp_seg to send * @param seg the tcp_seg to send
* @param pcb the tcp_pcb for the TCP connection used to send the segment * @param pcb the tcp_pcb for the TCP connection used to send the segment
*/ */
static void static err_t
tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
{ {
err_t err;
u16_t len; u16_t len;
u32_t *opts; u32_t *opts;
@ -1174,7 +1191,7 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
ipX_addr_t *local_ip; ipX_addr_t *local_ip;
ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip);
if ((netif == NULL) || (local_ip == NULL)) { if ((netif == NULL) || (local_ip == NULL)) {
return; return ERR_RTE;
} }
ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip);
} }
@ -1237,12 +1254,13 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
TCP_STATS_INC(tcp.xmit); TCP_STATS_INC(tcp.xmit);
#if LWIP_NETIF_HWADDRHINT #if LWIP_NETIF_HWADDRHINT
ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, err = ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip,
pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint); pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint);
#else /* LWIP_NETIF_HWADDRHINT*/ #else /* LWIP_NETIF_HWADDRHINT*/
ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, err = ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
pcb->tos, IP_PROTO_TCP); pcb->tos, IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/ #endif /* LWIP_NETIF_HWADDRHINT*/
return err;
} }
/** /**
@ -1448,9 +1466,10 @@ tcp_rexmit_fast(struct tcp_pcb *pcb)
* *
* @param pcb the tcp_pcb for which to send a keepalive packet * @param pcb the tcp_pcb for which to send a keepalive packet
*/ */
void err_t
tcp_keepalive(struct tcp_pcb *pcb) tcp_keepalive(struct tcp_pcb *pcb)
{ {
err_t err;
struct pbuf *p; struct pbuf *p;
#if CHECKSUM_GEN_TCP #if CHECKSUM_GEN_TCP
struct tcp_hdr *tcphdr; struct tcp_hdr *tcphdr;
@ -1467,7 +1486,7 @@ tcp_keepalive(struct tcp_pcb *pcb)
if(p == NULL) { if(p == NULL) {
LWIP_DEBUGF(TCP_DEBUG, LWIP_DEBUGF(TCP_DEBUG,
("tcp_keepalive: could not allocate memory for pbuf\n")); ("tcp_keepalive: could not allocate memory for pbuf\n"));
return; return ERR_MEM;
} }
#if CHECKSUM_GEN_TCP #if CHECKSUM_GEN_TCP
tcphdr = (struct tcp_hdr *)p->payload; tcphdr = (struct tcp_hdr *)p->payload;
@ -1479,17 +1498,18 @@ tcp_keepalive(struct tcp_pcb *pcb)
/* Send output to IP */ /* Send output to IP */
#if LWIP_NETIF_HWADDRHINT #if LWIP_NETIF_HWADDRHINT
ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, err = ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip,
pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint); pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint);
#else /* LWIP_NETIF_HWADDRHINT*/ #else /* LWIP_NETIF_HWADDRHINT*/
ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, err = ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
0, IP_PROTO_TCP); 0, IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/ #endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free(p); pbuf_free(p);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n",
pcb->snd_nxt - 1, pcb->rcv_nxt)); pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
return err;
} }
@ -1501,9 +1521,10 @@ tcp_keepalive(struct tcp_pcb *pcb)
* *
* @param pcb the tcp_pcb for which to send a zero-window probe packet * @param pcb the tcp_pcb for which to send a zero-window probe packet
*/ */
void err_t
tcp_zero_window_probe(struct tcp_pcb *pcb) tcp_zero_window_probe(struct tcp_pcb *pcb)
{ {
err_t err;
struct pbuf *p; struct pbuf *p;
struct tcp_hdr *tcphdr; struct tcp_hdr *tcphdr;
struct tcp_seg *seg; struct tcp_seg *seg;
@ -1525,7 +1546,8 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
seg = pcb->unsent; seg = pcb->unsent;
} }
if(seg == NULL) { if(seg == NULL) {
return; /* nothing to send, zero window probe not needed */
return ERR_OK;
} }
is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0); is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
@ -1535,7 +1557,7 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno); p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
if(p == NULL) { if(p == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
return; return ERR_MEM;
} }
tcphdr = (struct tcp_hdr *)p->payload; tcphdr = (struct tcp_hdr *)p->payload;
@ -1559,16 +1581,17 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
/* Send output to IP */ /* Send output to IP */
#if LWIP_NETIF_HWADDRHINT #if LWIP_NETIF_HWADDRHINT
ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, err = ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
0, IP_PROTO_TCP, &pcb->addr_hint); 0, IP_PROTO_TCP, &pcb->addr_hint);
#else /* LWIP_NETIF_HWADDRHINT*/ #else /* LWIP_NETIF_HWADDRHINT*/
ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); err = ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
#endif /* LWIP_NETIF_HWADDRHINT*/ #endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free(p); pbuf_free(p);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
" ackno %"U32_F".\n", " ackno %"U32_F" err %d.\n",
pcb->snd_nxt - 1, pcb->rcv_nxt)); pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
return err;
} }
#endif /* LWIP_TCP */ #endif /* LWIP_TCP */

View File

@ -61,6 +61,11 @@ void tcp_tmr (void); /* Must be called every
void tcp_slowtmr (void); void tcp_slowtmr (void);
void tcp_fasttmr (void); void tcp_fasttmr (void);
/* Call this from a netif driver (watch out for threading issues!) that has
returned a memory error on transmit and now has free buffers to send more.
This iterates all active pcbs that had an error and tries to call
tcp_output, so use this with care as it might slow down the system. */
void tcp_txnow (void);
/* Only used by IP to pass a TCP segment to TCP: */ /* Only used by IP to pass a TCP segment to TCP: */
void tcp_input (struct pbuf *p, struct netif *inp); void tcp_input (struct pbuf *p, struct netif *inp);
@ -490,8 +495,8 @@ void tcp_rst_impl(u32_t seqno, u32_t ackno,
u32_t tcp_next_iss(void); u32_t tcp_next_iss(void);
void tcp_keepalive(struct tcp_pcb *pcb); err_t tcp_keepalive(struct tcp_pcb *pcb);
void tcp_zero_window_probe(struct tcp_pcb *pcb); err_t tcp_zero_window_probe(struct tcp_pcb *pcb);
#if TCP_CALCULATE_EFF_SEND_MSS #if TCP_CALCULATE_EFF_SEND_MSS
u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest