📄 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()).
*/
/*-----------------------------------------------------------------------------------*/
void
tcp_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_t
tcp_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 + -