Enscript Output

extractedLnx/linux/net/inet/tcp.c_tcp_rcv.c

int
tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
	unsigned long daddr, unsigned short len,
	unsigned long saddr, int redo, struct inet_protocol * protocol)
{
	struct tcphdr *th;
	struct sock *sk;

	if (!skb) 
	{
		return(0);
	}

	if (!dev) 
	{
		return(0);
	}
  
	tcp_statistics.TcpInSegs++;
  
	if(skb->pkt_type!=PACKET_HOST)
	{
	  	kfree_skb(skb,FREE_READ);
	  	return(0);
	}
  
	th = skb->h.th;

	/*
	 *	Find the socket.
	 */

	sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);

	/*
	 *	If this socket has got a reset its to all intents and purposes 
  	 *	really dead 
  	 */
  	 
	if (sk!=NULL && sk->zapped)
		sk=NULL;

	if (!redo) 
	{
		if (tcp_check(th, len, saddr, daddr )) 
		{
			skb->sk = NULL;
			kfree_skb(skb,FREE_READ);
			/*
			 * We don't release the socket because it was
			 * never marked in use.
			 */
			return(0);
		}
		th->seq = ntohl(th->seq);

		/* See if we know about the socket. */
		if (sk == NULL) 
		{
			if (!th->rst)
				tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255);
			skb->sk = NULL;
			kfree_skb(skb, FREE_READ);
			return(0);
		}

		skb->len = len;
		skb->acked = 0;
		skb->used = 0;
		skb->free = 0;
		skb->saddr = daddr;
		skb->daddr = saddr;
	
		/* We may need to add it to the backlog here. */
		cli();
		if (sk->inuse) 
		{
			skb_queue_tail(&sk->back_log, skb);
			sti();
			return(0);
		}
		sk->inuse = 1;
		sti();
	}
	else
	{
		if (!sk) 
		{
			return(0);
		}
	}


	if (!sk->prot) 
	{
		return(0);
	}


	/*
	 *	Charge the memory to the socket. 
	 */
	 
	if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) 
	{
		kfree_skb(skb, FREE_READ);
		release_sock(sk);
		return(0);
	}

	skb->sk=sk;
	sk->rmem_alloc += skb->mem_len;

#ifdef TCP_FASTPATH
/*
 *	Incoming data stream fastpath. 
 *
 *	We try to optimise two things.
 *	1) Spot general data arriving without funny options and skip extra checks and the switch.
 *	2) Spot the common case in raw data receive streams of a packet that has no funny options,
 *	fits exactly on the end of the current queue and may or may not have the ack bit set.
 *
 *	Case two especially is done inline in this routine so there are no long jumps causing heavy
 *	cache thrashing, no function call overhead (except for the ack sending if needed) and for
 *	speed although further optimizing here is possible.
 */
 
	/* I'm trusting gcc to optimise this sensibly... might need judicious application of a software mallet */
	if(!(sk->shutdown & RCV_SHUTDOWN) && sk->state==TCP_ESTABLISHED && !th->urg && !th->syn && !th->fin && !th->rst)
	{	
		/* Packets in order. Fits window */
		if(th->seq == sk->acked_seq+1 && sk->window && tcp_clean_end(sk))
		{
			/* Ack is harder */
			if(th->ack && !tcp_ack(sk, th, saddr, len))
			{
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return 0;
			}
			/*
			 *	Set up variables
			 */
			skb->len -= (th->doff *4);
			sk->bytes_rcv += skb->len;
			tcp_rx_hit2++;
			if(skb->len)
			{
				skb_queue_tail(&sk->receive_queue,skb);	/* We already know where to put it */
				if(sk->window >= skb->len)
					sk->window-=skb->len;			/* We know its effect on the window */
				else
					sk->window=0;
				sk->acked_seq = th->seq+skb->len;	/* Easy */
				skb->acked=1;				/* Guaranteed true */
				if(!sk->delay_acks || sk->ack_backlog >= sk->max_ack_backlog || 
					sk->bytes_rcv > sk->max_unacked)
				{
					tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th , saddr);
				}
				else
				{
					sk->ack_backlog++;
					reset_timer(sk, TIME_WRITE, TCP_ACK_TIME);
				}
				if(!sk->dead)
					sk->data_ready(sk,0);
				release_sock(sk);
				return 0;
			}
		}
		/*
		 *	More generic case of arriving data stream in ESTABLISHED
		 */
		tcp_rx_hit1++;
		if(!tcp_sequence(sk, th, len, opt, saddr, dev))
		{
			kfree_skb(skb, FREE_READ);
			release_sock(sk);
			return 0;
		}
		if(th->ack && !tcp_ack(sk, th, saddr, len))
		{
			kfree_skb(skb, FREE_READ);
			release_sock(sk);
			return 0;
		}
		if(tcp_data(skb, sk, saddr, len))
			kfree_skb(skb, FREE_READ);
		release_sock(sk);
		return 0;
	}
	tcp_rx_miss++;
#endif	

	/*
	 *	Now deal with all cases.
	 */
	 
	switch(sk->state) 
	{
	
		/*
		 * This should close the system down if it's waiting
		 * for an ack that is never going to be sent.
		 */
		case TCP_LAST_ACK:
			if (th->rst) 
			{
				sk->zapped=1;
				sk->err = ECONNRESET;
 				tcp_set_state(sk,TCP_CLOSE);
				sk->shutdown = SHUTDOWN_MASK;
				if (!sk->dead) 
				{
					sk->state_change(sk);
				}
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}

		case TCP_ESTABLISHED:
		case TCP_CLOSE_WAIT:
		case TCP_CLOSING:
		case TCP_FIN_WAIT1:
		case TCP_FIN_WAIT2:
		case TCP_TIME_WAIT:

			/*
			 * is it a good packet?
			 */

			if (!tcp_sequence(sk, th, len, opt, saddr,dev)) 
			{
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}

			if (th->rst) 
			{
				if(sk->state!=TCP_TIME_WAIT)	/* RFC 1337 recommendation re RST in time wait */
				{
					tcp_statistics.TcpEstabResets++;
					sk->zapped=1;
					/* This means the thing should really be closed. */
					sk->err = ECONNRESET;
					if (sk->state == TCP_CLOSE_WAIT) 
					{
						sk->err = EPIPE;
					}
					tcp_set_state(sk,TCP_CLOSE);
					sk->shutdown = SHUTDOWN_MASK;
					if (!sk->dead) 
					{
						sk->state_change(sk);
					}
				}
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
			if (th->syn) 
			{
				long seq=sk->write_seq;
				int st=sk->state;
				tcp_statistics.TcpEstabResets++;
				sk->err = ECONNRESET;
				tcp_set_state(sk,TCP_CLOSE);
				sk->shutdown = SHUTDOWN_MASK;
				if(sk->debug)
					printk("Socket %p reset by SYN while established.\n", sk);
				if (!sk->dead) {
					sk->state_change(sk);
				}
				/*
				 *	The BSD port reuse protocol violation.
				 *	I do sometimes wonder how the *bsd people
				 *	have the nerve to talk about 'standards'.
				 *
				 *	If seq > last used on connection then
				 *	open a new connection and use 128000+seq of
				 *	old connection.
				 *
				 */
				 
				if(st==TCP_TIME_WAIT && th->seq > sk->acked_seq && sk->dead)
				{
					struct sock *psk=sk;
					/*
					 *	Find the listening socket.
					 */
					sk=get_sock(&tcp_prot, th->source, daddr, th->dest, saddr);
					if(sk && sk->state==TCP_LISTEN)
					{
						sk->inuse=1;
						tcp_conn_request(sk, skb, daddr, saddr,opt, dev,seq+128000);
						release_sock(psk);
						/* Fall through in case people are
						   also using the piggy backed SYN + data 
						   protocol violation */
					}
					else
					{
						tcp_reset(daddr, saddr,  th, psk->prot, opt,dev, psk->ip_tos,psk->ip_ttl);
						release_sock(psk);
						kfree_skb(skb, FREE_READ);
						return 0;
					}			
				}
				else
				{
					tcp_reset(daddr, saddr,  th, sk->prot, opt,dev, sk->ip_tos,sk->ip_ttl);
					kfree_skb(skb, FREE_READ);
					release_sock(sk);
					return(0);
				}
			}	
			if (th->ack && !tcp_ack(sk, th, saddr, len)) {
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
	
			if (tcp_urg(sk, th, saddr, len)) {
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}

	
			if (tcp_data(skb, sk, saddr, len)) {
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}	

			if (th->fin && tcp_fin(skb, sk, th, saddr, dev)) {
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
	
			release_sock(sk);
			return(0);


		case TCP_CLOSE:
			if (sk->dead || sk->daddr) {
				kfree_skb(skb, FREE_READ);
					release_sock(sk);
				return(0);
			}
	
			if (!th->rst) {
				if (!th->ack)
					th->ack_seq = 0;
				if(sk->debug) printk("Reset on closed socket %d.\n",sk->blog);
				tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
			}
			kfree_skb(skb, FREE_READ);
			release_sock(sk);
				return(0);
	
		case TCP_LISTEN:
			if (th->rst) {
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
			if (th->ack) {
				if(sk->debug) printk("Reset on listening socket %d.\n",sk->blog);
				tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
	
			if (th->syn) 
			{
				/*
				 * Now we just put the whole thing including
				 * the header and saddr, and protocol pointer
				 * into the buffer.  We can't respond until the
				 * user tells us to accept the connection.
				 */
				tcp_conn_request(sk, skb, daddr, saddr, opt, dev, tcp_init_seq());
				release_sock(sk);
				return(0);
			}

			kfree_skb(skb, FREE_READ);
			release_sock(sk);
			return(0);

		case TCP_SYN_RECV:
			if (th->syn) {
				/* Probably a retransmitted syn */
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
	
	
		default:
			if (!tcp_sequence(sk, th, len, opt, saddr,dev)) 
			{
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
	
		case TCP_SYN_SENT:
			if (th->rst) 
			{
				tcp_statistics.TcpAttemptFails++;
				sk->err = ECONNREFUSED;
				tcp_set_state(sk,TCP_CLOSE);
				sk->shutdown = SHUTDOWN_MASK;
				sk->zapped = 1;
				if (!sk->dead) 
				{
					sk->state_change(sk);
				}
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
			if (!th->ack) 
			{
				if (th->syn) 
				{
					/* Crossed SYN's are fine - but talking to
					   yourself is right out... */
					if(sk->saddr==saddr && sk->daddr==daddr &&
						sk->dummy_th.source==th->source &&
						sk->dummy_th.dest==th->dest)
					{
						tcp_statistics.TcpAttemptFails++;
						sk->err = ECONNREFUSED;
						tcp_set_state(sk,TCP_CLOSE);
						sk->shutdown = SHUTDOWN_MASK;
						sk->zapped = 1;
						if (!sk->dead) 
						{
							sk->state_change(sk);
						}
						kfree_skb(skb, FREE_READ);
						release_sock(sk);
						return(0);
					}
					tcp_set_state(sk,TCP_SYN_RECV);
				}
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
	
			switch(sk->state) 
			{
				case TCP_SYN_SENT:
					if (!tcp_ack(sk, th, saddr, len)) 
					{
						tcp_statistics.TcpAttemptFails++;
						tcp_reset(daddr, saddr, th,
							sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
						kfree_skb(skb, FREE_READ);
							release_sock(sk);
						return(0);
					}
	
					/*
					 * If the syn bit is also set, switch to
					 * tcp_syn_recv, and then to established.
					 */
					if (!th->syn) 
					{
						kfree_skb(skb, FREE_READ);
						release_sock(sk);
						return(0);
					}
	
					/* Ack the syn and fall through. */
					sk->acked_seq = th->seq+1;
					sk->fin_seq = th->seq;
					tcp_send_ack(sk->sent_seq, th->seq+1,
						sk, th, sk->daddr);
		
				case TCP_SYN_RECV:
					if (!tcp_ack(sk, th, saddr, len)) 
					{
						tcp_statistics.TcpAttemptFails++;
						tcp_reset(daddr, saddr, th,
							sk->prot, opt, dev,sk->ip_tos,sk->ip_ttl);
						kfree_skb(skb, FREE_READ);
						release_sock(sk);
						return(0);
					}
	
					tcp_set_state(sk,TCP_ESTABLISHED);
	
					/*
					 * 	Now we need to finish filling out
					 * 	some of the tcp header.
					 * 
					 *	We need to check for mtu info. 
					 */
					tcp_options(sk, th);
					sk->dummy_th.dest = th->source;
					sk->copied_seq = sk->acked_seq-1;
					if (!sk->dead) 
					{
						sk->state_change(sk);
					}
	
					/*
					 * We've already processed his first
					 * ack.  In just about all cases that
					 * will have set max_window.  This is
					 * to protect us against the possibility
					 * that the initial window he sent was 0.
					 * This must occur after tcp_options, which
					 * sets sk->mtu.
					 */
					if (sk->max_window == 0) 
					{
						sk->max_window = 32;
						sk->mss = min(sk->max_window, sk->mtu);
					}

					/*
					 * Now process the rest like we were
					 * already in the established state.
					 */
					if (th->urg) 
					{
						if (tcp_urg(sk, th, saddr, len)) 
						{ 
							kfree_skb(skb, FREE_READ);
							release_sock(sk);
							return(0);
						}
					}
					if (tcp_data(skb, sk, saddr, len))
						kfree_skb(skb, FREE_READ);

					if (th->fin)
						tcp_fin(skb, sk, th, saddr, dev);
					release_sock(sk);
					return(0);
			}
	
			if (th->urg) 
			{
				if (tcp_urg(sk, th, saddr, len)) 
				{
					kfree_skb(skb, FREE_READ);
					release_sock(sk);
					return(0);
				}
			}
			if (tcp_data(skb, sk, saddr, len)) 
			{
				kfree_skb(skb, FREE_READ);
				release_sock(sk);
				return(0);
			}
	
			if (!th->fin) 
			{
				release_sock(sk);
				return(0);
			}
			tcp_fin(skb, sk, th, saddr, dev);
			release_sock(sk);
			return(0);
	}
}

Generated by GNU enscript 1.6.4.