Descrizione dell'inizializzazione e della chiusura di una connessione TCP
-------------------------------------------------------------------------
Autore : Angelo Dell'Aera  <buffer@users.sourceforge.net>
---------------------------------------------------------

Cominciamo ad analizzare il caso in cui sia il nostro host a stabilire la 
connessione. Per questo scopo viene utilizzata la funzione tcp_connect()
che riporto e commento qui sotto. Prima di vedere questa per analizziamo
la tcp_connect_init() che  molto importante per lo scopo che stiamo
analizzando. Ma poich oggi mi piace cambiare opinione ogni 30 millisecondi
partiamo dalla tcp_v4_connect() perch da qui ha origine tutto!


/* This will initiate an outgoing connection. */

	int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
	{
		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
		struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
		struct rtable *rt;
		u32 daddr, nexthop;
		int tmp;
		int err;
	
		if (addr_len < sizeof(struct sockaddr_in))
			return(-EINVAL);
	
		if (usin->sin_family != AF_INET)
			return(-EAFNOSUPPORT);
	
		nexthop = daddr = usin->sin_addr.s_addr;
		if (sk->protinfo.af_inet.opt && sk->protinfo.af_inet.opt->srr) {
			if (daddr == 0)
				return -EINVAL;
			nexthop = sk->protinfo.af_inet.opt->faddr;
		}
	
		tmp = ip_route_connect(&rt, nexthop, sk->saddr,
				       RT_CONN_FLAGS(sk), sk->bound_dev_if);
		if (tmp < 0)
			return tmp;
	
		if (rt->rt_flags&(RTCF_MULTICAST|RTCF_BROADCAST)) {
			ip_rt_put(rt);
			return -ENETUNREACH;
		}
	
		__sk_dst_set(sk, &rt->u.dst);
		sk->route_caps = rt->u.dst.dev->features;
	
		if (!sk->protinfo.af_inet.opt || !sk->protinfo.af_inet.opt->srr)
			daddr = rt->rt_dst;
	
		if (!sk->saddr)
			sk->saddr = rt->rt_src;
		sk->rcv_saddr = sk->saddr;
	
		if (tp->ts_recent_stamp && sk->daddr != daddr) {
			/* Reset inherited state */
			tp->ts_recent = 0;
			tp->ts_recent_stamp = 0;
			tp->write_seq = 0;
		}
	
		if (sysctl_tcp_tw_recycle &&
		    !tp->ts_recent_stamp &&
		    rt->rt_dst == daddr) {
			struct inet_peer *peer = rt_get_peer(rt);
	
			/* VJ's idea. We save last timestamp seen from
			 * the destination in peer table, when entering state TIME-WAIT
			 * and initialize ts_recent from it, when trying new connection.
			 */
	
			if (peer && peer->tcp_ts_stamp + TCP_PAWS_MSL >= xtime.tv_sec) {
				tp->ts_recent_stamp = peer->tcp_ts_stamp;
				tp->ts_recent = peer->tcp_ts;
			}
		}
	
		sk->dport = usin->sin_port;
		sk->daddr = daddr;
	
		tp->ext_header_len = 0;
		if (sk->protinfo.af_inet.opt)
			tp->ext_header_len = sk->protinfo.af_inet.opt->optlen;
	
		tp->mss_clamp = 536;
	
		/* Socket identity is still unknown (sport may be zero).
		 * However we set state to SYN-SENT and not releasing socket
		 * lock select source port, enter ourselves into the hash tables and
		 * complete initalization after this.
		 */
		tcp_set_state(sk, TCP_SYN_SENT);
		err = tcp_v4_hash_connect(sk);
		if (err)
			goto failure;
	
		if (!tp->write_seq)
			tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
								   sk->sport, usin->sin_port);
	
		sk->protinfo.af_inet.id = tp->write_seq^jiffies;
	
		err = tcp_connect(sk);
		if (err)
			goto failure;
	
		return 0;
	
	failure:
		tcp_set_state(sk, TCP_CLOSE);
		__sk_dst_reset(sk);
		sk->route_caps = 0;
		sk->dport = 0;
		return err;
	}
	


/* 
 * Do all connect socket setups that can be done AF independent.
 */ 
static inline void tcp_connect_init(struct sock *sk)
{
	struct dst_entry *dst = __sk_dst_get(sk);
	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);

	/* We'll fix this up when we get a response from the other end.
	 * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
	 */

	/* Qui viene fissata la dimensione dell'header TCP. In realt qui questa viene fissata considerando 
	 * la dimensione dell'header TCP canonico (definito in /usr/src/linux/include/linux/tcp.h) e quindi
	 * 20 bytes a cui viene aggiunta, se sono abilitati i timestamp attraverso la sysctl opportuna, un
	 * numero di bytes sufficiente a piazzare la timestamp option.
	 */

	tp->tcp_header_len = sizeof(struct tcphdr) +
		(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);

	/* If user gave his TCP_MAXSEG, record it to clamp */

	/* Spiegazione. L'utente ha la possibilit tramite ioctl di settare un limite per MSS. In tal caso
	 * mss_clamp viene settato a tale valore. 
	 * Inoltre poich stiamo aprendo la connessione adesso la massima finestra (max_window) vista non 
	 * pu essere settata che a 0. Anche perch non l'abbiamo vista! 8)
	 * Prima di andare avanti attacco un commento di Alan che chiarisce il ruolo di alcune variabili.
	 * Leggilo perch questo commento  relativo a tcp_sync_mss() che trovi subito dopo.
         *
	 * This function synchronize snd mss to current pmtu/exthdr set.	 
	 *
	 * tp->user_mss is mss set by user by TCP_MAXSEG. It does NOT counts
	 * for TCP options, but includes only bare TCP header.
	 *
	 * tp->mss_clamp is mss negotiated at connection setup.
	 * It is minumum of user_mss and mss received with SYN.
	 * It also does not include TCP options.
	 *
	 * tp->pmtu_cookie is last pmtu, seen by this function.
	 *
	 * tp->mss_cache is current effective sending mss, including
	 * all tcp options except for SACKs. It is evaluated,
	 * taking into account current pmtu, but never exceeds
	 * tp->mss_clamp.
	 *
	 * NOTE1. rfc1122 clearly states that advertised MSS
	 * DOES NOT include either tcp or ip options.
	 *
	 * NOTE2. tp->pmtu_cookie and tp->mss_cache are READ ONLY outside
	 * this function.			--ANK (980731)
	 */

	if (tp->user_mss)
		tp->mss_clamp = tp->user_mss;
	tp->max_window = 0;
	tcp_sync_mss(sk, dst->pmtu);

	/* Settiamo un limite sulla massima finestra da notificare al peer (se non  stato gi fatto) e 
	 * settiamo l'advertised MSS.
	 */

	if (!tp->window_clamp)
		tp->window_clamp = dst->window;
	tp->advmss = dst->advmss;

	/* Questa  bella davvero! In pratica si inizializza la variabile RCV_MSS che sarebbe nient'altro
	 * che MSS usato dal peer. Ora il pi ganzo della brigata gi lo vedo alzare la mano e chiedere
	 * "Mi scusi ma se sto spedendo il SYN come faccio a sapere che diavolo succede dall'altra parte?"
	 * 30 e lode figliuolo! 8) Infatti qui per quanto strano possa sembrare si gioca a indovinare!
	 * Con un accorgimento. Nell'indecisione tra sovrastimare e sottostimare si sottostima.
	 * La correzione di questo valore la si fa poi in corsa con la tcp_measure_rcv_mss().
	 */

	tcp_initialize_rcv_mss(sk);

	/* Determiniamo ora la finestra da offrire al peer. Inoltre viene determinato eventualmente il
	 * window scaling da offrire. Il resto della funzione  semplice da capire e la lascio come
	 * compito per il volenteroso di turno.
	 */

	tcp_select_initial_window(tcp_full_space(sk),
				  tp->advmss - (tp->ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0),
				  &tp->rcv_wnd,
				  &tp->window_clamp,
				  sysctl_tcp_window_scaling,
				  &tp->rcv_wscale);

	tp->rcv_ssthresh = tp->rcv_wnd;

	sk->err = 0;
	sk->done = 0;
	tp->snd_wnd = 0;
	tcp_init_wl(tp, tp->write_seq, 0);
	tp->snd_una = tp->write_seq;
	tp->snd_sml = tp->write_seq;
	tp->rcv_nxt = 0;
	tp->rcv_wup = 0;
	tp->copied_seq = 0;

	tp->rto = TCP_TIMEOUT_INIT;
	tp->retransmits = 0;
	tcp_clear_retrans(tp);
}


/*
 * Build a SYN and send it off.
 */ 
int tcp_connect(struct sock *sk)
{
	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
	struct sk_buff *buff;

	tcp_connect_init(sk);

	buff = alloc_skb(MAX_TCP_HEADER + 15, sk->allocation);
	if (unlikely(buff == NULL))
		return -ENOBUFS;

	/* Reserve space for headers. */
	skb_reserve(buff, MAX_TCP_HEADER);

	TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN;
	TCP_ECN_send_syn(tp, buff);
	TCP_SKB_CB(buff)->sacked = 0;
	buff->csum = 0;
	TCP_SKB_CB(buff)->seq = tp->write_seq++;
	TCP_SKB_CB(buff)->end_seq = tp->write_seq;
	tp->snd_nxt = tp->write_seq;
	tp->pushed_seq = tp->write_seq;

	/* Send it off. */
	TCP_SKB_CB(buff)->when = tcp_time_stamp;
	tp->retrans_stamp = TCP_SKB_CB(buff)->when;
	__skb_queue_tail(&sk->write_queue, buff);
	tcp_charge_skb(sk, buff);
	tp->packets_out++;
	tcp_transmit_skb(sk, skb_clone(buff, GFP_KERNEL));
	TCP_INC_STATS(TcpActiveOpens);

	/* Timer for repeating the SYN until an answer. */
	tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
	return 0;
}


=======
Cominciamo ad analizzare il caso in cui sia il nostro host a stabilire la 
connessione. Per questo scopo viene utilizzata la funzione tcp_connect()
che riporto e commento qui sotto. Prima di vedere questa per analizziamo
la tcp_connect_init() che  molto importante per lo scopo che stiamo
analizzando. Ma poich oggi mi piace cambiare opinione ogni 30 millisecondi
partiamo dalla tcp_v4_connect() perch da qui ha origine tutto. 

Nota importante.
Non mi soffermer sui particolari di come la tcp_v4_connect() venga chiamata 
ma soltanto per rendere l'idea consiglio di vedere l'implementazione della
sys_connect() in net/socket.c. Ora nella sys_connect() a un certo punto dopo
alcune operazioni come ad esempio la sockfd_lookup che serve a ripescare il
sock su cui compiere le operazioni (vedere l'interfaccia del VFS per questo...
se comincio a parlare anche di questo  finita...)
>>>>>>> 1.3


/* This will initiate an outgoing connection. */

	int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
	{
		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
		struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
		struct rtable *rt;
		u32 daddr, nexthop;
		int tmp;
		int err;

		/* Alcuni controlli sono necessari. Chi ha scritto almeno una volta nella vita
		 * un applicativo client/server basato su TCP non ha bisogno di chiarimenti a 
		 * proposito dei due successivi controlli. Se stai pensando "io non l'ho mai
		 * fatto" non ti sembra si stare saltando qualche tappa di troppo? 8)
		 */
	
		if (addr_len < sizeof(struct sockaddr_in))
			return(-EINVAL);
	
		if (usin->sin_family != AF_INET)
			return(-EAFNOSUPPORT);
	
		nexthop = daddr = usin->sin_addr.s_addr;
		if (sk->protinfo.af_inet.opt && sk->protinfo.af_inet.opt->srr) {
			if (daddr == 0)
				return -EINVAL;
			nexthop = sk->protinfo.af_inet.opt->faddr;
		}
	
		tmp = ip_route_connect(&rt, nexthop, sk->saddr,
				       RT_CONN_FLAGS(sk), sk->bound_dev_if);
		if (tmp < 0)
			return tmp;
	
		if (rt->rt_flags&(RTCF_MULTICAST|RTCF_BROADCAST)) {
			ip_rt_put(rt);
			return -ENETUNREACH;
		}
	
		__sk_dst_set(sk, &rt->u.dst);
		sk->route_caps = rt->u.dst.dev->features;
	
		if (!sk->protinfo.af_inet.opt || !sk->protinfo.af_inet.opt->srr)
			daddr = rt->rt_dst;
	
		if (!sk->saddr)
			sk->saddr = rt->rt_src;
		sk->rcv_saddr = sk->saddr;
	
		if (tp->ts_recent_stamp && sk->daddr != daddr) {
			/* Reset inherited state */
			tp->ts_recent = 0;
			tp->ts_recent_stamp = 0;
			tp->write_seq = 0;
		}
	
		if (sysctl_tcp_tw_recycle &&
		    !tp->ts_recent_stamp &&
		    rt->rt_dst == daddr) {
			struct inet_peer *peer = rt_get_peer(rt);
	
			/* VJ's idea. We save last timestamp seen from
			 * the destination in peer table, when entering state TIME-WAIT
			 * and initialize ts_recent from it, when trying new connection.
			 */
	
			if (peer && peer->tcp_ts_stamp + TCP_PAWS_MSL >= xtime.tv_sec) {
				tp->ts_recent_stamp = peer->tcp_ts_stamp;
				tp->ts_recent = peer->tcp_ts;
			}
		}
	
		sk->dport = usin->sin_port;
		sk->daddr = daddr;
	
		tp->ext_header_len = 0;
		if (sk->protinfo.af_inet.opt)
			tp->ext_header_len = sk->protinfo.af_inet.opt->optlen;
	
		tp->mss_clamp = 536;
	
		/* Socket identity is still unknown (sport may be zero).
		 * However we set state to SYN-SENT and not releasing socket
		 * lock select source port, enter ourselves into the hash tables and
		 * complete initalization after this.
		 */
		tcp_set_state(sk, TCP_SYN_SENT);
		err = tcp_v4_hash_connect(sk);
		if (err)
			goto failure;
	
		if (!tp->write_seq)
			tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
								   sk->sport, usin->sin_port);
	
		sk->protinfo.af_inet.id = tp->write_seq^jiffies;
	
		err = tcp_connect(sk);
		if (err)
			goto failure;
	
		return 0;
	
	failure:
		tcp_set_state(sk, TCP_CLOSE);
		__sk_dst_reset(sk);
		sk->route_caps = 0;
		sk->dport = 0;
		return err;
	}
	


/* 
 * Do all connect socket setups that can be done AF independent.
 */ 
static inline void tcp_connect_init(struct sock *sk)
{
	struct dst_entry *dst = __sk_dst_get(sk);
	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);

	/* We'll fix this up when we get a response from the other end.
	 * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
	 */

	/* Qui viene fissata la dimensione dell'header TCP. In realt qui questa viene fissata considerando 
	 * la dimensione dell'header TCP canonico (definito in /usr/src/linux/include/linux/tcp.h) e quindi
	 * 20 bytes a cui viene aggiunta, se sono abilitati i timestamp attraverso la sysctl opportuna, un
	 * numero di bytes sufficiente a piazzare la timestamp option.
	 */

	tp->tcp_header_len = sizeof(struct tcphdr) +
		(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);

	/* If user gave his TCP_MAXSEG, record it to clamp */

	/* Spiegazione. L'utente ha la possibilit tramite ioctl di settare un limite per MSS. In tal caso
	 * mss_clamp viene settato a tale valore. 
	 * Inoltre poich stiamo aprendo la connessione adesso, la massima finestra (max_window) vista non 
	 * pu essere settata che a 0. Anche perch non l'abbiamo vista! 8)
	 * Prima di andare avanti attacco un commento di Alan che chiarisce il ruolo di alcune variabili.
	 * Leggilo perch questo commento  relativo a tcp_sync_mss() che trovi subito dopo.
         *
	 * This function synchronize snd mss to current pmtu/exthdr set.	 
	 *
	 * tp->user_mss is mss set by user by TCP_MAXSEG. It does NOT counts
	 * for TCP options, but includes only bare TCP header.
	 *
	 * tp->mss_clamp is mss negotiated at connection setup.
	 * It is minumum of user_mss and mss received with SYN.
	 * It also does not include TCP options.
	 *
	 * tp->pmtu_cookie is last pmtu, seen by this function.
	 *
	 * tp->mss_cache is current effective sending mss, including
	 * all tcp options except for SACKs. It is evaluated,
	 * taking into account current pmtu, but never exceeds
	 * tp->mss_clamp.
	 *
	 * NOTE1. rfc1122 clearly states that advertised MSS
	 * DOES NOT include either tcp or ip options.
	 *
	 * NOTE2. tp->pmtu_cookie and tp->mss_cache are READ ONLY outside
	 * this function.			--ANK (980731)
	 */

	if (tp->user_mss)
		tp->mss_clamp = tp->user_mss;
	tp->max_window = 0;
	tcp_sync_mss(sk, dst->pmtu);

	/* Settiamo un limite sulla massima finestra da notificare al peer (se non  stato gi fatto) e 
	 * settiamo l'advertised MSS.
	 */

	if (!tp->window_clamp)
		tp->window_clamp = dst->window;
	tp->advmss = dst->advmss;

	/* Questa  bella davvero! In pratica si inizializza la variabile RCV_MSS che sarebbe nient'altro
	 * che MSS usato dal peer. Ora il pi ganzo della brigata gi lo vedo alzare la mano e chiedere
	 * "Mi scusi ma se sto spedendo il SYN come faccio a sapere che diavolo succede dall'altra parte?"
	 * 30 e lode figliuolo! 8) Infatti qui per quanto strano possa sembrare si gioca a indovinare!
	 * Con un accorgimento. Nell'indecisione tra sovrastimare e sottostimare si sottostima.
	 * La correzione di questo valore la si fa poi in corsa con la tcp_measure_rcv_mss().
	 */

	tcp_initialize_rcv_mss(sk);

	/* Determiniamo ora la finestra da offrire al peer. Inoltre viene determinato eventualmente il
	 * window scaling da offrire. Il resto della funzione  semplice da capire e la lascio come
	 * compito per il volenteroso di turno.
	 */

	tcp_select_initial_window(tcp_full_space(sk),
				  tp->advmss - (tp->ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0),
				  &tp->rcv_wnd,
				  &tp->window_clamp,
				  sysctl_tcp_window_scaling,
				  &tp->rcv_wscale);

	tp->rcv_ssthresh = tp->rcv_wnd;

	sk->err = 0;
	sk->done = 0;
	tp->snd_wnd = 0;
	tcp_init_wl(tp, tp->write_seq, 0);
	tp->snd_una = tp->write_seq;
	tp->snd_sml = tp->write_seq;
	tp->rcv_nxt = 0;
	tp->rcv_wup = 0;
	tp->copied_seq = 0;

	tp->rto = TCP_TIMEOUT_INIT;
	tp->retransmits = 0;
	tcp_clear_retrans(tp);
}


/*
 * Build a SYN and send it off.
 */ 
int tcp_connect(struct sock *sk)
{
	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
	struct sk_buff *buff;

	tcp_connect_init(sk);

	/* Qui si alloca un sk_buff di dimensione opportuna preoccupandosi successivamente
	 * di riservare spazio per l'header TCP. La gestione degli sk_buff pu essere 
	 * alquanto oscura a chi vi si avvicini per la prima volta. Se  questo il caso
	 * consiglio di leggere un brevissimo (ma altrettanto utile) paper scritto da
	 * Alan Cox reperibile presso l'URL
	 *
	 * http://www.linux.org.uk/Documents/buffers.html
	 *
	 */

	buff = alloc_skb(MAX_TCP_HEADER + 15, sk->allocation);
	if (unlikely(buff == NULL))
		return -ENOBUFS;

	/* Reserve space for headers. */
	skb_reserve(buff, MAX_TCP_HEADER);

	TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN;
	TCP_ECN_send_syn(tp, buff);
	TCP_SKB_CB(buff)->sacked = 0;
	buff->csum = 0;
	TCP_SKB_CB(buff)->seq = tp->write_seq++;
	TCP_SKB_CB(buff)->end_seq = tp->write_seq;
	tp->snd_nxt = tp->write_seq;
	tp->pushed_seq = tp->write_seq;

	/* Send it off. */
	TCP_SKB_CB(buff)->when = tcp_time_stamp;
	tp->retrans_stamp = TCP_SKB_CB(buff)->when;
	__skb_queue_tail(&sk->write_queue, buff);
	tcp_charge_skb(sk, buff);
	tp->packets_out++;
	tcp_transmit_skb(sk, skb_clone(buff, GFP_KERNEL));
	TCP_INC_STATS(TcpActiveOpens);

	/* Timer for repeating the SYN until an answer. */
	tcp_reset_xmit_timer(sk, TCP_TIME_RETRANS, tp->rto);
	return 0;
}



Questa  la funzione principale che gestisce l'input dei segmenti (a
meno che il socket non sia in stato ESTABLISHED o TIME_WAIT).


	/*
	 *	This function implements the receiving procedure of RFC 793 for
	 *	all states except ESTABLISHED and TIME_WAIT. 
	 *	It's called from both tcp_v4_rcv and tcp_v6_rcv and should be
	 *	address independent.
	 */
	
	
	int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
				  struct tcphdr *th, unsigned len)
	{
		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
		int queued = 0;
	
		tp->saw_tstamp = 0;
	
		switch (sk->state) {
		case TCP_CLOSE:
			
			/* Lo stato del socket CLOSED? RFC793 stabilisce
			 * di ignorare il segmento incoming.
			 */
 
			goto discard;
	
		case TCP_LISTEN:

			/* Se il socket  in stato LISTEN *NON*  corretto
			 * che il segmento abbia il flag ACK settato ma
			 * soltanto SYN pu essere settato. Prescindo in
			 * questo momento da considerazioni relative a
			 * TTCP e simili (vedi sotto i commenti credo di
			 * Dave Miller).
			 */
			 
			if(th->ack)
				return 1;
	
			if(th->syn) {
				if(tp->af_specific->conn_request(sk, skb) < 0)
					return 1;
	
				/* Now we have several options: In theory there is 
				 * nothing else in the frame. KA9Q has an option to 
				 * send data with the syn, BSD accepts data with the
				 * syn up to the [to be] advertised window and 
				 * Solaris 2.1 gives you a protocol error. For now 
				 * we just ignore it, that fits the spec precisely 
				 * and avoids incompatibilities. It would be nice in
				 * future to drop through and process the data.
				 *
				 * Now that TTCP is starting to be used we ought to 
				 * queue this data.
				 * But, this leaves one open to an easy denial of
			 	 * service attack, and SYN cookies can't defend
				 * against this problem. So, we drop the data
				 * in the interest of security over speed.
				 */
				goto discard;
			}
			goto discard;
	
		case TCP_SYN_SENT:
	
			/* Lo stato SYN_SENT viene gestito tramite la funzione 
			 * tcp_rcv_synsent_state_process() che analizzo subito
			 * dopo.
			 */ 

			queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
			if (queued >= 0)
				return queued;
	
			/* Do step6 onward by hand. */
		
			/* Si processano eventuali Out-Of-Band data (come venivano
			 * chiamati ai tempi di 4.3 BSD), si libera skb e con 
			 * tcp_data_snd_check() si controlla se ci sono dati da
			 * spedire.
			 */

			tcp_urg(sk, skb, th);
			__kfree_skb(skb);
			tcp_data_snd_check(sk);
			return 0;
		}
	
		/* Parse delle opzioni..se ci va bene se ne usciamo col fast path
		 * altrimenti si chiama tcp_parse_options(). Se il PAWS check 
		 * fallisce e nel segmento non  settato RST si spedisce un dupack
		 * come stabilito da RFC1323 e si droppa il segmento.
		 */

		if (tcp_fast_parse_options(skb, th, tp) && tp->saw_tstamp &&
		    tcp_paws_discard(tp, skb)) {
			if (!th->rst) {
				NET_INC_STATS_BH(PAWSEstabRejected);
				tcp_send_dupack(sk, skb);
				goto discard;
			}
			/* Reset is accepted even if it did not pass PAWS. */
		}
	
		/* step 1: check sequence number */
		if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
			if (!th->rst)
				tcp_send_dupack(sk, skb);
			goto discard;
		}
	
		/* step 2: check RST bit */
		if(th->rst) {
			tcp_reset(sk);
			goto discard;
		}
	
		tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
	
		/* step 3: check security and precedence [ignored] */
	
		/*	step 4:
		 *
		 *	Check for a SYN in window.
		 */
		if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
			NET_INC_STATS_BH(TCPAbortOnSyn);
			tcp_reset(sk);
			return 1;
		}
	
		/* step 5: check the ACK field */
		if (th->ack) {
			int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH);
	
			switch(sk->state) {
			case TCP_SYN_RECV:
				if (acceptable) {
					tp->copied_seq = tp->rcv_nxt;
					mb();
					tcp_set_state(sk, TCP_ESTABLISHED);
					sk->state_change(sk);
	
					/* Note, that this wakeup is only for marginal
					 * crossed SYN case. Passively open sockets
					 * are not waked up, because sk->sleep == NULL
					 * and sk->socket == NULL.
					 */
					if (sk->socket) {
						sk_wake_async(sk,0,POLL_OUT);
					}
	
					tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
					tp->snd_wnd = ntohs(th->window) << tp->snd_wscale;
					tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(skb)->seq);
	
					/* tcp_ack considers this ACK as duplicate
					 * and does not calculate rtt.
					 * Fix it at least with timestamps.
					 */

					if (tp->saw_tstamp && tp->rcv_tsecr && !tp->srtt)
						tcp_ack_saw_tstamp(tp, 0);
	
					if (tp->tstamp_ok)
						tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
	
					tcp_init_metrics(sk);
					tcp_initialize_rcv_mss(sk);
					tcp_init_buffer_space(sk);
					tcp_fast_path_on(tp);
				} else {
					return 1;
				}
				break;
	
			case TCP_FIN_WAIT1:
				
				/* Il socket  in FIN_WAIT_1 quindi possiamo andare
				 * o in CLOSING oppure in FIN_WAIT_2.
				 */

				/* Se questo  vero significa che possiamo spostarci
				 * in FIN_WAIT_2 alla ricezione dell'ACK perch non
				 * ci sono dati da spedire nel send buffer. Pensa ad
				 * una half close invece di pensare ad una close...
				 */

				if (tp->snd_una == tp->write_seq) {
					tcp_set_state(sk, TCP_FIN_WAIT2);
					sk->shutdown |= SEND_SHUTDOWN;
					dst_confirm(sk->dst_cache);
	
					if (!sk->dead) {
						/* Wake up lingering close() */
						sk->state_change(sk);
					} else {
						int tmo;
	
						/* Di default la close(2) cerca di spedire i dati nel 
						 * send buffer, chiude la connessione e amen! 
						 * Ma se usiamo setsockopt(2) (SOL_SOCKET|SO_LINGER)
					 	 * possiamo cambiare questo comportamento evitando
						 * TIME_WAIT e quindi 2MSL. Potremmo anche inviare 
						 * un RST al peer scartando i dati nel send buffer
						 * impostando nella struct linger l_onoff diverso
						 * da 0 con l_linger 0 (linger time).
						 * Non descriver questo perch onestamente credo 
						 * che tutto ci andrebbe rimosso dal TCP. 
						 * Soltanto chi non ha capito una benemerita minchia 
						 * di TIME_WAIT e della sua utilit pu pensare che 
						 * sia una feature utile!
						 * Quote di un minchione qualunque su una qualunque
						 * ml di networking del pianeta : " ma perch non
						 * mandiamo subito un RST invece di un FIN? Cos
						 * non perderemmo tempo in TIME_WAIT per 2MSL 
						 * secondi!". Leggi Stevens idiota! E cerca di 
						 * capire!
						 */

						if (tp->linger2 < 0 ||
						    (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
						     after(TCP_SKB_CB(skb)->end_seq - th->fin, 
								tp->rcv_nxt))) {
							tcp_done(sk);
							NET_INC_STATS_BH(TCPAbortOnData);
							return 1;
						}
	
						tmo = tcp_fin_time(tp);
						if (tmo > TCP_TIMEWAIT_LEN) {
							tcp_reset_keepalive_timer(sk, tmo - 
								TCP_TIMEWAIT_LEN);
						} else if (th->fin || sk->lock.users) {
							/* Bad case. We could lose such FIN otherwise.
							 * It is not a big problem, but it looks confusing
							 * and not so rare event. We still can lose it now,
							 * if it spins in bh_lock_sock(), but it is really
							 * marginal case.
							 */
							tcp_reset_keepalive_timer(sk, tmo);
						} else {
							tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
							goto discard;
						}
					}
				}
				break;
	
			case TCP_CLOSING:
				if (tp->snd_una == tp->write_seq) {

					/* E' arrivata l'ora di crepare..non prima di
					 * una bella pausa di 2MSL.
					 */

					tcp_time_wait(sk, TCP_TIME_WAIT, 0);
					goto discard;
				}
				break;
	
			case TCP_LAST_ACK:

					/* LAST_ACK lo raggiunge soltanto chi fa passive
					 * open (ossia chi riceve il FIN). Si raggiunge 
					 * da CLOSE_WAIT. Se il peer invia FIN l'host va 
					 * in CLOSE_WAIT quando spedisce l'ACK. Quando
					 * finisce di spedire dati invia un FIN al peer
					 * va in LAST_ACK chiudendo de facto la connessione 
					 * dal suo lato. Andr in CLOSE soltanto alla ricezione
					 * dell'ACK del FIN. Qualcuno sta dicendo 'perch
					 * non va in CLOSE subito?'. Vai a mettere le ginocchia
					 * sui ceci! Se il FIN si perdesse che succederebbe?
					 * Una connessione tenuta in piedi per il piacere di
					 * vederla con lsof?! Ovviamente alla ricezione dell'
					 * ACK abbiamo soltanto l'onere di chiudere la baracca. 
					 */
  
				if (tp->snd_una == tp->write_seq) {
					tcp_update_metrics(sk);
					tcp_done(sk);
					goto discard;
				}
				break;
			}
		} else
			goto discard;
	
		/* step 6: check the URG bit */
		tcp_urg(sk, skb, th);
	
		/* step 7: process the segment text */
		switch (sk->state) {
		case TCP_CLOSE_WAIT:
		case TCP_CLOSING:
		case TCP_LAST_ACK:
			if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
				break;
		case TCP_FIN_WAIT1:
		case TCP_FIN_WAIT2:
			/* RFC 793 says to queue data in these states,
			 * RFC 1122 says we MUST send a reset. 
			 * BSD 4.4 also does reset.
			 */
			if (sk->shutdown & RCV_SHUTDOWN) {
				if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
				    after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
					NET_INC_STATS_BH(TCPAbortOnData);
					tcp_reset(sk);
					return 1;
				}
			}
			/* Fall through */
		case TCP_ESTABLISHED: 
			tcp_data_queue(sk, skb);
			queued = 1;
			break;
		}
	
		/* tcp_data could move socket to TIME-WAIT */
		if (sk->state != TCP_CLOSE) {
			tcp_data_snd_check(sk);
			tcp_ack_snd_check(sk);
		}
	
		if (!queued) { 
	discard:
			__kfree_skb(skb);
		}
		return 0;
	}


Vediamo la funzione che si preoccupa di gestire la situazione se siamo in stato
SYN_SENT e ci aspettiamo come da RFC793 un SYN-ACK dal peer. Qui nei commenti
viene riportato in parte RFC793 da cui la semplicit di lettura di questa funzione.


static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
						 struct tcphdr *th, unsigned len)
	{
		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
		int saved_clamp = tp->mss_clamp;
	
		/* Qui dobbiamo assolutamente analizzare il segmento
		 * entrante per le opzioni...perch? Perch se abbiamo
		 * spedito un SYN ragionevolmente ci abbiamo messo
		 * dentro delle nostre opzioni dicendo ad esempio 'io
		 * posso usare i SACKs'. L'altro peer ci risponder
		 * in questo caso 'anch'io' o 'io no' e questo ci 
		 * permetter di impostare dei flag specifici del
		 * socket che verranno usati fino alla morte del
		 * socket stesso.
		 */

		tcp_parse_options(skb, tp, 0);
	
		if (th->ack) {
			/* rfc793:
			 * "If the state is SYN-SENT then
			 *    first check the ACK bit
			 *      If the ACK bit is set
			 *	  If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send
			 *        a reset (unless the RST bit is set, if so drop
			 *        the segment and return)"
			 *
			 *  We do not send data with SYN, so that RFC-correct
			 *  test reduces to:
			 */

			if (TCP_SKB_CB(skb)->ack_seq != tp->snd_nxt)
				goto reset_and_undo;

			/* Un controllo PAWS (se ovviamente durante il parse delle
			 * opzioni  stato abilitato PAWS stesso).
			 */
	
			if (tp->saw_tstamp && tp->rcv_tsecr &&
			    !between(tp->rcv_tsecr, tp->retrans_stamp, tcp_time_stamp)) {
				NET_INC_STATS_BH(PAWSActiveRejected);
				goto reset_and_undo;
			}
	
			/* Now ACK is acceptable.
			 *
			 * "If the RST bit is set
			 *    If the ACK was acceptable then signal the user "error:
			 *    connection reset", drop the segment, enter CLOSED state,
			 *    delete TCB, and return."
			 */
	
			if (th->rst) {
				tcp_reset(sk);
				goto discard;
			}
	
			/* rfc793:
			 *   "fifth, if neither of the SYN or RST bits is set then
			 *    drop the segment and return."
			 *
			 *    See note below!
			 *                                        --ANK(990513)
			 */
			if (!th->syn)
				goto discard_and_undo;
	
			/* rfc793:
			 *   "If the SYN bit is on ...
			 *    are acceptable then ...
			 *    (our SYN has been ACKed), change the connection
			 *    state to ESTABLISHED..."
			 */
	
			TCP_ECN_rcv_synack(tp, th);


			/* Ricordo che snd_wl1  il sequence number usato per
			 * l'ultimo window update. A questo punto si acka 
			 * perch tutto  andato per il meglio.
			 */
	
			tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
			tcp_ack(sk, skb, FLAG_SLOWPATH);
	
			/* Ok.. it's good. Set up sequence numbers and
			 * move to established.
			 */

			tp->rcv_nxt = TCP_SKB_CB(skb)->seq+1;
			tp->rcv_wup = TCP_SKB_CB(skb)->seq+1;
	
			/* RFC1323: The window in SYN & SYN/ACK segments is
			 * never scaled.
			 */

			tp->snd_wnd = ntohs(th->window);
			tcp_init_wl(tp, TCP_SKB_CB(skb)->ack_seq, TCP_SKB_CB(skb)->seq);

			/* Si settano alcuni parametri del socket in base all'
			 * esito del parsing delle opzioni.
			 */
	
			if (tp->wscale_ok == 0) {
				tp->snd_wscale = tp->rcv_wscale = 0;

				/* window_clamp  la "maximum window to advertise" 
				 * e ovviamente non essendoci window scaling al massimo
				 * possiamo notificare 65535 bytes di finestra (16 bit)
				 */
				tp->window_clamp = min(tp->window_clamp, 65535U);
			}
	
			if (tp->saw_tstamp) {
				tp->tstamp_ok = 1;
				tp->tcp_header_len =
					sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;

				/* Nell'MSS non deve rientrare TCPOLEN_TSTAMP_ALIGNED...*/
				tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;

				/* Aggiorniamo il ts_recent perch servir */
				tcp_store_ts_recent(tp);
			} else {
				tp->tcp_header_len = sizeof(struct tcphdr);
			}

			/* Possiamo abilitare FACKs? */
	
			if (tp->sack_ok && sysctl_tcp_fack)
				tp->sack_ok |= 2;
	
			tcp_sync_mss(sk, tp->pmtu_cookie);
			tcp_initialize_rcv_mss(sk);
			tcp_init_metrics(sk);
			
			/* Vedi tcp_misc.txt dove  spiegato cosa fa la
  			 * tcp_init_buffer_space().
			 */

			tcp_init_buffer_space(sk);
	
			/* Qui me ne vado a senso...vediamo se ci prendo!
			 * Essendo il keepalive timer un timer che scatta
			 * ogni due ore (soltanto quando la connessione 
			 * idle per) per testare se la connessione  ancora
			 * in piedi o se il peer  andato a gambe per aria
			 * senza notificarci nulla ipotizzo che il flag
			 * (dichiarato volatile uhmm) keepopen sia settato
			 * dalla setsockopt(2) sotto Linux con level
			 * SOL_SOCKET e optname SO_KEEPALIVE.
			 * Appena controllato...tutto confermato! 8)
			 */

			if (sk->keepopen)
				tcp_reset_keepalive_timer(sk, keepalive_time_when(tp));
	
			if (tp->snd_wscale == 0)

				/* Aggiorna il prediction flag come da RFC1323...
				 * mitico Van Jacobson colpisce ancora! 8) Infatti
				 * sembrano profilarsi le condizioni per un data
				 * transfer senza anomalie.
				 */
 
				__tcp_fast_path_on(tp, tp->snd_wnd);
			else
				tp->pred_flags = 0;
	
			/* Remember, tcp_poll() does not lock socket!
			 * Change state from SYN-SENT only after copied_seq
			 * is initialized. */

			tp->copied_seq = tp->rcv_nxt;
			mb();
			tcp_set_state(sk, TCP_ESTABLISHED);
	
			if(!sk->dead) {
				sk->state_change(sk);
				sk_wake_async(sk, 0, POLL_OUT);
			}

			/* Se write_pending  settato (quindi una scrittura sul socket
			 * attende di poter partire) o se defer_accept  settato (l'user
			 * attende dati dopo la accept() ) o se ack.pingpong  settato
			 * (sessione interattiva).....
			 */
	
			if (tp->write_pending || tp->defer_accept || tp->ack.pingpong) {
				/* Save one ACK. Data will be ready after
				 * several ticks, if write_pending is set.
				 *
				 * It may be deleted, but with this feature tcpdumps
				 * look so _wonderfully_ clever, that I was not able
				 * to stand against the temptation 8)     --ANK
				 */
				tcp_schedule_ack(tp);
				tp->ack.lrcvtime = tcp_time_stamp;
				tp->ack.ato = TCP_ATO_MIN;
				tcp_incr_quickack(tp);
				tcp_enter_quickack_mode(tp);
				tcp_reset_xmit_timer(sk, TCP_TIME_DACK, TCP_DELACK_MAX);
	
	discard:
				__kfree_skb(skb);
				return 0;
			} else {
				tcp_send_ack(sk);
			}
			return -1;
		}
	
		/* No ACK in the segment */
	
		if (th->rst) {
			/* rfc793:
			 * "If the RST bit is set
			 *
			 *      Otherwise (no ACK) drop the segment and return."
			 */
	
			goto discard_and_undo;
		}
	
		/* PAWS check. */
		if (tp->ts_recent_stamp && tp->saw_tstamp && tcp_paws_check(tp, 0))
			goto discard_and_undo;
	
		if (th->syn) {

			/* We see SYN without ACK. It is attempt of
			 * simultaneous connect with crossed SYNs.
			 * Particularly, it can be connect to self.
			 */
			
			/* Per una trattazione lucida dei simultaneous open
			 * vedi W.R.Stevens "TCP-IP Illustrated vol.1" cap.18
			 */

			/* Entrambi gli host devono andare in SYN_RECV e il codice
			 * che segue  uguale a quello visto prima.
			 */

			tcp_set_state(sk, TCP_SYN_RECV);
	
			if (tp->saw_tstamp) {
				tp->tstamp_ok = 1;
				tcp_store_ts_recent(tp);
				tp->tcp_header_len =
					sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
			} else {
				tp->tcp_header_len = sizeof(struct tcphdr);
			}
	
			tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
			tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
	
			/* RFC1323: The window in SYN & SYN/ACK segments is
			 * never scaled.
			 */

			tp->snd_wnd = ntohs(th->window);
			tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
			tp->max_window = tp->snd_wnd;
	
			tcp_sync_mss(sk, tp->pmtu_cookie);
			tcp_initialize_rcv_mss(sk);
	
			TCP_ECN_rcv_syn(tp, th);

			/* Qui entrambi gli host spediscono il SYN-ACK e all'avvenuta
			 * ricezione vanno in ESTABLISHED.
			 */
	
			tcp_send_synack(sk);
	#if 0
			/* Note, we could accept data and URG from this segment.
			 * There are no obstacles to make this.
			 *
			 * However, if we ignore data in ACKless segments sometimes,
			 * we have no reasons to accept it sometimes.
			 * Also, seems the code doing it in step6 of tcp_rcv_state_process
			 * is not flawless. So, discard packet for sanity.
			 * Uncomment this return to process the data.
			 */
			return -1;
	#else
			goto discard;
	#endif
		}
		/* "fifth, if neither of the SYN or RST bits is set then
		 * drop the segment and return."
		 */
	
	discard_and_undo:
		tcp_clear_options(tp);
		tp->mss_clamp = saved_clamp;
		goto discard;
	
	reset_and_undo:
		tcp_clear_options(tp);
		tp->mss_clamp = saved_clamp;
		return 1;
	}



Vediamo adesso il caso in cui il socket sia in stato LISTEN. Ipotizziamo che
arrivi un segmento destinato alla porta bindata dal socket. La funzione
tcp_v4_do_rcv() [vedi tcp.txt] fa questo.

[..]

if (sk->state == TCP_LISTEN) {
                        struct sock *nsk = tcp_v4_hnd_req(sk, skb);
                        if (!nsk)
                                goto discard;

                        if (nsk != sk) {
                                if (tcp_child_process(sk, nsk, skb))
                                        goto reset;
                                return 0;
                        }
                }

[..]


Quindi chi si incarica di gestire il tutto  la tcp_v4_hnd_req() che proponiamo
di seguito. Funzione molto intuitiva e quindi mi limito ad alcuni semplici
commenti.


static struct sock *tcp_v4_hnd_req(struct sock *sk,struct sk_buff *skb)
	{
		struct open_request *req, **prev;
		struct tcphdr *th = skb->h.th;
		struct iphdr *iph = skb->nh.iph;
		struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
		struct sock *nsk;

		/* Vediamo queste struct prima di andare avanti...
		 *
		 * struct open_request {
		 *	struct open_request	*dl_next; /* Must be first member! */
		 *      __u32			rcv_isn;
		 *	__u32			snt_isn;
		 *	__u16			rmt_port;
		 * 	__u16			mss;
		 * 	__u8			retrans;
		 *	__u8			__pad;
		 *	__u16	snd_wscale : 4, 
		 *		rcv_wscale : 4, 
		 *		tstamp_ok : 1,
		 *		sack_ok : 1,
		 *		wscale_ok : 1,
		 *		ecn_ok : 1,
		 *		acked : 1;
		 * /* The following two fields can be easily recomputed I think -AK */
		 * 	__u32			window_clamp;	/* window clamp at creation time */
		 *	__u32			rcv_wnd;	/* rcv_wnd offered first time */
		 *	__u32			ts_recent;
		 * 	unsigned long		expires;
		 *	struct or_calltable	*class;
		 * 	struct sock		*sk;
		 * 	union {
		 *		struct tcp_v4_open_req v4_req;
		 * 	#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
		 *		struct tcp_v6_open_req v6_req;
		 *	#endif
		 *	} af;
		 *};
		 *
		 *
		 * struct or_calltable {
		 *	int  family;
		 *	int  (*rtx_syn_ack) (struct sock *sk, struct open_request *req, struct dst_entry*);
		 * 	void (*send_ack) (struct sk_buff *skb, struct open_request *req);
		 *	void (*destructor)	(struct open_request *req);
		 * 	void (*send_reset)	(struct sk_buff *skb);
		 *};
		 */

	
		/* Find possible connection requests. */
		req = tcp_v4_search_req(tp, &prev,
					th->source,
					iph->saddr, iph->daddr);

		/* tcp_check_req() processa segmenti incoming quando l'host
		 *  in stato SYN_RECV.
		 */

		if (req)
			return tcp_check_req(sk, skb, req, prev);
	
		nsk = __tcp_v4_lookup_established(skb->nh.iph->saddr,
						  th->source,
						  skb->nh.iph->daddr,
						  ntohs(th->dest),
						  tcp_v4_iif(skb));
	
		if (nsk) {
			if (nsk->state != TCP_TIME_WAIT) {
				bh_lock_sock(nsk);
				return nsk;
			}
			tcp_tw_put((struct tcp_tw_bucket*)nsk);
			return NULL;
		}

	/* Per la felicit di Bernstein (il pap dei syncookies) il mio kernel
	 * non li ha mai usati! Quindi a casa mia queste righe vengono saltate
	 * a pi pari! 8)
	 */
	
	#ifdef CONFIG_SYN_COOKIES
		if (!th->rst && !th->syn && th->ack)
			sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
	#endif
		return sk;
	}


Vediamo adesso la tcp_fin(). Il codice  ampiamente commentato ed  tra l'
altro nient'altro che una riproposizione in codice di quanto affermato 
in RFC793. Ritengo inutile aggiungere commenti.


/*
 *      Process the FIN bit. This now behaves as it is supposed to work
 *      and the FIN takes effect when it is validly part of sequence
 *      space. Not before when we get holes.
 *
 *      If we are ESTABLISHED, a received fin moves us to CLOSE-WAIT
 *      (and thence onto LAST-ACK and finally, CLOSE, we never enter
 *      TIME-WAIT)
 *
 *      If we are in FINWAIT-1, a received FIN indicates simultaneous
 *      close and we go into CLOSING (and later onto TIME-WAIT)
 *
 *      If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT.
 */

static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
{
        struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);

        tcp_schedule_ack(tp);

        sk->shutdown |= RCV_SHUTDOWN;
        sk->done = 1;

        switch(sk->state) {
                case TCP_SYN_RECV:
                case TCP_ESTABLISHED:
                        /* Move to CLOSE_WAIT */
                        tcp_set_state(sk, TCP_CLOSE_WAIT);
                        tp->ack.pingpong = 1;
                        break;
                case TCP_CLOSE_WAIT:
                case TCP_CLOSING:
                        /* Received a retransmission of the FIN, do
                         * nothing.
                         */
                        break;
                case TCP_LAST_ACK:
                        /* RFC793: Remain in the LAST-ACK state. */
                        break;
                case TCP_FIN_WAIT1:
                        /* This case occurs when a simultaneous close
                         * happens, we must ack the received FIN and
                         * enter the CLOSING state.
  			 */
                        tcp_send_ack(sk);
                        tcp_set_state(sk, TCP_CLOSING);
                        break;
                case TCP_FIN_WAIT2:
                        /* Received a FIN -- send ACK and enter TIME_WAIT. */
                        tcp_send_ack(sk);
                        tcp_time_wait(sk, TCP_TIME_WAIT, 0);
                        break;
                default:
                        /* Only TCP_LISTEN and TCP_CLOSE are left, in these
                         * cases we should never reach this piece of code.
                         */
                        printk("tcp_fin: Impossible, sk->state=%d\n", sk->state);
                        break;
        };

        /* It _is_ possible, that we have something out-of-order _after_ FIN.
         * Probably, we should reset in this case. For now drop them.
         */
        __skb_queue_purge(&tp->out_of_order_queue);
        if (tp->sack_ok)
                tcp_sack_reset(tp);
        tcp_mem_reclaim(sk);

        if (!sk->dead) {
                sk->state_change(sk);
	 /* Do not send POLL_HUP for half duplex close. */
                if (sk->shutdown == SHUTDOWN_MASK || sk->state == TCP_CLOSE)
                        sk_wake_async(sk, 1, POLL_HUP);
                else
                        sk_wake_async(sk, 1, POLL_IN);
        }
}

