|
|
@@ -132,6 +132,8 @@ static u8_t tcp_timer;
|
|
|
static u8_t tcp_timer_ctr;
|
|
|
static u16_t tcp_new_port(void);
|
|
|
|
|
|
+static err_t tcp_close_shutdown_fin(struct tcp_pcb *pcb);
|
|
|
+
|
|
|
/**
|
|
|
* Initialize this module.
|
|
|
*/
|
|
|
@@ -258,8 +260,6 @@ tcp_backlog_accepted(struct tcp_pcb* pcb)
|
|
|
static err_t
|
|
|
tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
|
|
|
{
|
|
|
- err_t err;
|
|
|
-
|
|
|
if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
|
|
|
if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND_MAX(pcb))) {
|
|
|
/* Not all data received by application, send RST to tell the remote
|
|
|
@@ -290,6 +290,8 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* - states which free the pcb are handled here,
|
|
|
+ - states which send FIN and change state are handled in tcp_close_shutdown_impl() */
|
|
|
switch (pcb->state) {
|
|
|
case CLOSED:
|
|
|
/* Closing a pcb in the CLOSED state might seem erroneous,
|
|
|
@@ -299,27 +301,34 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
|
|
|
* or for a pcb that has been used and then entered the CLOSED state
|
|
|
* is erroneous, but this should never happen as the pcb has in those cases
|
|
|
* been freed, and so any remaining handles are bogus. */
|
|
|
- err = ERR_OK;
|
|
|
if (pcb->local_port != 0) {
|
|
|
TCP_RMV(&tcp_bound_pcbs, pcb);
|
|
|
}
|
|
|
memp_free(MEMP_TCP_PCB, pcb);
|
|
|
- pcb = NULL;
|
|
|
break;
|
|
|
case LISTEN:
|
|
|
- err = ERR_OK;
|
|
|
tcp_listen_closed(pcb);
|
|
|
tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
|
|
|
memp_free(MEMP_TCP_PCB_LISTEN, pcb);
|
|
|
- pcb = NULL;
|
|
|
break;
|
|
|
case SYN_SENT:
|
|
|
- err = ERR_OK;
|
|
|
TCP_PCB_REMOVE_ACTIVE(pcb);
|
|
|
memp_free(MEMP_TCP_PCB, pcb);
|
|
|
- pcb = NULL;
|
|
|
MIB2_STATS_INC(mib2.tcpattemptfails);
|
|
|
break;
|
|
|
+ default:
|
|
|
+ return tcp_close_shutdown_fin(pcb);
|
|
|
+ }
|
|
|
+ return ERR_OK;
|
|
|
+}
|
|
|
+
|
|
|
+static err_t
|
|
|
+tcp_close_shutdown_fin(struct tcp_pcb *pcb)
|
|
|
+{
|
|
|
+ err_t err;
|
|
|
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
|
|
|
+
|
|
|
+ switch (pcb->state) {
|
|
|
case SYN_RCVD:
|
|
|
err = tcp_send_fin(pcb);
|
|
|
if (err == ERR_OK) {
|
|
|
@@ -344,18 +353,20 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
|
|
|
break;
|
|
|
default:
|
|
|
/* Has already been closed, do nothing. */
|
|
|
- err = ERR_OK;
|
|
|
- pcb = NULL;
|
|
|
+ return ERR_OK;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- if (pcb != NULL && err == ERR_OK) {
|
|
|
+ if (err == ERR_OK) {
|
|
|
/* To ensure all data has been sent when tcp_close returns, we have
|
|
|
to make sure tcp_output doesn't fail.
|
|
|
Since we don't really have to ensure all data has been sent when tcp_close
|
|
|
returns (unsent data is sent from tcp timer functions, also), we don't care
|
|
|
for the return value of tcp_output for now. */
|
|
|
tcp_output(pcb);
|
|
|
+ } else if (err == ERR_MEM) {
|
|
|
+ /* Mark this pcb for closing. Closing is retried from tcp_tmr. */
|
|
|
+ pcb->flags |= TF_CLOSEPEND;
|
|
|
}
|
|
|
return err;
|
|
|
}
|
|
|
@@ -1244,6 +1255,12 @@ tcp_fasttmr_start:
|
|
|
tcp_output(pcb);
|
|
|
pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
|
|
|
}
|
|
|
+ /* send pending FIN */
|
|
|
+ if (pcb->flags & TF_CLOSEPEND) {
|
|
|
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: pending FIN\n"));
|
|
|
+ pcb->flags &= ~(TF_CLOSEPEND);
|
|
|
+ tcp_close_shutdown_fin(pcb);
|
|
|
+ }
|
|
|
|
|
|
next = pcb->next;
|
|
|
|