patch-2.4.4 linux/net/ipv4/tcp_ipv4.c

Next file: linux/net/ipv4/tcp_minisocks.c
Previous file: linux/net/ipv4/tcp_input.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.222 2000/12/08 17:15:53 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.229 2001/04/20 20:46:19 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -670,6 +670,7 @@
 	}
 
 	__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;
@@ -717,6 +718,7 @@
 	tp->ext_header_len = 0;
 	if (sk->protinfo.af_inet.opt)
 		tp->ext_header_len = sk->protinfo.af_inet.opt->optlen;
+	sk->protinfo.af_inet.id = tp->write_seq^jiffies;
 
 	tp->mss_clamp = 536;
 
@@ -726,6 +728,7 @@
 
 failure:
 	__sk_dst_reset(sk);
+	sk->route_caps = 0;
 	sk->dport = 0;
 	return err;
 }
@@ -850,32 +853,21 @@
  *
  */
 
-void tcp_v4_err(struct sk_buff *skb, unsigned char *dp, int len)
+void tcp_v4_err(struct sk_buff *skb, u32 info)
 {
-	struct iphdr *iph = (struct iphdr*)dp;
-	struct tcphdr *th; 
+	struct iphdr *iph = (struct iphdr*)skb->data;
+	struct tcphdr *th = (struct tcphdr*)(skb->data+(iph->ihl<<2));
 	struct tcp_opt *tp;
 	int type = skb->h.icmph->type;
 	int code = skb->h.icmph->code;
-#if ICMP_MIN_LENGTH < 14
-	int no_flags = 0;
-#else
-#define no_flags 0
-#endif
 	struct sock *sk;
 	__u32 seq;
 	int err;
 
-	if (len < (iph->ihl << 2) + ICMP_MIN_LENGTH) { 
+	if (skb->len < (iph->ihl << 2) + 8) {
 		ICMP_INC_STATS_BH(IcmpInErrors); 
 		return;
 	}
-#if ICMP_MIN_LENGTH < 14
-	if (len < (iph->ihl << 2) + 14)
-		no_flags = 1;
-#endif
-
-	th = (struct tcphdr*)(dp+(iph->ihl<<2));
 
 	sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, tcp_v4_iif(skb));
 	if (sk == NULL) {
@@ -921,7 +913,7 @@
 
 		if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
 			if (sk->lock.users == 0)
-				do_pmtu_discovery(sk, iph, ntohs(skb->h.icmph->un.frag.mtu));
+				do_pmtu_discovery(sk, iph, info);
 			goto out;
 		}
 
@@ -940,15 +932,6 @@
 		if (sk->lock.users != 0)
 			goto out;
 
-		/* The final ACK of the handshake should be already 
-		 * handled in the new socket context, not here.
-		 * Strictly speaking - an ICMP error for the final
-		 * ACK should set the opening flag, but that is too
-		 * complicated right now. 
-		 */ 
-		if (!no_flags && !th->syn && !th->ack)
-			goto out;
-
 		req = tcp_v4_search_req(tp, iph, th, &prev); 
 		if (!req)
 			goto out;
@@ -976,8 +959,6 @@
 	case TCP_SYN_RECV:  /* Cannot happen.
 			       It can f.e. if SYNs crossed.
 			     */ 
-		if (!no_flags && !th->syn)
-			goto out;
 		if (sk->lock.users == 0) {
 			TCP_INC_STATS_BH(TcpAttemptFails);
 			sk->err = err;
@@ -1023,8 +1004,13 @@
 void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, 
 		       struct sk_buff *skb)
 {
-	th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
-				 csum_partial((char *)th, th->doff<<2, skb->csum));
+	if (skb->ip_summed == CHECKSUM_HW) {
+		th->check = ~tcp_v4_check(th, len, sk->saddr, sk->daddr, 0);
+		skb->csum = offsetof(struct tcphdr, check);
+	} else {
+		th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
+					 csum_partial((char *)th, th->doff<<2, skb->csum));
+	}
 }
 
 /*
@@ -1445,6 +1431,7 @@
 		goto exit;
 
 	newsk->dst_cache = dst;
+	newsk->route_caps = dst->dev->features;
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
 	newsk->daddr = req->af.v4_req.rmt_addr;
@@ -1457,6 +1444,7 @@
 	newtp->ext_header_len = 0;
 	if (newsk->protinfo.af_inet.opt)
 		newtp->ext_header_len = newsk->protinfo.af_inet.opt->optlen;
+	newsk->protinfo.af_inet.id = newtp->write_seq^jiffies;
 
 	tcp_sync_mss(newsk, dst->pmtu);
 	newtp->advmss = dst->advmss;
@@ -1512,23 +1500,23 @@
 static int tcp_v4_checksum_init(struct sk_buff *skb)
 {
 	if (skb->ip_summed == CHECKSUM_HW) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		if (!tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
+				  skb->nh.iph->daddr,skb->csum))
+			return 0;
+
+		NETDEBUG(printk(KERN_DEBUG "hw tcp v4 csum failed\n"));
+		skb->ip_summed = CHECKSUM_NONE;
+	}
+	if (skb->len <= 76) {
 		if (tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
-				 skb->nh.iph->daddr,skb->csum)) {
-			NETDEBUG(printk(KERN_DEBUG "hw tcp v4 csum failed\n"));
+				 skb->nh.iph->daddr,
+				 skb_checksum(skb, 0, skb->len, 0)))
 			return -1;
-		}
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	} else {
-		if (skb->len <= 76) {
-			if (tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
-					 skb->nh.iph->daddr,
-					 csum_partial((char *)skb->h.th, skb->len, 0)))
-				return -1;
-			skb->ip_summed = CHECKSUM_UNNECESSARY;
-		} else {
-			skb->csum = ~tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
-						  skb->nh.iph->daddr,0);
-		}
+		skb->csum = ~tcp_v4_check(skb->h.th,skb->len,skb->nh.iph->saddr,
+					  skb->nh.iph->daddr,0);
 	}
 	return 0;
 }
@@ -1601,7 +1589,7 @@
  *	From tcp_input.c
  */
 
-int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
+int tcp_v4_rcv(struct sk_buff *skb)
 {
 	struct tcphdr *th;
 	struct sock *sk;
@@ -1610,31 +1598,35 @@
 	if (skb->pkt_type!=PACKET_HOST)
 		goto discard_it;
 
-	th = skb->h.th;
-
-	/* Pull up the IP header. */
-	__skb_pull(skb, skb->h.raw - skb->data);
-
 	/* Count it even if it's bad */
 	TCP_INC_STATS_BH(TcpInSegs);
 
+	if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
+		goto discard_it;
+
+	th = skb->h.th;
+
+	if (th->doff < sizeof(struct tcphdr)/4)
+		goto bad_packet;
+	if (!pskb_may_pull(skb, th->doff*4))
+		goto discard_it;
+
 	/* An explanation is required here, I think.
 	 * Packet length and doff are validated by header prediction,
 	 * provided case of th->doff==0 is elimineted.
 	 * So, we defer the checks. */
-	if (th->doff < sizeof(struct tcphdr)/4 ||
-	    (skb->ip_summed != CHECKSUM_UNNECESSARY &&
+	if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
 	     tcp_v4_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 = skb->nh.iph->tos;
 	TCP_SKB_CB(skb)->sacked = 0;
-	skb->used = 0;
 
 	sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source,
 			     skb->nh.iph->daddr, ntohs(th->dest), tcp_v4_iif(skb));
@@ -1665,7 +1657,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 {
@@ -1682,7 +1674,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);
 		goto discard_and_relse;
 	}
@@ -1740,7 +1732,7 @@
 		return err;
 
 	__sk_dst_set(sk, &rt->u.dst);
-	/* sk->route_caps = rt->u.dst.dev->features; */
+	sk->route_caps = rt->u.dst.dev->features;
 
 	new_saddr = rt->rt_src;
 
@@ -1788,20 +1780,19 @@
 			      sk->bound_dev_if);
 	if (!err) {
 		__sk_dst_set(sk, &rt->u.dst);
-		/* sk->route_caps = rt->u.dst.dev->features; */
+		sk->route_caps = rt->u.dst.dev->features;
 		return 0;
 	}
 
 	/* Routing failed... */
-	/* sk->route_caps = 0; */
+	sk->route_caps = 0;
 
 	if (!sysctl_ip_dynaddr ||
 	    sk->state != TCP_SYN_SENT ||
 	    (sk->userlocks & SOCK_BINDADDR_LOCK) ||
-	    (err = tcp_v4_reselect_saddr(sk)) != 0) {
+	    (err = tcp_v4_reselect_saddr(sk)) != 0)
 		sk->err_soft=-err;
-		/* sk->error_report(sk); */
-	}
+
 	return err;
 }
 
@@ -1919,7 +1910,8 @@
 
 	sk->state = TCP_CLOSE;
 
-	sk->write_space = tcp_write_space; 
+	sk->write_space = tcp_write_space;
+	sk->use_write_queue = 1;
 
 	sk->tp_pinfo.af_tcp.af_specific = &ipv4_specific;
 
@@ -1940,7 +1932,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 */
@@ -1950,6 +1942,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 0;
@@ -2076,7 +2072,7 @@
 			if (pos >= offset) {
 				get_tcp_sock(sk, tmpbuf, num);
 				len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
-				if (len >= length) {
+				if (pos >= offset + length) {
 					tcp_listen_unlock();
 					goto out_no_bh;
 				}
@@ -2097,7 +2093,7 @@
 							continue;
 						get_openreq(sk, req, tmpbuf, num, uid);
 						len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
-						if(len >= length) {
+						if (pos >= offset + length) {
 							read_unlock_bh(&tp->syn_wait_lock);
 							tcp_listen_unlock();
 							goto out_no_bh;
@@ -2129,7 +2125,7 @@
 				continue;
 			get_tcp_sock(sk, tmpbuf, num);
 			len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
-			if(len >= length) {
+			if (pos >= offset + length) {
 				read_unlock(&head->lock);
 				goto out;
 			}
@@ -2144,7 +2140,7 @@
 				continue;
 			get_timewait_sock(tw, tmpbuf, num);
 			len += sprintf(buffer+len, "%-*s\n", TMPSZ-1, tmpbuf);
-			if(len >= length) {
+			if (pos >= offset + length) {
 				read_unlock(&head->lock);
 				goto out;
 			}
@@ -2159,7 +2155,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)