📄 tcp_input.c
字号:
/* * Copyright (c) 2001, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This file is part of the lwIP TCP/IP stack. * * Author: Adam Dunkels <adam@sics.se> * * $Id: tcp_input.c,v 1.1 2003/05/21 10:37:40 chenyu Exp $ *//*-----------------------------------------------------------------------------------*//* tcp_input.c * * The input processing functions of TCP. * * These functions are generally called in the order (ip_input() ->) tcp_input() -> * tcp_process() -> tcp_receive() (-> application). * *//*-----------------------------------------------------------------------------------*/#include "lwip/debug.h"#include "lwip/def.h"#include "lwip/opt.h"#include "lwip/netif.h"#include "lwip/mem.h"#include "lwip/memp.h"#include "lwip/inet.h"#include "lwip/tcp.h"#include "lwip/stats.h"#include "arch/perf.h"static struct tcp_seg inseg;/* Forward declarations. */static err_t tcp_process(struct tcp_pcb *pcb);static void tcp_receive(struct tcp_pcb *pcb);static void tcp_parseopt(struct tcp_pcb *pcb);/*-----------------------------------------------------------------------------------*//* tcp_input: * * 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()). *//*-----------------------------------------------------------------------------------*/voidtcp_input(struct pbuf *p, struct netif *inp){ struct tcp_hdr *tcphdr; struct tcp_pcb *pcb, *prev; struct ip_hdr *iphdr; u8_t offset; err_t err; PERF_START; #ifdef TCP_STATS ++stats.tcp.recv;#endif /* TCP_STATS */ iphdr = p->payload; tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4/sizeof(u8_t)); pbuf_header(p, -(IPH_HL(iphdr) * 4/sizeof(u8_t))); /* Don't even process incoming broadcasts/multicasts. */ if(ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask)) || ip_addr_ismulticast(&(iphdr->dest))) { pbuf_free(p); return; } /* 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) { DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04x\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 */#ifdef TCP_STATS ++stats.tcp.chkerr; ++stats.tcp.drop;#endif /* TCP_STATS */ pbuf_free(p); return; } /* Move the payload pointer in the pbuf so that it points to the TCP data instead of the TCP header. */ offset = TCPH_OFFSET(tcphdr) >> 4; pbuf_header(p, -(offset * 4)); /* Convert fields in TCP header to host byte order. */ tcphdr->src = ntohs(tcphdr->src); tcphdr->dest = ntohs(tcphdr->dest); tcphdr->seqno = ntohl(tcphdr->seqno); tcphdr->ackno = ntohl(tcphdr->ackno); tcphdr->wnd = ntohs(tcphdr->wnd); /* 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) { ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); 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). */ 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; } ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); break; } prev = pcb; } /* If it did not go to an active connection, we check the connections in the TIME-WAIT state. */ if(pcb == NULL) { for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { 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. */ break; } } /* Finally, if we still did not get a match, we check all PCBs that are LISTENing for incomming connections. */ prev = NULL; if(pcb == NULL) { for(pcb = (struct tcp_pcb *)tcp_listen_pcbs; pcb != NULL; pcb = pcb->next) { ASSERT("tcp_input: LISTEN pcb->state == LISTEN", pcb->state == LISTEN); if((ip_addr_isany(&(pcb->local_ip)) || ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) && pcb->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) { prev->next = pcb->next; pcb->next = (struct tcp_pcb *)tcp_listen_pcbs; tcp_listen_pcbs = (struct tcp_pcb_listen *)pcb; } break; } prev = pcb; } } } #if TCP_INPUT_DEBUG DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));#endif /* TCP_INPUT_DEBUG */ /* seg = memp_malloc2(MEMP_TCP_SEG); if(seg != NULL && pcb != NULL) {*/ if(pcb != NULL) { #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; /* The len field in the tcp_seg structure is the segment length in TCP terms. In TCP, the SYN and FIN segments are treated as one byte, hence increment the len field. */ /* if(TCPH_FLAGS(tcphdr) & TCP_FIN || TCPH_FLAGS(tcphdr) & TCP_SYN) { ++inseg.len; } */ if(pcb->state != LISTEN && pcb->state != TIME_WAIT) { pcb->recv_data = NULL; } err = tcp_process(pcb); /* A return value of ERR_ABRT means that tcp_abort() was called and that the pcb has been freed. */ if(err != ERR_ABRT) { if(pcb->state != LISTEN) { if(pcb->flags & TF_RESET) { if(pcb->state != LISTEN) { if(pcb->errf != NULL) { pcb->errf(pcb->callback_arg, ERR_RST); } } if(pcb->state == TIME_WAIT) { tcp_pcb_remove(&tcp_tw_pcbs, pcb); } else { tcp_pcb_remove(&tcp_active_pcbs, pcb); } memp_free(MEMP_TCP_PCB, pcb); } else if(pcb->flags & TF_CLOSED) { tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else { if(pcb->state < TIME_WAIT) { err = ERR_OK; /* If the application has registered a "sent" function to be called when new send buffer space is avaliable, we call it now. */ if(pcb->acked > 0 && pcb->sent != NULL) { err = pcb->sent(pcb->callback_arg, pcb, pcb->acked); } if(pcb->recv != NULL) { if(pcb->recv_data != NULL) { err = pcb->recv(pcb->callback_arg, pcb, pcb->recv_data, ERR_OK); } if(pcb->flags & TF_GOT_FIN) { err = pcb->recv(pcb->callback_arg, pcb, NULL, ERR_OK); } } else { err = ERR_OK; pbuf_free(pcb->recv_data); if(pcb->flags & TF_GOT_FIN) { tcp_close(pcb); } } if(err == ERR_OK) { tcp_output(pcb); } } else if(pcb->state == TIME_WAIT) { pbuf_free(pcb->recv_data); tcp_output(pcb); } } } } pbuf_free(inseg.p);#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. */ DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); if(!(TCPH_FLAGS(tcphdr) & TCP_RST)) {#ifdef TCP_STATS ++stats.tcp.proterr; ++stats.tcp.drop;#endif /* TCP_STATS */ tcp_rst(tcphdr->ackno, tcphdr->seqno + p->tot_len + ((TCPH_FLAGS(tcphdr) & TCP_FIN || TCPH_FLAGS(tcphdr) & TCP_SYN)? 1: 0), &(iphdr->dest), &(iphdr->src), tcphdr->dest, tcphdr->src); } pbuf_free(p); } ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); PERF_STOP("tcp_input");}/*-----------------------------------------------------------------------------------*//* tcp_process * * 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. *//*-----------------------------------------------------------------------------------*/static err_ttcp_process(struct tcp_pcb *pcb){ struct tcp_pcb *npcb; struct ip_hdr *iphdr; struct tcp_hdr *tcphdr; u32_t seqno, ackno; u8_t flags; u32_t optdata; struct tcp_seg *rseg; u8_t acceptable = 0; iphdr = (struct ip_hdr *)((u8_t *)inseg.tcphdr - IP_HLEN/sizeof(u8_t)); tcphdr = inseg.tcphdr; flags = TCPH_FLAGS(tcphdr); seqno = tcphdr->seqno; ackno = tcphdr->ackno; /* Process incoming RST segments. */ if(flags & TCP_RST) { /* First, determine if the reset is acceptable. */ if(pcb->state != LISTEN) { if(pcb->state == SYN_SENT) { if(ackno == pcb->snd_nxt) { acceptable = 1; } } else { if(TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) && TCP_SEQ_LEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) { acceptable = 1; } } } if(acceptable) { DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); pcb->flags |= TF_RESET; pcb->flags &= ~TF_ACK_DELAY; } else { DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %lu rcv_nxt %lu\n", seqno, pcb->rcv_nxt)); DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %lu rcv_nxt %lu\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -