patch-2.1.36 linux/net/ipv6/tcp_ipv6.c

Next file: linux/net/unix/af_unix.c
Previous file: linux/net/ipv6/route.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.35/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.20 1997/04/13 10:31:48 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.27 1997/04/22 02:53:20 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -41,6 +41,10 @@
 
 #include <asm/uaccess.h>
 
+extern int sysctl_tcp_sack;
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
+
 static void	tcp_v6_send_reset(struct in6_addr *saddr,
 				  struct in6_addr *daddr,
 				  struct tcphdr *th, struct proto *prot,
@@ -65,7 +69,8 @@
 
 	hashent ^= (laddr->s6_addr32[0] ^ laddr->s6_addr32[1]);
 	hashent ^= (faddr->s6_addr32[0] ^ faddr->s6_addr32[1]);
-	return (hashent & (TCP_HTABLE_SIZE - 1));
+	hashent ^= (faddr->s6_addr32[2] ^ faddr->s6_addr32[3]);
+	return (hashent & ((TCP_HTABLE_SIZE/2) - 1));
 }
 
 static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
@@ -166,11 +171,14 @@
 	if(state != TCP_CLOSE) {
 		struct sock **skp;
 
-		if(state == TCP_LISTEN)
+		if(state == TCP_LISTEN) {
 			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
-		else
-			skp = &tcp_established_hash[tcp_v6_sk_hashfn(sk)];
-
+		} else {
+			int hash = tcp_v6_sk_hashfn(sk);
+			if(state == TCP_TIME_WAIT)
+				hash += (TCP_HTABLE_SIZE/2);
+			skp = &tcp_established_hash[hash];
+		}
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
 		*skp = sk;
@@ -180,11 +188,12 @@
 	SOCKHASH_UNLOCK();
 }
 
-static struct sock *tcp_v6_lookup_longway(struct in6_addr *daddr, unsigned short hnum)
+static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum)
 {
-	struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)];
+	struct sock *sk;
 	struct sock *result = NULL;
 
+	sk = tcp_listening_hash[tcp_lhashfn(hnum)];
 	for(; sk; sk = sk->next) {
 		if((sk->num == hnum) && (sk->family == AF_INET6)) {
 			struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
@@ -207,13 +216,13 @@
 {
 	unsigned short hnum = ntohs(dport);
 	struct sock *sk;
+	int hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
 
 	/* Optimize here for direct hit, only listening connections can
 	 * have wildcards anyways.  It is assumed that this code only
 	 * gets called from within NET_BH.
 	 */
-	sk = tcp_established_hash[tcp_v6_hashfn(daddr, hnum, saddr, sport)];
-	for(; sk; sk = sk->next)
+	for(sk = tcp_established_hash[hash]; sk; sk = sk->next)
 		/* For IPV6 do the cheaper port and family tests first. */
 		if(sk->num		== hnum			&& /* local port     */
 		   sk->family		== AF_INET6		&& /* address family */
@@ -221,7 +230,17 @@
 		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
 		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
 			goto hit; /* You sunk my battleship! */
-	sk = tcp_v6_lookup_longway(daddr, hnum);
+
+	/* Must check for a TIME_WAIT'er before going to listener hash. */
+	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
+		if(sk->num		== hnum			&& /* local port     */
+		   sk->family		== AF_INET6		&& /* address family */
+		   sk->dummy_th.dest	== sport		&& /* remote port    */
+		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
+		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+			goto hit;
+
+	sk = tcp_v6_lookup_listener(daddr, hnum);
 hit:
 	return sk;
 }
@@ -267,7 +286,7 @@
 	struct tcphdr *th;
 	struct sk_buff *buff;
 	struct sk_buff *skb1;
-	__u8 *ptr;
+	int tmp;
 	int addr_type;
 
 	if (sk->state != TCP_CLOSE) 
@@ -369,6 +388,8 @@
 		ipv6_addr_copy(&np->saddr, saddr);
 	}
 
+	/* FIXME: Need to do tcp_v6_unique_address() here! -DaveM */
+
 	/*
 	 *	Init variables
 	 */
@@ -413,9 +434,8 @@
 	th->ack = 0;
 	th->window = 2;
 	th->syn = 1;
-	th->doff = 6;
 
-	sk->window_clamp = 0;
+	tp->window_clamp = 0;
 
 	sk->mtu = dst->pmtu;
 	sk->mss = sk->mtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
@@ -424,14 +444,12 @@
 	 *	Put in the TCP options to say MTU.
 	 */
 
-	ptr = skb_put(buff,4);
-	ptr[0] = 2;
-	ptr[1] = 4;
-	ptr[2] = (sk->mss) >> 8;
-	ptr[3] = (sk->mss) & 0xff;
-	buff->csum = csum_partial(ptr, 4, 0);
-
-	tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + 4, buff);
+        tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack,
+                sysctl_tcp_timestamps,
+                sysctl_tcp_window_scaling?tp->rcv_wscale:0);
+        th->doff = sizeof(*th)/4 + (tmp>>2);
+	buff->csum = 0;
+	tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + tmp, buff);
 
 	tcp_set_state(sk, TCP_SYN_SENT);
 
@@ -445,10 +463,10 @@
 
 	tcp_init_xmit_timers(sk);
 
-	atomic_set(&sk->retransmits, 0);
+	tp->retransmits = 0;
 
 	skb_queue_tail(&sk->write_queue, buff);
-	atomic_inc(&sk->packets_out);
+	tp->packets_out++;
 	buff->when = jiffies;
 	skb1 = skb_clone(buff, GFP_KERNEL);
 	skb_set_owner_w(skb1, sk);
@@ -573,9 +591,9 @@
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	struct sk_buff * skb;
 	struct tcphdr *th;
-	unsigned char *ptr;
 	struct dst_entry *dst;
 	struct flowi fl;
+	int tmp;
 
 	skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
 	if (skb == NULL)
@@ -615,17 +633,12 @@
 	
 	th->window = ntohs(tp->rcv_wnd);
 
-	/* FIXME: csum_partial() of a four byte quantity is itself! -DaveM */
-	ptr = skb_put(skb, TCPOLEN_MSS);
-	ptr[0] = TCPOPT_MSS;
-	ptr[1] = TCPOLEN_MSS;
-	ptr[2] = (dst->pmtu >> 8) & 0xff;
-	ptr[3] = dst->pmtu & 0xff;
-	skb->csum = csum_partial(ptr, TCPOLEN_MSS, 0);
-
-	th->check = tcp_v6_check(th, sizeof(*th) + TCPOLEN_MSS,
+	tmp = tcp_syn_build_options(skb, sk->mss, req->sack_ok, req->tstamp_ok,
+		(req->snd_wscale)?tp->rcv_wscale:0);
+	th->doff = sizeof(*th)/4 + (tmp>>2);
+	th->check = tcp_v6_check(th, sizeof(*th) + tmp,
 				 &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
-				 csum_partial((char *)th, sizeof(*th), skb->csum));
+				 csum_partial((char *)th, sizeof(*th)+tmp, skb->csum));
 
 	ip6_dst_store(sk, dst);
 	ip6_xmit(sk, skb, &fl, req->af.v6_req.opt);
@@ -646,6 +659,7 @@
 static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
 			       __u32 isn)
 {
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	struct open_request *req;
 	__u16 req_mss;
 	
@@ -680,7 +694,8 @@
 	req->rcv_isn = skb->seq;
 	req->snt_isn = isn;
 
-	req_mss = tcp_parse_options(skb->h.th);
+	tcp_parse_options(skb->h.th,tp);
+	req_mss = tp->in_mss;
 	if (!req_mss)
 		req_mss = 536;
 	req->mss = req_mss;
@@ -714,7 +729,7 @@
 	th->check = 0;
 	
 	th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 
-				    csum_partial((char *)th, sizeof(*th), 
+				    csum_partial((char *)th, th->doff<<2, 
 						 skb->csum));
 }
 
@@ -773,8 +788,6 @@
 	 *	Unused
 	 */
 
-	newsk->send_head = NULL;
-
 	newtp = &(newsk->tp_pinfo.af_tcp);
 	np = &newsk->net_pinfo.af_inet6;
 
@@ -787,13 +800,13 @@
 
 	newsk->prot->init(newsk);
 
-	newsk->cong_count = 0;
-	newsk->ssthresh = 0;
+	newtp->snd_cwnd_cnt = 0;
+#if 0 /* Don't mess up the initialization we did in the init routine! */
+	newtp->snd_ssthresh = 0;
+#endif
 	newtp->backoff = 0;
-	newsk->intr = 0;
 	newsk->proc = 0;
 	newsk->done = 0;
-	newsk->partial = NULL;
 	newsk->pair = NULL;
 	atomic_set(&newsk->wmem_alloc, 0);
 	atomic_set(&newsk->rmem_alloc, 0);
@@ -805,24 +818,23 @@
 	newsk->shutdown = 0;
 	newsk->ack_backlog = 0;
 
-	newsk->fin_seq = req->rcv_isn;
+	newtp->fin_seq = req->rcv_isn;
 	newsk->syn_seq = req->rcv_isn;
 	newsk->state = TCP_SYN_RECV;
 	newsk->timeout = 0;
-	newsk->ip_xmit_timeout = 0;
 
 	newsk->write_seq = req->snt_isn;
 
 	newtp->snd_wnd = ntohs(skb->h.th->window);
-	newsk->max_window = newtp->snd_wnd;
+	newtp->max_window = newtp->snd_wnd;
 	newtp->snd_wl1 = req->rcv_isn;
 	newtp->snd_wl2 = newsk->write_seq;
 	newtp->snd_una = newsk->write_seq++;
 	newtp->snd_nxt = newsk->write_seq;
 
 	newsk->urg_data = 0;
-	atomic_set(&newsk->packets_out, 0);
-	atomic_set(&newsk->retransmits, 0);
+	newtp->packets_out = 0;
+	newtp->retransmits = 0;
 	newsk->linger=0;
 	newsk->destroy = 0;
 	init_timer(&newsk->timer);
@@ -861,13 +873,25 @@
 
 	ip6_dst_store(newsk, dst);
 
+        newtp->sack_ok = req->sack_ok;
+        newtp->tstamp_ok = req->tstamp_ok;
+        newtp->snd_wscale = req->snd_wscale;
+        newtp->ts_recent = req->ts_recent;
+        if (newtp->tstamp_ok) {
+                newtp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: define the contant. */
+                newsk->dummy_th.doff += 3;
+        } else {
+                newtp->tcp_header_len = sizeof(struct tcphdr);
+        }
+  
 	if (dst->error)
 		newsk->mtu = req->af.v6_req.dev->mtu;
 	else
 		newsk->mtu = dst->pmtu;
 
-	newsk->mss = min(req->mss, (newsk->mtu - sizeof(struct ipv6hdr) -
-				    sizeof(struct tcphdr)));
+	newsk->mss = min(req->mss+sizeof(struct tcphdr)-newtp->tcp_header_len,
+		(newsk->mtu - sizeof(struct ipv6hdr) - newtp->tcp_header_len));
+	/* XXX tp->window_clamp??? -DaveM */
 
 	newsk->daddr	= LOOPBACK4_IPV6;
 	newsk->saddr	= LOOPBACK4_IPV6;
@@ -957,7 +981,7 @@
 	if (!req)
 		return sk;
 
-	do {
+	while(req) {
 		if (!ipv6_addr_cmp(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr) &&
 		    !ipv6_addr_cmp(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr) &&
 		    req->rmt_port == skb->h.th->source) {
@@ -994,7 +1018,8 @@
 			req->sk = sk;
 			break;
 		}
-	} while ((req = req->dl_next) != tp->syn_wait_queue);
+		req = req->dl_next;
+	}
 	return sk;
 }
 
@@ -1057,7 +1082,6 @@
 		skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
 		skb->ack_seq = ntohl(th->ack_seq);
 
-		skb->acked = 0;
 		skb->used = 0;
 	}
 
@@ -1286,7 +1310,7 @@
 
 	/* start with only sending one packet at a time. */
 	tp->snd_cwnd = 1;
-	sk->ssthresh = 0x7fffffff;
+	tp->snd_ssthresh = 0x7fffffff;
 
 	sk->priority = 1;
 	sk->state = TCP_CLOSE;
@@ -1306,6 +1330,10 @@
   	sk->dummy_th.ack=1;
   	sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
 
+	/* Init SYN queue. */
+	tp->syn_wait_queue = NULL;
+	tp->syn_wait_last = &tp->syn_wait_queue;
+
 	sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
 
 	return 0;
@@ -1325,19 +1353,15 @@
 	 *	Cleanup up the write buffer.
 	 */
 
-  	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
-		IS_SKB(skb);
+  	while((skb = skb_dequeue(&sk->write_queue)) != NULL)
 		kfree_skb(skb, FREE_WRITE);
-  	}
 
 	/*
 	 *  Cleans up our, hopefuly empty, out_of_order_queue
 	 */
 
-  	while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL) {
-		IS_SKB(skb);
+  	while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL)
 		kfree_skb(skb, FREE_READ);
-  	}
 
 	/*
 	 *	Release destination entry

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov