/* * Aborts a connection by sending a RST to the remote host and deletes * the local protocol control block. This is done when a connection is * killed because of shortage of memory. */ void tcp_abort (tcp_socket_t *s) { ip_t *ip = s->ip; unsigned long seqno, ackno; unsigned short remote_port, local_port; mutex_lock (&s->lock); /* Figure out on which TCP PCB list we are, and remove us. If we * are in an active state, send an RST to the remote end. */ if (s->state == TIME_WAIT) { tcp_queue_free (s); mutex_unlock (&s->lock); mutex_lock (&ip->lock); tcp_socket_remove (&ip->tcp_closing_sockets, s); mutex_unlock (&ip->lock); return; } seqno = s->snd_nxt; ackno = s->rcv_nxt; local_port = s->local_port; remote_port = s->remote_port; tcp_queue_free (s); mutex_unlock (&s->lock); mutex_lock (&ip->lock); tcp_socket_remove (&ip->tcp_sockets, s); tcp_socket_purge(s); tcp_debug ("tcp_abort: sending RST\n"); tcp_rst (ip, seqno, ackno, ipref_as_ucs(s->local_ip), s->remote_ip.ucs, local_port, remote_port); mutex_unlock (&ip->lock); }
/** * Called by tcp_input() when a segment arrives for a connection in * TIME_WAIT. * * @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_timewait_input(struct tcp_pcb *pcb) { /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ /* RFC 793 3.9 Event Processing - Segment Arrives: * - first check sequence number - we skip that one in TIME_WAIT (always * acceptable since we only send ACKs) * - second check the RST bit (... return) */ if (flags & TCP_RST) { return ERR_OK; } /* - fourth, check the SYN bit, */ if (flags & TCP_SYN) { /* If an incoming segment is not acceptable, an acknowledgment should be sent in reply */ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { /* If the SYN is in the window it is an error, send a reset */ tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), tcphdr->dest, tcphdr->src); return ERR_OK; } } else if (flags & TCP_FIN) { /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. Restart the 2 MSL time-wait timeout.*/ pcb->tmr = tcp_ticks; } if ((tcplen > 0)) { /* Acknowledge data, FIN or out-of-window SYN */ pcb->flags |= TF_ACK_NOW; return tcp_output(pcb); } return ERR_OK; }
/** * 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); } } }
/** * 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 */ }
///发送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); } } }
/* * Aborts a connection by sending a RST to the remote host and deletes * the local protocol control block. This is done when a connection is * killed because of shortage of memory. */ void tcp_abort (tcp_socket_t *s) { ip_t *ip = s->ip; unsigned long seqno, ackno; unsigned short remote_port, local_port; unsigned char remote_ip[4], local_ip[4]; mutex_lock (&s->lock); /* Figure out on which TCP PCB list we are, and remove us. If we * are in an active state, send an RST to the remote end. */ if (s->state == TIME_WAIT) { tcp_queue_free (s); mutex_unlock (&s->lock); mutex_lock (&ip->lock); tcp_socket_remove (&ip->tcp_closing_sockets, s); mutex_unlock (&ip->lock); return; } seqno = s->snd_nxt; ackno = s->rcv_nxt; memcpy (local_ip, s->local_ip, 4); memcpy (remote_ip, s->remote_ip, 4); local_port = s->local_port; remote_port = s->remote_port; tcp_queue_free (s); mutex_unlock (&s->lock); mutex_lock (&ip->lock); tcp_socket_remove (&ip->tcp_sockets, s); if (s->unacked != 0) { tcp_segments_free (s->unacked); } if (s->unsent != 0) { tcp_segments_free (s->unsent); } tcp_debug ("tcp_abort: sending RST\n"); tcp_rst (ip, seqno, ackno, local_ip, remote_ip, local_port, remote_port); mutex_unlock (&ip->lock); }
/*-----------------------------------------------------------------------------------*/ void tcp_abort(struct tcp_pcb *pcb) { u32_t seqno, ackno; u16_t remote_port, local_port; struct ip_addr remote_ip, local_ip; void (* errf)(void *arg, err_t err); 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. */ if(pcb->state == TIME_WAIT) { tcp_pcb_remove(TCP_LIST_TW, pcb); memp_free(MEMP_TCP_PCB, pcb); } else if(pcb->state == LISTEN) { tcp_pcb_remove(TCP_LIST_LISTEN, pcb); memp_free(MEMP_TCP_PCB_LISTEN, 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; errf = pcb->errf; errf_arg = pcb->callback_arg; tcp_pcb_remove(TCP_LIST_ACTIVE, pcb); memp_free(MEMP_TCP_PCB, pcb); if(errf != NULL) { errf(errf_arg, ERR_ABRT); } DEBUGF(TCP_RST_DEBUG, ("tcp_abort: sending RST\n")); tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); } }
/** * 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; }
/** * The initial input processing of TCP. It verifies the TCP header, demultiplexes * the segment between the PCBs and passes it on to tcp_process(), which implements * the TCP finite state machine. This function is called by the IP layer (in * ip_input()). * * @param p received TCP segment to process (p->payload pointing to the IP header) * @param inp network interface on which this segment was received */ void tcp_input(struct pbuf *p, struct netif *inp) { struct tcp_pcb *pcb, *prev; struct tcp_pcb_listen *lpcb; u8_t hdrlen; err_t err; PERF_START; TCP_STATS_INC(tcp.recv); snmp_inc_tcpinsegs(); iphdr = p->payload; tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); #if TCP_INPUT_DEBUG tcp_debug_print(tcphdr); #endif /* remove header from payload */ if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) { /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); TCP_STATS_INC(tcp.lenerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } /* Don't even process incoming broadcasts/multicasts. */ if (ip_addr_isbroadcast(&(iphdr->dest), inp) || ip_addr_ismulticast(&(iphdr->dest))) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } #if CHECKSUM_CHECK_TCP /* Verify TCP checksum. */ if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_TCP, p->tot_len) != 0) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_TCP, p->tot_len))); #if TCP_DEBUG tcp_debug_print(tcphdr); #endif /* TCP_DEBUG */ TCP_STATS_INC(tcp.chkerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } #endif /* Move the payload pointer in the pbuf so that it points to the TCP data instead of the TCP header. */ hdrlen = TCPH_HDRLEN(tcphdr); if(pbuf_header(p, -(hdrlen * 4))){ /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); TCP_STATS_INC(tcp.lenerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } /* Convert fields in TCP header to host byte order. */ tcphdr->src = ntohs(tcphdr->src); tcphdr->dest = ntohs(tcphdr->dest); seqno = tcphdr->seqno = ntohl(tcphdr->seqno); ackno = tcphdr->ackno = ntohl(tcphdr->ackno); tcphdr->wnd = ntohs(tcphdr->wnd); flags = TCPH_FLAGS(tcphdr) & TCP_FLAGS; tcplen = p->tot_len + ((flags & TCP_FIN || flags & TCP_SYN)? 1: 0); /* Demultiplex an incoming segment. First, we check if it is destined for an active connection. */ prev = NULL; for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); if (pcb->remote_port == tcphdr->src && pcb->local_port == tcphdr->dest && ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); if (prev != NULL) { prev->next = pcb->next; pcb->next = tcp_active_pcbs; tcp_active_pcbs = pcb; } LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); break; } prev = pcb; } if (pcb == NULL) { /* If it did not go to an active connection, we check the connections in the TIME-WAIT state. */ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); if (pcb->remote_port == tcphdr->src && pcb->local_port == tcphdr->dest && ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { /* We don't really care enough to move this PCB to the front of the list since we are not very likely to receive that many segments for connections in TIME-WAIT. */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); tcp_timewait_input(pcb); pbuf_free(p); return; } } /* Finally, if we still did not get a match, we check all PCBs that are LISTENing for incoming connections. */ prev = NULL; for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { if ((ip_addr_isany(&(lpcb->local_ip)) || ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && lpcb->local_port == tcphdr->dest) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ if (prev != NULL) { ((struct tcp_pcb_listen *)prev)->next = lpcb->next; /* our successor is the remainder of the listening list */ lpcb->next = tcp_listen_pcbs.listen_pcbs; /* put this listening pcb at the head of the listening list */ tcp_listen_pcbs.listen_pcbs = lpcb; } LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); tcp_listen_input(lpcb); pbuf_free(p); return; } prev = (struct tcp_pcb *)lpcb; } } #if TCP_INPUT_DEBUG LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); #endif /* TCP_INPUT_DEBUG */ if (pcb != NULL) { /* The incoming segment belongs to a connection. */ #if TCP_INPUT_DEBUG #if TCP_DEBUG tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ #endif /* TCP_INPUT_DEBUG */ /* Set up a tcp_seg structure. */ inseg.next = NULL; inseg.len = p->tot_len; inseg.dataptr = p->payload; inseg.p = p; inseg.tcphdr = tcphdr; recv_data = NULL; recv_flags = 0; /* If there is data which was previously "refused" by upper layer */ if (pcb->refused_data != NULL) { /* Notify again application with data previously received. */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); if (err == ERR_OK) { pcb->refused_data = NULL; } else { /* drop incoming packets, because pcb is "full" */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } } tcp_input_pcb = pcb; err = tcp_process(pcb); tcp_input_pcb = NULL; /* A return value of ERR_ABRT means that tcp_abort() was called and that the pcb has been freed. If so, we don't do anything. */ if (err != ERR_ABRT) { if (recv_flags & TF_RESET) { /* TF_RESET means that the connection was reset by the other end. We then call the error callback to inform the application that the connection is dead before we deallocate the PCB. */ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else if (recv_flags & TF_CLOSED) { /* The connection has been closed and we will deallocate the PCB. */ tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else { err = ERR_OK; /* If the application has registered a "sent" function to be called when new send buffer space is available, we call it now. */ if (pcb->acked > 0) { TCP_EVENT_SENT(pcb, pcb->acked, err); } if (recv_data != NULL) { if(flags & TCP_PSH) { recv_data->flags |= PBUF_FLAG_PUSH; } /* Notify application that data has been received. */ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); /* If the upper layer can't receive this data, store it */ if (err != ERR_OK) { pcb->refused_data = recv_data; LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); } } /* If a FIN segment was received, we call the callback function with a NULL buffer to indicate EOF. */ if (recv_flags & TF_GOT_FIN) { TCP_EVENT_RECV(pcb, NULL, ERR_OK, err); } /* If there were no errors, we try to send something out. */ if (err == ERR_OK) { tcp_output(pcb); } } } /* give up our reference to inseg.p */ if (inseg.p != NULL) { pbuf_free(inseg.p); inseg.p = NULL; } #if TCP_INPUT_DEBUG #if TCP_DEBUG tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ #endif /* TCP_INPUT_DEBUG */ } else { /* If no matching PCB was found, send a TCP RST (reset) to the sender. */ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), tcphdr->dest, tcphdr->src); } pbuf_free(p); } LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); PERF_STOP("tcp_input"); }
/** * 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; }
/** * Called by tcp_input() when a segment arrives for a listening * connection (from tcp_input()). * * @param pcb the tcp_pcb_listen for which a segment arrived * @return ERR_OK if the segment was processed * another err_t on error * * @note the return value is not (yet?) used in tcp_input() * @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_listen_input(struct tcp_pcb_listen *pcb) { struct tcp_pcb *npcb; u32_t optdata; /* In the LISTEN state, we check for incoming SYN segments, creates a new PCB, and responds with a SYN|ACK. */ if (flags & TCP_ACK) { /* For incoming segments with the ACK flag set, respond with a RST. */ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); tcp_rst(ackno + 1, seqno + tcplen, &(iphdr->dest), &(iphdr->src), tcphdr->dest, tcphdr->src); } else if (flags & TCP_SYN) { LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); #if TCP_LISTEN_BACKLOG if (pcb->accepts_pending >= pcb->backlog) { return ERR_ABRT; } #endif /* TCP_LISTEN_BACKLOG */ npcb = tcp_alloc(pcb->prio); /* If a new PCB could not be created (probably due to lack of memory), we don't do anything, but rely on the sender will retransmit the SYN at a time when we have more memory available. */ if (npcb == NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); TCP_STATS_INC(tcp.memerr); return ERR_MEM; } #if TCP_LISTEN_BACKLOG pcb->accepts_pending++; #endif /* TCP_LISTEN_BACKLOG */ /* Set up the new PCB. */ ip_addr_set(&(npcb->local_ip), &(iphdr->dest)); npcb->local_port = pcb->local_port; ip_addr_set(&(npcb->remote_ip), &(iphdr->src)); npcb->remote_port = tcphdr->src; npcb->state = SYN_RCVD; npcb->rcv_nxt = seqno + 1; npcb->snd_wnd = tcphdr->wnd; npcb->ssthresh = npcb->snd_wnd; npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ npcb->callback_arg = pcb->callback_arg; #if LWIP_CALLBACK_API npcb->accept = pcb->accept; #endif /* LWIP_CALLBACK_API */ /* inherit socket options */ npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER); /* Register the new PCB so that we can begin receiving segments for it. */ TCP_REG(&tcp_active_pcbs, npcb); /* Parse any options in the SYN. */ tcp_parseopt(npcb); #if TCP_CALCULATE_EFF_SEND_MSS npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip)); #endif /* TCP_CALCULATE_EFF_SEND_MSS */ snmp_inc_tcppassiveopens(); /* Build an MSS option. */ optdata = TCP_BUILD_MSS_OPTION(); /* Send a SYN|ACK together with the MSS option. */ tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (u8_t *)&optdata, 4); return tcp_output(npcb); } return ERR_OK; }
END_TEST START_TEST(test_sockets_recv_after_rst) { int sl, sact; int spass = -1; int ret; struct sockaddr_in sa_listen; const u16_t port = 1234; int arg; const char txbuf[] = "something"; char rxbuf[16]; struct lwip_sock *sact_sock; int err; LWIP_UNUSED_ARG(_i); fail_unless(test_sockets_get_used_count() == 0); memset(&sa_listen, 0, sizeof(sa_listen)); sa_listen.sin_family = AF_INET; sa_listen.sin_port = PP_HTONS(port); sa_listen.sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK); /* set up the listener */ sl = lwip_socket(AF_INET, SOCK_STREAM, 0); fail_unless(sl >= 0); fail_unless(test_sockets_get_used_count() == 0); ret = lwip_bind(sl, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); fail_unless(ret == 0); ret = lwip_listen(sl, 0); fail_unless(ret == 0); /* set up the client */ sact = lwip_socket(AF_INET, SOCK_STREAM, 0); fail_unless(sact >= 0); fail_unless(test_sockets_get_used_count() == 0); /* set the client to nonblocking to simplify this test */ arg = 1; ret = lwip_ioctl(sact, FIONBIO, &arg); fail_unless(ret == 0); /* connect */ do { ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); err = errno; fail_unless((ret == 0) || (ret == -1)); if (ret != 0) { if (err == EISCONN) { /* Although this is not valid, use EISCONN as an indicator for successful connection. This marks us as "connect phase is done". On error, we would either have a different errno code or "send" fails later... -> good enough for this test. */ ret = 0; } else { fail_unless(err == EINPROGRESS); if (err != EINPROGRESS) { goto cleanup; } /* we're in progress: little side check: test for EALREADY */ ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen)); err = errno; fail_unless(ret == -1); fail_unless(err == EALREADY); if ((ret != -1) || (err != EALREADY)) { goto cleanup; } } tcpip_thread_poll_one(); tcpip_thread_poll_one(); tcpip_thread_poll_one(); tcpip_thread_poll_one(); } } while (ret != 0); fail_unless(ret == 0); /* accept the server connection part */ spass = lwip_accept(sl, NULL, NULL); fail_unless(spass >= 0); /* write data from client */ ret = lwip_send(sact, txbuf, sizeof(txbuf), 0); fail_unless(ret == sizeof(txbuf)); tcpip_thread_poll_one(); tcpip_thread_poll_one(); /* issue RST (This is a HACK, don't try this in your own app!) */ sact_sock = lwip_socket_dbg_get_socket(sact); fail_unless(sact_sock != NULL); if (sact_sock != NULL) { struct netconn *sact_conn = sact_sock->conn; fail_unless(sact_conn != NULL); if (sact_conn != NULL) { struct tcp_pcb *pcb = sact_conn->pcb.tcp; fail_unless(pcb != NULL); if (pcb != NULL) { tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port); } } } tcpip_thread_poll_one(); tcpip_thread_poll_one(); /* expect to receive data first */ ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); fail_unless(ret > 0); tcpip_thread_poll_one(); tcpip_thread_poll_one(); /* expect to receive RST indication */ ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); fail_unless(ret == -1); err = errno; fail_unless(err == ECONNRESET); tcpip_thread_poll_one(); tcpip_thread_poll_one(); /* expect to receive ENOTCONN indication */ ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); fail_unless(ret == -1); err = errno; fail_unless(err == ENOTCONN); tcpip_thread_poll_one(); tcpip_thread_poll_one(); /* expect to receive ENOTCONN indication */ ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0); fail_unless(ret == -1); err = errno; fail_unless(err == ENOTCONN); tcpip_thread_poll_one(); tcpip_thread_poll_one(); cleanup: ret = lwip_close(sl); fail_unless(ret == 0); ret = lwip_close(sact); fail_unless(ret == 0); if (spass >= 0) { ret = lwip_close(spass); fail_unless(ret == 0); } }
int IpConnTrack_track_state(IpConnTrack *self, IpConn *conn, struct netpkt *pkt) { netpkt_tcp *tcp = pkt->pkt_tcp; IpConnStats *st=0; if( conn->conn_pkt_flags & CONN_PKT_FROM_CLIENT ) { st = &conn->conn_stats_client; if( conn->conn_flags & CONN_LOCAL_CLIENT ) { conn->conn_pkt_flags |= CONN_PKT_LOCAL_SRC; } if( conn->conn_flags & CONN_LOCAL_SERVER ) { conn->conn_pkt_flags |= CONN_PKT_LOCAL_DST; } } else if( conn->conn_pkt_flags & CONN_PKT_FROM_SERVER ) { st = &conn->conn_stats_server; if( conn->conn_flags & CONN_LOCAL_CLIENT ) { conn->conn_pkt_flags |= CONN_PKT_LOCAL_DST; } if( conn->conn_flags & CONN_LOCAL_SERVER ) { conn->conn_pkt_flags |= CONN_PKT_LOCAL_SRC; } } if( st ) { st->packets++; st->bytes += pkt->pkt_len; // track the next expected sequence numbers to mark duplicate // packets. I won't complain if retries are different. pkt->pkt_tcp_seq_diff = 0; if( tcp ) { u32 seq; seq = ntohl(tcp->seq); if( !st->tcp_seq_next_ok || seq == st->tcp_seq_next ) { if( tcp_syn(tcp) ) { st->tcp_seq_next = seq + 1; } else { st->tcp_seq_next = seq + pkt->pkt_len; } st->tcp_seq_next_ok = 1; } else { pkt->pkt_tcp_seq_diff = tcp_seq_diff(seq, st->tcp_seq_next); } } } conn->conn_time_prev = conn->conn_time_last; conn->conn_time_last = mstime(); if( tcp ) { if( tcp_fin(tcp) || tcp_rst(tcp) ) { conn->conn_state = CONN_STATE_FIN; } } return 0; }