⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tcp_input.c

📁 基于uCOS操作系统的功能强大的源代码,包括shell,lwip支持AT91处理器
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 * 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 + -