Ejemplo n.º 1
0
/**
 * Abandons a connection and optionally sends a RST to the remote
 * host.  Deletes the local protocol control block. This is done when
 * a connection is killed because of shortage of memory.
 *
 * @param pcb the tcp_pcb to abort
 * @param reset boolean to indicate whether a reset should be sent
 */
void tcp_abandon(TCP_PCB *pcb, uint16 reset)
{
    uint32 seqno, ackno;
    uint16 remote_port, local_port;
    IP_ADDR remote_ip, local_ip;

    /* if there is an outstanding delayed ACKs, send it */
    if (pcb->state != TIME_WAIT && pcb->flags & TF_ACK_DELAY)
    {
        pcb->flags |= TF_ACK_NOW;
        tcp_output(pcb);
    }

    /* Figure out on which TCP PCB list we are, and remove us. If we
       are in an active state, call the receive function associated with
       the PCB with a NULL argument, and send an RST to the remote end. */
    if (pcb->state == TIME_WAIT)
    {
        pcb->state = CLOSED;
        tcp_pcb_purge(pcb);
	    tcp_close(pcb);
    }
    else
    {
        seqno = pcb->snd_nxt;
        ackno = pcb->rcv_nxt;
        ip_addr_set(&local_ip, &(pcb->local_ip));
        ip_addr_set(&remote_ip, &(pcb->remote_ip));
        local_port = pcb->local_port;
        remote_port = pcb->remote_port;

        pcb->state = CLOSED;

        if (pcb->unacked != NULL)
        {
            tcp_segs_free(pcb->unacked);
			pcb->unacked = NULL;
        }

        if (pcb->unsent != NULL)
        {
            tcp_segs_free(pcb->unsent);
			pcb->unsent = NULL;
        }

        if (pcb->ooseq != NULL)
        {
            tcp_segs_free(pcb->ooseq);
			pcb->ooseq = NULL;
        }

        if (reset)
        {
			printf("tcp rst tcp_abandon!\n");
            tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
        }
    }
}
Ejemplo n.º 2
0
/******************************************************************************
 * FunctionName : espconn_tcp_disconnect
 * Description  : disconnect with host
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_tcp_disconnect_successful(void *arg)
{
	espconn_msg *pdiscon_cb = arg;
	sint8 dis_err = 0;
	espconn_buf *pdis_buf = NULL;
	espconn_buf *pdis_back = NULL;
	espconn_kill_oldest_pcb();
	if (pdiscon_cb != NULL) {
		struct espconn *espconn = pdiscon_cb->preverse;

		dis_err = pdiscon_cb->pcommon.err;
		if (pdiscon_cb->pespconn != NULL){
			struct tcp_pcb *pcb = NULL;
			if (espconn != NULL){/*Process the server's message block*/
				if (pdiscon_cb->pespconn->proto.tcp != NULL && espconn->proto.tcp){
					espconn_copy_partial(espconn, pdiscon_cb->pespconn);
					espconn_printf("server: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->remote_ip[0],
							espconn->proto.tcp->remote_ip[1],espconn->proto.tcp->remote_ip[2],
							espconn->proto.tcp->remote_ip[3],espconn->proto.tcp->remote_port);
					free(pdiscon_cb->pespconn->proto.tcp);
					pdiscon_cb->pespconn->proto.tcp = NULL;
				}
				free(pdiscon_cb->pespconn);
				pdiscon_cb->pespconn = NULL;
			} else {/*Process the client's message block*/
				espconn = pdiscon_cb->pespconn;
				espconn_printf("client: %d.%d.%d.%d : %d disconnect\n", espconn->proto.tcp->local_ip[0],
						espconn->proto.tcp->local_ip[1],espconn->proto.tcp->local_ip[2],
						espconn->proto.tcp->local_ip[3],espconn->proto.tcp->local_port);
			}
			/*process the current TCP block*/
			pcb = espconn_find_current_pcb(pdiscon_cb);
			if (pcb != NULL){
				if (espconn_reuse_disabled(pdiscon_cb)) {
					struct tcp_pcb *cpcb = NULL;
					struct tcp_pcb *prev = NULL;
					uint8_t pcb_remove;
					espconn_printf("espconn_tcp_disconnect_successful %d, %d\n",	pcb->state, pcb->local_port);
					cpcb = tcp_tw_pcbs;
					while (cpcb != NULL) {
						pcb_remove = 0;
						if (cpcb->local_port == pcb->local_port) {
							++pcb_remove;
						}
						/* If the PCB should be removed, do it. */
						if (pcb_remove) {
							struct tcp_pcb *backup_pcb = NULL;
							tcp_pcb_purge(cpcb);
							/* Remove PCB from tcp_tw_pcbs list. */
							if (prev != NULL) {
								LWIP_ASSERT("espconn_tcp_delete: middle cpcb != tcp_tw_pcbs",cpcb != tcp_tw_pcbs);
								prev->next = cpcb->next;
							} else {
								/* This PCB was the first. */
								LWIP_ASSERT("espconn_tcp_delete: first cpcb == tcp_tw_pcbs",tcp_tw_pcbs == cpcb);
								tcp_tw_pcbs = cpcb->next;
							}
							backup_pcb = cpcb;
							cpcb = cpcb->next;
							memp_free(MEMP_TCP_PCB, backup_pcb);
						} else {
							prev = cpcb;
							cpcb = cpcb->next;
						}
					}

				} else {
					tcp_arg(pcb, NULL);
					tcp_err(pcb, NULL);
				}
			}
		}

		/*to prevent memory leaks, ensure that each allocated is deleted*/
		pdis_buf = pdiscon_cb->pcommon.pbuf;
		while (pdis_buf != NULL) {
			pdis_back = pdis_buf;
			pdis_buf = pdis_back->pnext;
			espconn_pbuf_delete(&pdiscon_cb->pcommon.pbuf, pdis_back);
			free(pdis_back);
			pdis_back = NULL;
		}
		bzero(&pktinfo[0], sizeof(struct espconn_packet));
		memcpy(&pktinfo[0], (void*)&pdiscon_cb->pcommon.packet_info, sizeof(struct espconn_packet));
		free(pdiscon_cb);
		pdiscon_cb = NULL;
		if (espconn->proto.tcp && espconn->proto.tcp->disconnect_callback != NULL) {
			espconn->proto.tcp->disconnect_callback(espconn);
		}
	} else {
		espconn_printf("espconn_tcp_disconnect err\n");
	}
}
Ejemplo n.º 3
0
/**
 * Closes the TX side of a connection held by the PCB.
 * For tcp_close(), a RST is sent if the application didn't receive all data
 * (tcp_recved() not called for all data passed to recv callback).
 *
 * Listening pcbs are freed and may not be referenced any more.
 * Connection pcbs are freed if not yet connected and may not be referenced
 * any more. If a connection is established (at least SYN received or in
 * a closing state), the connection is closed, and put in a closing state.
 * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
 * unsafe to reference it.
 *
 * @param pcb the tcp_pcb to close
 * @return ERR_OK if connection has been closed
 *         another err_t if closing failed and pcb is not freed
 */
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 != LISTEN)) {
    if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
      /* Not all data received by application, send RST to tell the remote
         side about this. */
      LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);

      /* don't call tcp_abort here: we must not deallocate the pcb since
         that might not be expected when calling tcp_close */
      tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
        pcb->local_port, pcb->remote_port);

      tcp_pcb_purge(pcb);

      /* TODO: to which state do we move now? */

      /* move to TIME_WAIT since we close actively */
      TCP_RMV(&tcp_active_pcbs, pcb);
      pcb->state = TIME_WAIT;
      TCP_REG(&tcp_tw_pcbs, pcb);

      return ERR_OK;
    }
  }

  switch (pcb->state) {
  case CLOSED:
    /* Closing a pcb in the CLOSED state might seem erroneous,
     * however, it is in this state once allocated and as yet unused
     * and the user needs some way to free it should the need arise.
     * Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
     * 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_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(&tcp_active_pcbs, pcb);
    memp_free(MEMP_TCP_PCB, pcb);
    pcb = NULL;
    snmp_inc_tcpattemptfails();
    break;
  case SYN_RCVD:
    err = tcp_send_fin(pcb);
    if (err == ERR_OK) {
      snmp_inc_tcpattemptfails();
      pcb->state = FIN_WAIT_1;
    }
    break;
  case ESTABLISHED:
    err = tcp_send_fin(pcb);
    if (err == ERR_OK) {
      snmp_inc_tcpestabresets();
      pcb->state = FIN_WAIT_1;
    }
    break;
  case CLOSE_WAIT:
    err = tcp_send_fin(pcb);
    if (err == ERR_OK) {
      snmp_inc_tcpestabresets();
      pcb->state = LAST_ACK;
    }
    break;
  default:
    /* Has already been closed, do nothing. */
    err = ERR_OK;
    pcb = NULL;
    break;
  }

  if (pcb != NULL && 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. */
    /* @todo: When implementing SO_LINGER, this must be changed somehow:
       If SOF_LINGER is set, the data should be sent and acked before close returns.
       This can only be valid for sequential APIs, not for the raw API. */
    tcp_output(pcb);
  }
  return err;
}
Ejemplo n.º 4
0
/**
 * Called every 500 ms and implements the retransmission timer and the timer that
 * removes PCBs that have been in TIME-WAIT for enough time. It also increments
 * various timers such as the inactivity timer in each PCB.
 *
 * Automatically called from tcp_tmr().
 */
void tcp_slowtmr(void)
{
    TCP_PCB *pcb, *pcb2, *prev;
    uint16 eff_wnd;
    uint8 err= ERR_OK;

    ++tcp_ticks;

    /* Steps through all of the active PCBs. */
    prev = NULL;
    pcb = &stTcpPcb;

    if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX)
    {
        printf("SYN SENT CLOSED\n");
        pcb->state = CLOSED;
        tcp_pcb_purge(pcb);
	    tcp_close(pcb);
        return;
    }
    else if (pcb->nrtx == TCP_MAXRTX)
    {
        printf("TCP MAXRTX CLOSED\n");
        pcb->state = CLOSED;
        tcp_pcb_purge(pcb);
	    tcp_close(pcb);
        return;
    }
    else
    {
        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++;
                }
                tcp_zero_window_probe(pcb);
            }
        }
        else
        {
            /* Increase the retransmission timer if it is running */
            if (pcb->rtime >= 0)
                ++pcb->rtime;

            if (pcb->unacked != NULL && pcb->rtime >= pcb->rto)
            {
                /* Double retransmission time-out unless we are trying to
                 * connect to somebody (i.e., we are in SYN_SENT). */
                if (pcb->state != SYN_SENT)
                {
                    pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
                }

                /* Reset the retransmission timer. */
                pcb->rtime = 0;

                /* Reduce congestion window and ssthresh. */
                eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
                pcb->ssthresh = eff_wnd >> 1;
                if (pcb->ssthresh < pcb->mss)
                {
                    pcb->ssthresh = pcb->mss * 2;
                }
                pcb->cwnd = pcb->mss;

                /* The following needs to be called AFTER cwnd is set to one
                   mss - STJ */
                tcp_rexmit_rto(pcb);
            }
        }
    }
Ejemplo n.º 5
0
/**
 * Implements the TCP state machine. Called by tcp_input. In some
 * states tcp_receive() is called to receive data. The tcp_seg
 * argument will be freed by the caller (tcp_input()) unless the
 * recv_data pointer in the pcb is set.
 *
 * @param pcb the tcp_pcb for which a segment arrived
 *
 * @note the segment which arrived is saved in global variables, therefore only the pcb
 *       involved is passed as a parameter to this function
 */
static err_t
tcp_process(struct tcp_pcb *pcb)
{
  struct tcp_seg *rseg;
  u8_t acceptable = 0;
  err_t err;
  u8_t accepted_inseq;

  err = ERR_OK;

  /* Process incoming RST segments. */
  if (flags & TCP_RST) {
    /* First, determine if the reset is acceptable. */
    if (pcb->state == SYN_SENT) {
      if (ackno == pcb->snd_nxt) {
        acceptable = 1;
      }
    } else {
      if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
                          pcb->rcv_nxt+pcb->rcv_ann_wnd)) {
        acceptable = 1;
      }
    }

    if (acceptable) {
      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
      LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
      recv_flags = TF_RESET;
      pcb->flags &= ~TF_ACK_DELAY;
      return ERR_RST;
    } else {
      LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
       seqno, pcb->rcv_nxt));
      LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
       seqno, pcb->rcv_nxt));
      return ERR_OK;
    }
  }

  /* Update the PCB (in)activity timer. */
  pcb->tmr = tcp_ticks;
  pcb->keep_cnt_sent = 0;

  /* Do different things depending on the TCP state. */
  switch (pcb->state) {
  case SYN_SENT:
    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
     pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
    /* received SYN ACK with expected sequence number? */
    if ((flags & TCP_ACK) && (flags & TCP_SYN)
        && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
      pcb->snd_buf++;
      pcb->rcv_nxt = seqno + 1;
      pcb->lastack = ackno;
      pcb->snd_wnd = tcphdr->wnd;
      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
      pcb->state = ESTABLISHED;

      /* Parse any options in the SYNACK before using pcb->mss since that
       * can be changed by the received options! */
      tcp_parseopt(pcb);
#if TCP_CALCULATE_EFF_SEND_MSS
      pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */

      /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
       * but for the default value of pcb->mss) */
      pcb->ssthresh = pcb->mss * 10;

      pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
      LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
      --pcb->snd_queuelen;
      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
      rseg = pcb->unacked;
      pcb->unacked = rseg->next;

      /* If there's nothing left to acknowledge, stop the retransmit
         timer, otherwise reset it to start again */
      if(pcb->unacked == NULL)
        pcb->rtime = -1;
      else {
        pcb->rtime = 0;
        pcb->nrtx = 0;
      }

      tcp_seg_free(rseg);

      /* Call the user specified function to call when sucessfully
       * connected. */
      TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
      tcp_ack_now(pcb);
    }
    /* received ACK? possibly a half-open connection */
    else if (flags & TCP_ACK) {
      /* send a RST to bring the other side in a non-synchronized state. */
      tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
        tcphdr->dest, tcphdr->src);
    }
    break;
  case SYN_RCVD:
    if (flags & TCP_ACK &&
       !(flags & TCP_RST)) {
      /* expected ACK number? */
      if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
        u16_t old_cwnd;
        pcb->state = ESTABLISHED;
        LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
#if LWIP_CALLBACK_API
        LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
#endif
        /* Call the accept function. */
        TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
        if (err != ERR_OK) {
          /* If the accept function returns with an error, we abort
           * the connection. */
          tcp_abort(pcb);
          return ERR_ABRT;
        }
        old_cwnd = pcb->cwnd;
        /* If there was any data contained within this ACK,
         * we'd better pass it on to the application as well. */
        accepted_inseq = tcp_receive(pcb);

        pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);

        if ((flags & TCP_FIN) && accepted_inseq) {
          tcp_ack_now(pcb);
          pcb->state = CLOSE_WAIT;
        }
      }
      /* incorrect ACK number */
      else {
        /* send RST */
        tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
                tcphdr->dest, tcphdr->src);
      }
    }
    break;
  case CLOSE_WAIT:
    /* FALLTHROUGH */
  case ESTABLISHED:
    accepted_inseq = tcp_receive(pcb);
    if ((flags & TCP_FIN) && accepted_inseq) { /* passive close */
      tcp_ack_now(pcb);
      pcb->state = CLOSE_WAIT;
    }
    break;
  case FIN_WAIT_1:
    tcp_receive(pcb);
    if (flags & TCP_FIN) {
      if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
        LWIP_DEBUGF(TCP_DEBUG,
          ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
        tcp_ack_now(pcb);
        tcp_pcb_purge(pcb);
        TCP_RMV(&tcp_active_pcbs, pcb);
        pcb->state = TIME_WAIT;
        TCP_REG(&tcp_tw_pcbs, pcb);
      } else {
        tcp_ack_now(pcb);
        pcb->state = CLOSING;
      }
    } else if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
      pcb->state = FIN_WAIT_2;
    }
    break;
  case FIN_WAIT_2:
    tcp_receive(pcb);
    if (flags & TCP_FIN) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      tcp_ack_now(pcb);
      tcp_pcb_purge(pcb);
      TCP_RMV(&tcp_active_pcbs, pcb);
      pcb->state = TIME_WAIT;
      TCP_REG(&tcp_tw_pcbs, pcb);
    }
    break;
  case CLOSING:
    tcp_receive(pcb);
    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      tcp_ack_now(pcb);
      tcp_pcb_purge(pcb);
      TCP_RMV(&tcp_active_pcbs, pcb);
      pcb->state = TIME_WAIT;
      TCP_REG(&tcp_tw_pcbs, pcb);
    }
    break;
  case LAST_ACK:
    tcp_receive(pcb);
    if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
      LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
      /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
      recv_flags = TF_CLOSED;
    }
    break;
  default:
    break;
  }
  return ERR_OK;
}