Пример #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);
        }
    }
}
Пример #2
0
 ///发送rst告诉远程  可以停止
 ///把本地的pcb给删了 ?free?
void
tcp_abandon(struct tcp_pcb *pcb, int reset)
{
  u32_t seqno, ackno;
  u16_t remote_port, local_port;
  struct ip_addr remote_ip, local_ip;
#if LWIP_CALLBACK_API  
  void (* errf)(void *arg, err_t err);
#endif /* LWIP_CALLBACK_API */
  void *errf_arg;

  
  /* 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. */
	 //pcb处于等待状态 ,删了
	 //pcb处于act状态,	调用接收函数,将pcb清空? 并且发送RST给远程
  if (pcb->state == TIME_WAIT) {
    printf("before jinhu 1 ");
    tcp_pcb_remove(&tcp_tw_pcbs, pcb);
    memp_free(MEMP_TCP_PCB, 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;
#if LWIP_CALLBACK_API
    errf = pcb->errf;
#endif /* LWIP_CALLBACK_API */
    errf_arg = pcb->callback_arg;
	printf("bero jinhu  2");
    tcp_pcb_remove(&tcp_active_pcbs, pcb);
    if (pcb->unacked != NULL) {
      tcp_segs_free(pcb->unacked);
    }
    if (pcb->unsent != NULL) {
      tcp_segs_free(pcb->unsent);
    }
#if TCP_QUEUE_OOSEQ    
    if (pcb->ooseq != NULL) {
      tcp_segs_free(pcb->ooseq);
    }
#endif /* TCP_QUEUE_OOSEQ */
    memp_free(MEMP_TCP_PCB, pcb);
    TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
    if (reset) {
      LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
      tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
    }
  }
}
Пример #3
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(struct tcp_pcb *pcb, int reset)
{
  u32_t seqno, ackno;
  u16_t remote_port, local_port;
  ip_addr_t remote_ip, local_ip;
#if LWIP_CALLBACK_API  
  tcp_err_fn errf;
#endif /* LWIP_CALLBACK_API */
  void *errf_arg;

  /* pcb->state LISTEN not allowed here */
  LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
    pcb->state != LISTEN);
  /* 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) {
    tcp_pcb_remove(&tcp_tw_pcbs, pcb);
    memp_free(MEMP_TCP_PCB, pcb);
  } else {
    seqno = pcb->snd_nxt;
    ackno = pcb->rcv_nxt;
    ip_addr_copy(local_ip, pcb->local_ip);
    ip_addr_copy(remote_ip, pcb->remote_ip);
    local_port = pcb->local_port;
    remote_port = pcb->remote_port;
#if LWIP_CALLBACK_API
    errf = pcb->errf;
#endif /* LWIP_CALLBACK_API */
    errf_arg = pcb->callback_arg;
    tcp_pcb_remove(&tcp_active_pcbs, pcb);
    if (pcb->unacked != NULL) {
      tcp_segs_free(pcb->unacked);
    }
    if (pcb->unsent != NULL) {
      tcp_segs_free(pcb->unsent);
    }
#if TCP_QUEUE_OOSEQ    
    if (pcb->ooseq != NULL) {
      tcp_segs_free(pcb->ooseq);
    }
#endif /* TCP_QUEUE_OOSEQ */
    memp_free(MEMP_TCP_PCB, pcb);
    TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
    if (reset) {
      LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
      tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port);
    }
  }
}
/**
 * Attempt to reclaim some memory from queued out-of-sequence TCP segments
 * if we run out of pool pbufs. It's better to give priority to new packets
 * if we're running out.
 *
 * This must be done in the correct thread context therefore this function
 * can only be used with NO_SYS=0 and through tcpip_callback.
 */
static void
pbuf_free_ooseq(void* arg)
{
  struct tcp_pcb* pcb;
  SYS_ARCH_DECL_PROTECT(old_level);
  LWIP_UNUSED_ARG(arg);

  SYS_ARCH_PROTECT(old_level);
  pbuf_free_ooseq_queued = 0;
  SYS_ARCH_UNPROTECT(old_level);

  sys_lock_acquire( &tcp_lock );

  for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
    if (NULL != pcb->ooseq) {
	  sys_lock_release( &tcp_lock );
      /** Free the ooseq pbufs of one PCB only */
      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
      tcp_segs_free(pcb->ooseq);
      pcb->ooseq = NULL;
      return;
    }
  }
  sys_lock_release( &tcp_lock );
}
Пример #5
0
/**
 * Insert segment into the list (segments covered with new one will be deleted)
 *
 * Called from tcp_receive()
 */
static void
tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
{
	struct tcp_seg *old_seg;

	if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
		/* received segment overlaps all following segments */
		tcp_segs_free(next);
		next = NULL;
	} else {
		/* delete some following segments
		   oos queue may have segments with FIN flag */
		while (next &&
			   TCP_SEQ_GEQ((seqno + cseg->len),
						   (next->tcphdr->seqno + next->len))) {
			/* cseg with FIN already processed */
			if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
				TCPH_FLAGS_SET(cseg->tcphdr, TCPH_FLAGS(cseg->tcphdr) | TCP_FIN);
			}
			old_seg = next;
			next = next->next;
			tcp_seg_free(old_seg);
		}
		if (next &&
			TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
			/* We need to trim the incoming segment. */
			cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
			pbuf_realloc(cseg->p, cseg->len);
		}
	}
	cseg->next = next;
}
Пример #6
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(struct tcp_pcb *pcb, int reset)
{
  u32_t seqno, ackno;
  u16_t remote_port, local_port;
  ip_addr_t remote_ip, local_ip;
#if LWIP_CALLBACK_API  
  tcp_err_fn errf;
#endif /* LWIP_CALLBACK_API */
  void *errf_arg;

  /* get_tcp_state(pcb) LISTEN not allowed here */
  LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
		  get_tcp_state(pcb) != LISTEN);
  /* 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 (get_tcp_state(pcb) == TIME_WAIT) {
    tcp_pcb_remove(pcb);
  } else {
    int send_rst = reset && (get_tcp_state(pcb) != CLOSED);
    seqno = pcb->snd_nxt;
    ackno = pcb->rcv_nxt;
    ip_addr_copy(local_ip, pcb->local_ip);
    ip_addr_copy(remote_ip, pcb->remote_ip);
    local_port = pcb->local_port;
    remote_port = pcb->remote_port;
#if LWIP_CALLBACK_API
    errf = pcb->errf;
#endif /* LWIP_CALLBACK_API */
    errf_arg = pcb->my_container;
    tcp_pcb_remove(pcb);
    if (pcb->unacked != NULL) {
      tcp_tx_segs_free(pcb, pcb->unacked);
      pcb->unacked = NULL;
    }
    if (pcb->unsent != NULL) {
      tcp_tx_segs_free(pcb, pcb->unsent);
      pcb->unsent = NULL;
    }
#if TCP_QUEUE_OOSEQ    
    if (pcb->ooseq != NULL) {
      tcp_segs_free(pcb, pcb->ooseq);
    }
#endif /* TCP_QUEUE_OOSEQ */
    TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
    if (send_rst) {
      LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
      tcp_rst(seqno, ackno, local_port, remote_port, pcb);
    }
  }
  (void)local_ip;  /* Fix warning -Wunused-but-set-variable */
  (void)remote_ip; /* Fix warning -Wunused-but-set-variable */
}
static
#endif /* !NO_SYS */
void
pbuf_free_ooseq(void)
{
  struct tcp_pcb* pcb;
  SYS_ARCH_SET(pbuf_free_ooseq_pending, 0);

  for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
    if (NULL != pcb->ooseq) {
      /** Free the ooseq pbufs of one PCB only */
      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
      tcp_segs_free(pcb->ooseq);
      pcb->ooseq = NULL;
      return;
    }
  }
}
Пример #8
0
/**
 * Attempt to reclaim some memory from queued out-of-sequence TCP segments
 * if we run out of pool pbufs. It's better to give priority to new packets
 * if we're running out.
 *
 * This must be done in the correct thread context therefore this function
 * can only be used with NO_SYS=0 and through tcpip_callback.
 */
#if !NO_SYS
//static
#endif /* !NO_SYS */
void
pbuf_free_ooseq(void)
{
  struct tcp_pcb* pcb;
  SYS_ARCH_DECL_PROTECT(old_level);

  SYS_ARCH_PROTECT(old_level);
  pbuf_free_ooseq_pending = 0;
  SYS_ARCH_UNPROTECT(old_level);

  for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
    if (NULL != pcb->ooseq) {
      /** Free the ooseq pbufs of one PCB only */
      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
      tcp_segs_free(pcb->ooseq);
      pcb->ooseq = NULL;
      return;
    }
  }
}
Пример #9
0
/**
 * Attempt to reclaim some memory from queued out-of-sequence TCP segments
 * if we run out of pool pbufs. It's better to give priority to new packets
 * if we're running out.
 *
 * This must be done in the correct thread context therefore this function
 * can only be used with NO_SYS=0 and through tcpip_callback.
 */
static void
pbuf_free_ooseq(void* arg)
{
  struct tcp_pcb* pcb;
  char cpu = sched_getcpu();
  SYS_ARCH_DECL_PROTECT(old_level);
  LWIP_UNUSED_ARG(arg);

  SYS_ARCH_PROTECT(old_level);
  pbuf_free_ooseq_queued = 0;
  SYS_ARCH_UNPROTECT(old_level);

  for (pcb = lwip_tcpip_thread[cpu]->tcpip_data.tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
    if (NULL != pcb->ooseq) {
      /** Free the ooseq pbufs of one PCB only */
      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
      tcp_segs_free(pcb->ooseq);
      pcb->ooseq = NULL;
      return;
    }
  }
}
/**
 * Enqueue either data or TCP options (but not both) for tranmission
 * 
 * 
 * 
 * @arg pcb Protocol control block for the TCP connection to enqueue data for.
 * @arg arg Pointer to the data to be enqueued for sending.
 * @arg len Data length in bytes
 * @arg flags
 * @arg copy 1 if data must be copied, 0 if data is non-volatile and can be
 * referenced.
 * @arg optdata
 * @arg optlen
 */
err_t
tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
  u8_t flags, u8_t copy,
  u8_t *optdata, u8_t optlen)
{
  struct pbuf *p;
  struct tcp_seg *seg, *useg, *queue;
  u32_t left, seqno;
  u16_t seglen;
  void *ptr;
  u8_t queuelen;

  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", copy=%"U16_F")\n",
    (void *)pcb, arg, len, (u16_t)flags, (u16_t)copy));
  LWIP_ASSERT("tcp_enqueue: len == 0 || optlen == 0 (programmer violates API)",
      len == 0 || optlen == 0);
  LWIP_ASSERT("tcp_enqueue: arg == NULL || optdata == NULL (programmer violates API)",
      arg == NULL || optdata == NULL);
  /* fail on too much data */
  if (len > pcb->snd_buf) {
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf));
    return ERR_MEM;
  }
  left = len;
  ptr = arg;

  /* seqno will be the sequence number of the first segment enqueued
   * by the call to this function. */
  seqno = pcb->snd_lbb;

  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));

  /* If total number of pbufs on the unsent/unacked queues exceeds the
   * configured maximum, return an error */
  queuelen = pcb->snd_queuelen;
  if (queuelen >= TCP_SND_QUEUELEN) {
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
    TCP_STATS_INC(tcp.memerr);
    return ERR_MEM;
  }
  if (queuelen != 0) {
    LWIP_ASSERT("tcp_enqueue: pbufs on queue => at least one queue non-empty",
      pcb->unacked != NULL || pcb->unsent != NULL);
  } else {
    LWIP_ASSERT("tcp_enqueue: no pbufs on queue => both queues empty",
      pcb->unacked == NULL && pcb->unsent == NULL);
  }

  /* First, break up the data into segments and tuck them together in
   * the local "queue" variable. */
  useg = queue = seg = NULL;
  seglen = 0;
  while (queue == NULL || left > 0) {

    /* The segment length should be the MSS if the data to be enqueued
     * is larger than the MSS. */
    seglen = left > pcb->mss? pcb->mss: left;

    /* Allocate memory for tcp_seg, and fill in fields. */
    seg = memp_malloc(MEMP_TCP_SEG);
    if (seg == NULL) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for tcp_seg\n"));
      goto memerr;
    }
    seg->next = NULL;
    seg->p = NULL;

    /* first segment of to-be-queued data? */
    if (queue == NULL) {
      queue = seg;
    }
    /* subsequent segments of to-be-queued data */
    else {
      /* Attach the segment to the end of the queued segments */
      LWIP_ASSERT("useg != NULL", useg != NULL);
      useg->next = seg;
    }
    /* remember last segment of to-be-queued data for next iteration */
    useg = seg;

    /* If copy is set, memory should be allocated
     * and data copied into pbuf, otherwise data comes from
     * ROM or other static memory, and need not be copied. If
     * optdata is != NULL, we have options instead of data. */
     
    /* options? */
    if (optdata != NULL) {
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
        goto memerr;
      }
      ++queuelen;
      seg->dataptr = seg->p->payload;
    }
    /* copy from volatile memory? */
    else if (copy) {
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) {
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
        goto memerr;
      }
      ++queuelen;
      if (arg != NULL) {
        memcpy(seg->p->payload, ptr, seglen);
      }
      seg->dataptr = seg->p->payload;
    }
    /* do not copy data */
    else {
      /* First, allocate a pbuf for holding the data.
       * since the referenced data is available at least until it is sent out on the
       * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM
       * instead of PBUF_REF here.
       */
      if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n"));
        goto memerr;
      }
      ++queuelen;
      /* reference the non-volatile payload data */
      p->payload = ptr;
      seg->dataptr = ptr;

      /* Second, allocate a pbuf for the headers. */
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) {
        /* If allocation fails, we have to deallocate the data pbuf as
         * well. */
        pbuf_free(p);
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n"));
        goto memerr;
      }
      ++queuelen;

      /* Concatenate the headers and data pbufs together. */
      pbuf_cat(seg->p/*header*/, p/*data*/);
      p = NULL;
    }

    /* Now that there are more segments queued, we check again if the
    length of the queue exceeds the configured maximum. */
    if (queuelen > TCP_SND_QUEUELEN) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
      goto memerr;
    }

    seg->len = seglen;

    /* build TCP header */
    if (pbuf_header(seg->p, TCP_HLEN)) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n"));
      TCP_STATS_INC(tcp.err);
      goto memerr;
    }
    seg->tcphdr = seg->p->payload;
    seg->tcphdr->src = htons(pcb->local_port);
    seg->tcphdr->dest = htons(pcb->remote_port);
    seg->tcphdr->seqno = htonl(seqno);
    seg->tcphdr->urgp = 0;
    TCPH_FLAGS_SET(seg->tcphdr, flags);
    /* don't fill in tcphdr->ackno and tcphdr->wnd until later */

    /* Copy the options into the header, if they are present. */
    if (optdata == NULL) {
      TCPH_HDRLEN_SET(seg->tcphdr, 5);
    }
    else {
      TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4));
      /* Copy options into data portion of segment.
       Options can thus only be sent in non data carrying
       segments such as SYN|ACK. */
      memcpy(seg->dataptr, optdata, optlen);
    }
    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
      ntohl(seg->tcphdr->seqno),
      ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
      (u16_t)flags));

    left -= seglen;
    seqno += seglen;
    ptr = (void *)((u8_t *)ptr + seglen);
  }

  /* Now that the data to be enqueued has been broken up into TCP
  segments in the queue variable, we add them to the end of the
  pcb->unsent queue. */
  if (pcb->unsent == NULL) {
    useg = NULL;
  }
  else {
    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
  }
  /* { useg is last segment on the unsent queue, NULL if list is empty } */

  /* If there is room in the last pbuf on the unsent queue,
  chain the first pbuf on the queue together with that. */
  if (useg != NULL &&
    TCP_TCPLEN(useg) != 0 &&
    !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) &&
    !(flags & (TCP_SYN | TCP_FIN)) &&
    /* fit within max seg size */
    useg->len + queue->len <= pcb->mss) {
    /* Remove TCP header from first segment of our to-be-queued list */
    pbuf_header(queue->p, -TCP_HLEN);
    pbuf_cat(useg->p, queue->p);
    useg->len += queue->len;
    useg->next = queue->next;

    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE | DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len));
    if (seg == queue) {
      seg = NULL;
    }
    memp_free(MEMP_TCP_SEG, queue);
  }
  else {
    /* empty list */
    if (useg == NULL) {
      /* initialize list with this segment */
      pcb->unsent = queue;
    }
    /* enqueue segment */
    else {
      useg->next = queue;
    }
  }
  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
    ++len;
  }
  pcb->snd_lbb += len;

  pcb->snd_buf -= len;

  /* update number of segments on the queues */
  pcb->snd_queuelen = queuelen;
  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
  if (pcb->snd_queuelen != 0) {
    LWIP_ASSERT("tcp_enqueue: valid queue length",
      pcb->unacked != NULL || pcb->unsent != NULL);
  }

  /* Set the PSH flag in the last segment that we enqueued, but only
  if the segment has data (indicated by seglen > 0). */
  if (seg != NULL && seglen > 0 && seg->tcphdr != NULL) {
    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
  }

  return ERR_OK;
memerr:
  TCP_STATS_INC(tcp.memerr);

  if (queue != NULL) {
    tcp_segs_free(queue);
  }
  if (pcb->snd_queuelen != 0) {
    LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
      pcb->unsent != NULL);
  }
  LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
  return ERR_MEM;
}
Пример #11
0
/**
 * Closes the connection held by the PCB.
 *
 * 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 uint8 if closing failed and pcb is not freed
 */
uint8 tcp_close(TCP_PCB *pcb)
{
    uint8 err = ERR_OK;

	tcp_tick_ack_unable(pcb);//printf("pcb->timerackflag = 0\n");
    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. */
            pcb->pcb_close(TCP_CLOSED);//Http_Check_Tcp_State
            memset(pcb, 0, sizeof(pcb));
            pcb = NULL;
            break;

        case SYN_SENT:
            tcp_pcb_remove_nolist(pcb);
            if (pcb->unacked != NULL)
            {
                tcp_segs_free(pcb->unacked);
    			pcb->unacked = NULL;
            }
            if (pcb->unsent != NULL)
            {
                tcp_segs_free(pcb->unsent);
    			pcb->unsent = NULL;
            }
            pcb->pcb_close(TCP_CLOSED);//Http_Check_Tcp_State
            memset(pcb, 0, sizeof(pcb));
            pcb = NULL;
            break;

        case ESTABLISHED:
            err = tcp_send_ctrl(pcb, TCP_FIN);
            if (err == ERR_OK)
            {
             	pcb->state = FIN_WAIT_1;
            }
            break;

        case CLOSE_WAIT:
            err = tcp_send_ctrl(pcb, TCP_FIN);
            if (err == ERR_OK) 
			{
	            pcb->state = LAST_ACK;
            }
            break;

        default:
            /* Has already been closed, do nothing. */
            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 when tcp_close returns. */
        tcp_output(pcb);
    }
    return err;
}
Пример #12
0
err_t tcp_enqueue (
       struct tcp_pcb* pcb, void* arg, u16_t len,
       u8_t          flags, u8_t            copy,
       u8_t*       optdata, u8_t          optlen
      ) {

 struct pbuf*    p;
 struct tcp_seg* seg, *useg, *queue;
 u32_t           left, seqno;
 u16_t           seglen;
 void*           ptr;
 u8_t            queuelen;
 flags_t         lPCBFlags = pcb -> flags;
 int             iSegCNT   = 0;

 left = len;
 ptr  = arg;

 if ( len > pcb -> snd_buf ) return ERR_MEM;

 seqno    = pcb -> snd_lbb;
 queue    = NULL;
 queuelen = pcb -> snd_queuelen;

 if ( queuelen >= TCP_SND_QUEUELEN ) goto memerr;

 seg    = useg = NULL;
 seglen = 0;

 while ( !queue || left > 0 ) {

  if ( lPCBFlags & TF_EVENSEG ) {

   ++iSegCNT;

   seglen = left > pcb -> mss ? pcb -> mss
                              : (((iSegCNT%2) == 1)? ((left + 1) / 2): left);
  } else seglen = left > pcb -> mss ? pcb -> mss : left;

  seg = memp_malloc ( MEMP_TCP_SEG );

  if ( !seg ) goto memerr;

  seg -> next = NULL;
  seg -> p    = NULL;

  if ( !queue )
   useg = queue = seg;
  else {
   useg -> next = seg;
   useg         = seg;
  }  /* end else */

    if (optdata != NULL) {
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
        goto memerr;
      }
      ++queuelen;
      seg->dataptr = seg->p->payload;
    }
    else if (copy) {
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) {
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %u\n", seglen));
        goto memerr;
      }
      ++queuelen;
      if (arg != NULL) {
        mips_memcpy(seg->p->payload, ptr, seglen);
      }
      seg->dataptr = seg->p->payload;
    }
    /* do not copy data */
    else {

      /* first, allocate a pbuf for holding the data.
       * since the referenced data is available at least until it is sent out on the
       * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM
       * instead of PBUF_REF here.
       */
      if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n"));
        goto memerr;
      }
      ++queuelen;
      p->payload = ptr;
      seg->dataptr = ptr;

      /* Second, allocate a pbuf for the headers. */
      if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) {
        /* If allocation fails, we have to deallocate the data pbuf as
         * well. */
        pbuf_free(p);
        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n"));
        goto memerr;
      }
      ++queuelen;

      /* Concatenate the headers and data pbufs together. */
      pbuf_cat(seg->p, p);
      p = NULL;
    }

    /* Now that there are more segments queued, we check again if the
    length of the queue exceeds the configured maximum. */
    if (queuelen > TCP_SND_QUEUELEN) {
      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %u (%u)\n", queuelen, TCP_SND_QUEUELEN));
      goto memerr;
    }

    seg->len = seglen;

    if (pbuf_header(seg->p, TCP_HLEN)) {

      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n"));

      TCP_STATS_INC(tcp.err);
      goto memerr;
    }
    seg->tcphdr = seg->p->payload;
    seg->tcphdr->src = htons(pcb->local_port);
    seg->tcphdr->dest = htons(pcb->remote_port);
    seg->tcphdr->seqno = htonl(seqno);
    seg->tcphdr->urgp = 0;
    TCPH_FLAGS_SET(seg->tcphdr, flags);
    /* don't fill in tcphdr->ackno and tcphdr->wnd until later */

    /* Copy the options into the header, if they are present. */
    if (optdata == NULL) {
      TCPH_HDRLEN_SET(seg->tcphdr, 5);
    }
    else {
      TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4));
      /* Copy options into data portion of segment.
       Options can thus only be sent in non data carrying
       segments such as SYN|ACK. */
      mips_memcpy(seg->dataptr, optdata, optlen);
    }

    left -= seglen;
    seqno += seglen;
    ptr = (void *)((char *)ptr + seglen);
  }


  /* Now that the data to be enqueued has been broken up into TCP
  segments in the queue variable, we add them to the end of the
  pcb->unsent queue. */
  if (pcb->unsent == NULL) {
    useg = NULL;
  }
  else {
    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
  }

  /* If there is room in the last pbuf on the unsent queue,
  chain the first pbuf on the queue together with that. */
  if (useg != NULL &&
    TCP_TCPLEN(useg) != 0 &&
    !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) &&
    !(flags & (TCP_SYN | TCP_FIN)) &&
    useg->len + queue->len <= pcb->mss) {
    /* Remove TCP header from first segment. */
    pbuf_header(queue->p, -TCP_HLEN);
    pbuf_cat(useg->p, queue->p);
    useg->len += queue->len;
    useg->next = queue->next;

    if (seg == queue) seg = NULL;

    memp_free(MEMP_TCP_SEG, queue);
  }
  else {
    if (useg == NULL) {
      pcb->unsent = queue;

    }
    else {
      useg->next = queue;
    }
  }
  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
    ++len;
  }
  pcb->snd_lbb += len;
  pcb->snd_buf -= len;
  pcb->snd_queuelen = queuelen;
  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (after enqueued)\n", pcb->snd_queuelen));
  if (pcb->snd_queuelen != 0) {
    LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
      pcb->unsent != NULL);

  }

  /* Set the PSH flag in the last segment that we enqueued, but only
  if the segment has data (indicated by seglen > 0). */
  if (seg != NULL && seglen > 0 && seg->tcphdr != NULL) {
    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
  }

  return ERR_OK;
  memerr:
  TCP_STATS_INC(tcp.memerr);

  if (queue != NULL) {
    tcp_segs_free(queue);
  }
  if (pcb->snd_queuelen != 0) {
    LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
      pcb->unsent != NULL);

  }
  LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen));
  return ERR_MEM;
}
Пример #13
0
/*-----------------------------------------------------------------------------------*/
err_t
tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len,
	    u8_t flags, u8_t copy,
            u8_t *optdata, u8_t optlen)
{
  struct pbuf *p;
  struct tcp_seg *seg, *useg, *queue;
  u32_t left, seqno;
  u16_t seglen;
  void *ptr;
  u8_t queuelen;

  left = len;
  ptr = arg;
  
  if(len > pcb->snd_buf) {
    DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too much data %d\n", len));
    return ERR_MEM;
  }
  
  seqno = pcb->snd_lbb;
  
  queue = NULL;
  DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d\n", pcb->snd_queuelen));
  queuelen = pcb->snd_queuelen;
  if(queuelen >= TCP_SND_QUEUELEN) {
    DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: too long queue %d (max %d)\n", queuelen, TCP_SND_QUEUELEN));
    goto memerr;
  }   
  
#ifdef LWIP_DEBUG
  if(pcb->snd_queuelen != 0) {
    ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
	   pcb->unsent != NULL);      
  }
#endif /* LWIP_DEBUG */
  
  seg = NULL;
  seglen = 0;
  
  while(queue == NULL || left > 0) {
    
    seglen = left > pcb->mss? pcb->mss: left;
    
    /* allocate memory for tcp_seg, and fill in fields */
    seg = memp_malloc(MEMP_TCP_SEG);
    if(seg == NULL) {
      DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for tcp_seg\n"));
      goto memerr;
    }
    seg->next = NULL;
    seg->p = NULL;
    
    
    if(queue == NULL) {
      queue = seg;
    } else {
      for(useg = queue; useg->next != NULL; useg = useg->next);
      useg->next = seg;
    }
      
    /* If copy is set, memory should be allocated
       and data copied into pbuf, otherwise data comes from
       ROM or other static memory, and need not be copied. If
       optdata is != NULL, we have options instead of data. */
    if(optdata != NULL) {
      if((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
	goto memerr;
      }
      ++queuelen;
      seg->dataptr = seg->p->payload;
    } else if(copy) {
      if((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) {
	DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf copy\n"));	  
	goto memerr;
      }
      ++queuelen;
      if(arg != NULL) {
	memcpy(seg->p->payload, ptr, seglen);
      }
      seg->dataptr = seg->p->payload;
    } else {
      /* Do not copy the data. */
      if((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
	DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for pbuf non-copy\n"));	  	  
	goto memerr;
      }
      ++queuelen;
      p->payload = ptr;
      seg->dataptr = ptr;
      if((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) {
	pbuf_free(p);
	DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: could not allocate memory for header pbuf\n"));		  
	goto memerr;
      }
      ++queuelen;
      pbuf_chain(seg->p, p);
    }
    if(queuelen > TCP_SND_QUEUELEN) {
      DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queue too long %d (%d)\n", queuelen, TCP_SND_QUEUELEN)); 	
      goto memerr;
    }
      
    seg->len = seglen;
    /*    if((flags & TCP_SYN) || (flags & TCP_FIN)) { 
      ++seg->len;
      }*/
      
    /* build TCP header */
    if(pbuf_header(seg->p, TCP_HLEN)) {
	
      DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: no room for TCP header in pbuf.\n"));
	
#ifdef TCP_STATS
      ++stats.tcp.err;
#endif /* TCP_STATS */
      goto memerr;
    }
    seg->tcphdr = seg->p->payload;
    seg->tcphdr->src = htons(pcb->local_port);
    seg->tcphdr->dest = htons(pcb->remote_port);
    seg->tcphdr->seqno = htonl(seqno);
    seg->tcphdr->urgp = 0;
    TCPH_FLAGS_SET(seg->tcphdr, flags);
    /* don't fill in tcphdr->ackno and tcphdr->wnd until later */
      
    if(optdata == NULL) {
      TCPH_OFFSET_SET(seg->tcphdr, 5 << 4);
    } else {
      TCPH_OFFSET_SET(seg->tcphdr, (5 + optlen / 4) << 4);
      /* Copy options into data portion of segment.
	 Options can thus only be sent in non data carrying
	 segments such as SYN|ACK. */
      memcpy(seg->dataptr, optdata, optlen);
    }
    DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue: queueing %lu:%lu (0x%x)\n",
			      ntohl(seg->tcphdr->seqno),
			      ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
			      flags));

    left -= seglen;
    seqno += seglen;
    ptr = (void *)((char *)ptr + seglen);
  }

    
  /* Go to the last segment on the ->unsent queue. */    
  if(pcb->unsent == NULL) {
    useg = NULL;
  } else {
    for(useg = pcb->unsent; useg->next != NULL; useg = useg->next);
  }
    
  /* If there is room in the last pbuf on the unsent queue,
     chain the first pbuf on the queue together with that. */
  if(useg != NULL &&
     TCP_TCPLEN(useg) != 0 &&
     !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) &&
     !(flags & (TCP_SYN | TCP_FIN)) &&
     useg->len + queue->len <= pcb->mss) {
    /* Remove TCP header from first segment. */
    pbuf_header(queue->p, -TCP_HLEN);
    pbuf_chain(useg->p, queue->p);
    useg->len += queue->len;
    useg->next = queue->next;
      
    DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: chaining, new len %u\n", useg->len));
    if(seg == queue) {
      seg = NULL;
    }
    memp_free(MEMP_TCP_SEG, queue);
  } else {      
    if(useg == NULL) {
      pcb->unsent = queue;
    } else {
      useg->next = queue;
    }
  }
  if((flags & TCP_SYN) || (flags & TCP_FIN)) {
    ++len;
  }
  pcb->snd_lbb += len;
  pcb->snd_buf -= len;
  pcb->snd_queuelen = queuelen;
  DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (after enqueued)\n", pcb->snd_queuelen));
#ifdef LWIP_DEBUG
  if(pcb->snd_queuelen != 0) {
    ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
	   pcb->unsent != NULL);
      
  }
#endif /* LWIP_DEBUG */
    
  /* Set the PSH flag in the last segment that we enqueued, but only
     if the segment has data (indicated by seglen > 0). */
  if(seg != NULL && seglen > 0 && seg->tcphdr != NULL) {
    TCPH_FLAGS_SET(seg->tcphdr, TCPH_FLAGS(seg->tcphdr) | TCP_PSH);
  }
  
  return ERR_OK;
 memerr:
#ifdef TCP_STATS
  ++stats.tcp.memerr;
#endif /* TCP_STATS */

  if(queue != NULL) {
    tcp_segs_free(queue);
  }
#ifdef LWIP_DEBUG
    if(pcb->snd_queuelen != 0) {
      ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL ||
	     pcb->unsent != NULL);
      
    }
#endif /* LWIP_DEBUG */
    DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %d (with mem err)\n", pcb->snd_queuelen));
  return ERR_MEM;
}