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. */
pcb->persist_cnt++; u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1];
if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { if (pcb->persist_cnt < backoff_cnt) {
pcb->persist_cnt = 0; pcb->persist_cnt++;
if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { }
pcb->persist_backoff++; if (pcb->persist_cnt >= backoff_cnt) {
if (tcp_zero_window_probe(pcb) == ERR_OK) {
pcb->persist_cnt = 0;
if (pcb->persist_backoff < sizeof(tcp_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,8 +961,10 @@ 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);
pcb->keep_cnt_sent++; if (err == ERR_OK) {
pcb->keep_cnt_sent++;
}
} }
} }
@ -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;
@ -1465,9 +1484,9 @@ tcp_keepalive(struct tcp_pcb *pcb)
p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1)); p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
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