/** * 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); } } }
///发送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); } } }
/** * 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 ); }
/** * 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; }
/** * 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; } } }
/** * 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; } } }
/** * 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; }
/** * 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; }
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; }
/*-----------------------------------------------------------------------------------*/ 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; }