diff --git a/CHANGELOG b/CHANGELOG index 146b4b8a..0704a818 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -186,6 +186,13 @@ HISTORY ++ 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 * snmp*: made community writable, fixed some const pointers diff --git a/src/api/api_msg.c b/src/api/api_msg.c index 544213f9..cf416f6e 100644 --- a/src/api/api_msg.c +++ b/src/api/api_msg.c @@ -1482,6 +1482,7 @@ err_mem: } if (err == ERR_OK) { + err_t out_err; conn->write_offset += len; if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) { /* return sent length */ @@ -1490,18 +1491,34 @@ err_mem: write_finished = 1; 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) { /* 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 only a temporary error! */ /* 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 - conn->flags |= NETCONN_FLAG_WRITE_DELAYED; + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; #endif + } } else { /* On errors != ERR_MEM, we don't try writing any more but return the error to the application thread. */ diff --git a/src/core/tcp.c b/src/core/tcp.c index 5edc73cd..0225c2aa 100644 --- a/src/core/tcp.c +++ b/src/core/tcp.c @@ -879,13 +879,17 @@ tcp_slowtmr_start: if (pcb->persist_backoff > 0) { /* If snd_wnd is zero, use persist timer to send 1 byte probes * instead of using the standard retransmission mechanism. */ - pcb->persist_cnt++; - if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) { - pcb->persist_cnt = 0; - if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) { - pcb->persist_backoff++; + u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1]; + if (pcb->persist_cnt < backoff_cnt) { + pcb->persist_cnt++; + } + 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 { /* 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)) / TCP_SLOW_INTERVAL) { - tcp_keepalive(pcb); - pcb->keep_cnt_sent++; + err = tcp_keepalive(pcb); + 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 */ err_t tcp_process_refused_data(struct tcp_pcb *pcb) diff --git a/src/core/tcp_out.c b/src/core/tcp_out.c index 10ed021f..854ef2db 100644 --- a/src/core/tcp_out.c +++ b/src/core/tcp_out.c @@ -94,7 +94,7 @@ #endif /* 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 * functions other than the default tcp_output -> tcp_output_segment @@ -895,6 +895,7 @@ tcp_build_wnd_scale_option(u32_t *opts) err_t tcp_send_empty_ack(struct tcp_pcb *pcb) { + err_t err; struct pbuf *p; u8_t optlen = 0; #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)); 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")); return ERR_BUF; } @@ -917,8 +920,6 @@ tcp_send_empty_ack(struct tcp_pcb *pcb) #endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ 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); /* NB. MSS and window scale options are only sent on SYNs, so ignore them here */ #if LWIP_TCP_TIMESTAMPS @@ -934,15 +935,23 @@ tcp_send_empty_ack(struct tcp_pcb *pcb) &pcb->local_ip, &pcb->remote_ip); #endif #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); #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); #endif /* LWIP_NETIF_HWADDRHINT*/ 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; u32_t wnd, snd_nxt; + err_t err; #if TCP_CWND_DEBUG s16_t i = 0; #endif /* TCP_CWND_DEBUG */ @@ -1041,17 +1051,23 @@ tcp_output(struct tcp_pcb *pcb) ++i; #endif /* TCP_CWND_DEBUG */ - pcb->unsent = seg->next; - if (pcb->state != SYN_SENT) { TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); - pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); } #if TCP_OVERSIZE_DBGCHECK seg->oversize_left = 0; #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); if (TCP_SEQ_LT(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 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) { + err_t err; u16_t len; u32_t *opts; @@ -1174,7 +1191,7 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) ipX_addr_t *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)) { - return; + return ERR_RTE; } 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); #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); #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); #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 */ -void +err_t tcp_keepalive(struct tcp_pcb *pcb) { + err_t err; struct pbuf *p; #if CHECKSUM_GEN_TCP 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)); if(p == NULL) { - LWIP_DEBUGF(TCP_DEBUG, + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: could not allocate memory for pbuf\n")); - return; + return ERR_MEM; } #if CHECKSUM_GEN_TCP tcphdr = (struct tcp_hdr *)p->payload; @@ -1479,17 +1498,18 @@ tcp_keepalive(struct tcp_pcb *pcb) /* Send output to IP */ #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); #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); #endif /* LWIP_NETIF_HWADDRHINT*/ pbuf_free(p); - LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", - pcb->snd_nxt - 1, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n", + 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 */ -void +err_t tcp_zero_window_probe(struct tcp_pcb *pcb) { + err_t err; struct pbuf *p; struct tcp_hdr *tcphdr; struct tcp_seg *seg; @@ -1525,7 +1546,8 @@ tcp_zero_window_probe(struct tcp_pcb *pcb) seg = pcb->unsent; } 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); @@ -1535,7 +1557,7 @@ tcp_zero_window_probe(struct tcp_pcb *pcb) p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno); if(p == NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); - return; + return ERR_MEM; } tcphdr = (struct tcp_hdr *)p->payload; @@ -1559,16 +1581,17 @@ tcp_zero_window_probe(struct tcp_pcb *pcb) /* Send output to IP */ #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); #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*/ pbuf_free(p); LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F - " ackno %"U32_F".\n", - pcb->snd_nxt - 1, pcb->rcv_nxt)); + " ackno %"U32_F" err %d.\n", + pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err)); + return err; } #endif /* LWIP_TCP */ diff --git a/src/include/lwip/tcp_impl.h b/src/include/lwip/tcp_impl.h index d6f98c9e..f3e9470c 100644 --- a/src/include/lwip/tcp_impl.h +++ b/src/include/lwip/tcp_impl.h @@ -61,6 +61,11 @@ void tcp_tmr (void); /* Must be called every void tcp_slowtmr (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: */ 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); -void tcp_keepalive(struct tcp_pcb *pcb); -void tcp_zero_window_probe(struct tcp_pcb *pcb); +err_t tcp_keepalive(struct tcp_pcb *pcb); +err_t tcp_zero_window_probe(struct tcp_pcb *pcb); #if TCP_CALCULATE_EFF_SEND_MSS u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest