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

Next file: linux/net/ipv6/udp.c
Previous file: linux/net/ipv6/sit.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/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.128 2000/12/08 17:15:54 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.136 2001/04/20 20:46:19 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -635,6 +635,7 @@
 	}
 
 	ip6_dst_store(sk, dst, NULL);
+	sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
 
 	if (saddr == NULL) {
 		err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf);
@@ -678,25 +679,23 @@
 failure:
 	__sk_dst_reset(sk);
 	sk->dport = 0;
+	sk->route_caps = 0;
 	return err;
 }
 
-void tcp_v6_err(struct sk_buff *skb, struct ipv6hdr *hdr,
-		struct inet6_skb_parm *opt,
-		int type, int code, unsigned char *header, __u32 info)
+void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+		int type, int code, int offset, __u32 info)
 {
+	struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;
 	struct in6_addr *saddr = &hdr->saddr;
 	struct in6_addr *daddr = &hdr->daddr;
-	struct tcphdr *th = (struct tcphdr *)header;
+	struct tcphdr *th = (struct tcphdr *)(skb->data+offset);
 	struct ipv6_pinfo *np;
 	struct sock *sk;
 	int err;
 	struct tcp_opt *tp; 
 	__u32 seq;
 
-	if (header + 8 > skb->tail)
-		return;
-
 	sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source, skb->dev->ifindex);
 
 	if (sk == NULL) {
@@ -914,10 +913,15 @@
 			      struct sk_buff *skb)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	
-	th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 
-				    csum_partial((char *)th, th->doff<<2, 
-						 skb->csum));
+
+	if (skb->ip_summed == CHECKSUM_HW) {
+		th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,  0);
+		skb->csum = offsetof(struct tcphdr, check);
+	} else {
+		th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP, 
+					    csum_partial((char *)th, th->doff<<2, 
+							 skb->csum));
+	}
 }
 
 
@@ -1298,6 +1302,7 @@
 	MOD_INC_USE_COUNT;
 
 	ip6_dst_store(newsk, dst, NULL);
+	sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
 
@@ -1371,22 +1376,20 @@
 static int tcp_v6_checksum_init(struct sk_buff *skb)
 {
 	if (skb->ip_summed == CHECKSUM_HW) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
+				  &skb->nh.ipv6h->daddr,skb->csum))
+			return 0;
+		NETDEBUG(printk(KERN_DEBUG "hw tcp v6 csum failed\n"));
+	}
+	if (skb->len <= 76) {
 		if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-				 &skb->nh.ipv6h->daddr,skb->csum)) {
-			NETDEBUG(printk(KERN_DEBUG "hw tcp v6 csum failed\n"));
+				 &skb->nh.ipv6h->daddr,skb_checksum(skb, 0, skb->len, 0)))
 			return -1;
-		}
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	} else {
-		if (skb->len <= 76) {
-			if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-					 &skb->nh.ipv6h->daddr,csum_partial((char *)skb->h.th, skb->len, 0)))
-				return -1;
-			skb->ip_summed = CHECKSUM_UNNECESSARY;
-		} else {
-			skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-						  &skb->nh.ipv6h->daddr,0);
-		}
+		skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
+					  &skb->nh.ipv6h->daddr,0);
 	}
 	return 0;
 }
@@ -1526,46 +1529,45 @@
 	return 0;
 }
 
-int tcp_v6_rcv(struct sk_buff *skb, unsigned long len)
+int tcp_v6_rcv(struct sk_buff *skb)
 {
 	struct tcphdr *th;	
 	struct sock *sk;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
 	int ret;
 
-	th = skb->h.th;
-
 	if (skb->pkt_type != PACKET_HOST)
 		goto discard_it;
 
 	/*
-	 *	Pull up the IP header.
+	 *	Count it even if it's bad.
 	 */
+	TCP_INC_STATS_BH(TcpInSegs);
 
-	__skb_pull(skb, skb->h.raw - skb->data);
+	if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+		goto discard_it;
 
-	/*
-	 *	Count it even if it's bad.
-	 */
+	th = skb->h.th;
 
-	TCP_INC_STATS_BH(TcpInSegs);
+	if (th->doff < sizeof(struct tcphdr)/4)
+		goto bad_packet;
+	if (!pskb_may_pull(skb, th->doff*4))
+		goto discard_it;
 
-	if (th->doff < sizeof(struct tcphdr)/4 ||
-	    (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+	if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
 	     tcp_v6_checksum_init(skb) < 0))
 		goto bad_packet;
 
+	th = skb->h.th;
 	TCP_SKB_CB(skb)->seq = ntohl(th->seq);
 	TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
-				    len - th->doff*4);
+				    skb->len - th->doff*4);
 	TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
 	TCP_SKB_CB(skb)->when = 0;
 	TCP_SKB_CB(skb)->flags = ip6_get_dsfield(skb->nh.ipv6h);
 	TCP_SKB_CB(skb)->sacked = 0;
-	skb->used = 0;
 
-	sk = __tcp_v6_lookup(saddr, th->source, daddr, ntohs(th->dest), tcp_v6_iif(skb));
+	sk = __tcp_v6_lookup(&skb->nh.ipv6h->saddr, th->source,
+			     &skb->nh.ipv6h->daddr, ntohs(th->dest), tcp_v6_iif(skb));
 
 	if (!sk)
 		goto no_tcp_socket;
@@ -1591,7 +1593,7 @@
 	return ret;
 
 no_tcp_socket:
-	if (len < (th->doff<<2) || tcp_checksum_complete(skb)) {
+	if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 bad_packet:
 		TCP_INC_STATS_BH(TcpInErrs);
 	} else {
@@ -1612,7 +1614,7 @@
 	goto discard_it;
 
 do_time_wait:
-	if (len < (th->doff<<2) || tcp_checksum_complete(skb)) {
+	if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 		TCP_INC_STATS_BH(TcpInErrs);
 		sock_put(sk);
 		goto discard_it;
@@ -1673,10 +1675,12 @@
 		if (dst->error) {
 			err = dst->error;
 			dst_release(dst);
+			sk->route_caps = 0;
 			return err;
 		}
 
 		ip6_dst_store(sk, dst, NULL);
+		sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
 	}
 
 	return 0;
@@ -1819,6 +1823,7 @@
 	sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
 
 	sk->write_space = tcp_write_space;
+	sk->use_write_queue = 1;
 
 	sk->sndbuf = sysctl_tcp_wmem[1];
 	sk->rcvbuf = sysctl_tcp_rmem[1];
@@ -1837,7 +1842,7 @@
 	/* Cleanup up the write buffer. */
   	tcp_writequeue_purge(sk);
 
-	/* Cleans up our, hopefuly empty, out_of_order_queue. */
+	/* Cleans up our, hopefully empty, out_of_order_queue. */
   	__skb_queue_purge(&tp->out_of_order_queue);
 
 	/* Clean prequeue, it must be empty really */
@@ -1847,6 +1852,10 @@
 	if(sk->prev != NULL)
 		tcp_put_port(sk);
 
+	/* If sendmsg cached page exists, toss it. */
+	if (tp->sndmsg_page != NULL)
+		__free_page(tp->sndmsg_page);
+
 	atomic_dec(&tcp_sockets_allocated);
 
 	return inet6_destroy_sock(sk);
@@ -1967,7 +1976,7 @@
 	off_t begin, pos = 0;
 	char tmpbuf[LINE_LEN+2];
 
-	if(offset < LINE_LEN+1)
+	if (offset < LINE_LEN+1)
 		len += sprintf(buffer, LINE_FMT,
 			       "  sl  "						/* 6 */
 			       "local_address                         "		/* 38 */
@@ -1997,7 +2006,7 @@
 			if (pos >= offset) {
 				get_tcp6_sock(sk, tmpbuf, num);
 				len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-				if (len >= length) {
+				if (pos >= offset + length) {
 					tcp_listen_unlock();
 					goto out_no_bh;
 				}
@@ -2016,7 +2025,7 @@
 							continue;
 						get_openreq6(sk, req, tmpbuf, num, uid);
 						len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-						if(len >= length) { 
+						if (pos >= offset + length) { 
 							read_unlock_bh(&tp->syn_wait_lock);
 							tcp_listen_unlock();
 							goto out_no_bh;
@@ -2048,7 +2057,7 @@
 				continue;
 			get_tcp6_sock(sk, tmpbuf, num);
 			len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-			if(len >= length) {
+			if (pos >= offset + length) {
 				read_unlock(&head->lock);
 				goto out;
 			}
@@ -2063,7 +2072,7 @@
 				continue;
 			get_timewait6_sock(tw, tmpbuf, num);
 			len += sprintf(buffer+len, LINE_FMT, tmpbuf);
-			if(len >= length) {
+			if (pos >= offset + length) {
 				read_unlock(&head->lock);
 				goto out;
 			}
@@ -2078,7 +2087,7 @@
 	begin = len - (pos - offset);
 	*start = buffer + begin;
 	len -= begin;
-	if(len > length)
+	if (len > length)
 		len = length;
 	if (len < 0)
 		len = 0; 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)