|
|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|
@ -1276,6 +1287,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; |
|
|
|
|
|
|
|
|
|
|