patch-2.1.79 linux/net/ipv6/ip6_output.c

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

diff -u --recursive --new-file v2.1.78/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.5 1997/09/21 18:33:14 kuznet Exp $
+ *	$Id: ip6_output.c,v 1.7 1997/12/29 19:52:46 kuznet Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
@@ -35,32 +35,33 @@
 
 static u32	ipv6_fragmentation_id = 1;
 
-static void ipv6_build_mac_hdr(struct sk_buff *skb, struct dst_entry *dst,
-			       int len)
+int ip6_output(struct sk_buff *skb)
 {
-	struct device *dev;
-	
-	
-	dev = dst->dev;
+	struct dst_entry *dst = skb->dst;
+	struct device *dev = dst->dev;
+	struct hh_cache *hh = dst->hh;
 
-	skb->arp = 1;
-	
-	if (dev->hard_header) {
-		int mac;
+	skb->protocol = __constant_htons(ETH_P_IPV6);
+	skb->dev = dev;
 
-		/* Maybe when Alexey has done his new magic I'll hack this
-		   it seems to be worth 1-2% on IPv4 */
-#if 0
-		if (dst->hh)
-			hh_copy_header(dst->hh, skb);
+	if (hh) {
+#ifdef __alpha__
+		/* Alpha has disguisting memcpy. Help it. */
+	        u64 *aligned_hdr = (u64*)(skb->data - 16);
+		u64 *aligned_hdr0 = hh->hh_data;
+		aligned_hdr[0] = aligned_hdr0[0];
+		aligned_hdr[1] = aligned_hdr0[1];
+#else
+		memcpy(skb->data - 16, hh->hh_data, 16);
 #endif
-		mac = dev->hard_header(skb, dev, ETH_P_IPV6, NULL, NULL, len);
-
-		if (mac < 0)
-			skb->arp = 0;
-	}
-	
-	skb->mac.raw = skb->data;
+	        skb_push(skb, dev->hard_header_len);
+		return hh->hh_output(skb);
+	} else if (dst->neighbour)
+		return dst->neighbour->output(skb);
+
+	printk(KERN_DEBUG "khm\n");
+	kfree_skb(skb, FREE_WRITE);
+	return -EINVAL;
 }
 
 /*
@@ -78,14 +79,15 @@
 
 	hdr = skb->nh.ipv6h;
 
-	if (sk)
+	if (sk) {
 		np = &sk->net_pinfo.af_inet6;
 
-	if (np && np->dst) {
-		/*
-		 *	dst_check returns NULL if route is no longer valid
-		 */
-		dst = dst_check(&dst, np->dst_cookie);
+		if (sk->dst_cache) {
+			/*
+			 *	dst_check returns NULL if route is no longer valid
+			 */
+			dst = dst_check(&sk->dst_cache, np->dst_cookie);
+		}
 	}
 
 	if (dst == NULL) {
@@ -95,24 +97,15 @@
 			/*
 			 *	NETUNREACH usually
 			 */
+			dst_release(dst);
 			return dst->error;
 		}
 	}
 
 	skb->dst = dst_clone(dst);
-	skb->dev = dst->dev;
 	seg_len = skb->tail - ((unsigned char *) hdr);
-	
-	/*
-	 *	Link Layer headers
-	 */
-
-	skb->protocol = __constant_htons(ETH_P_IPV6);
 	hdr = skb->nh.ipv6h;
 
-	ipv6_build_mac_hdr(skb, dst, seg_len);
-
-	
 	/*
 	 *	Fill in the IPv6 header
 	 */
@@ -135,9 +128,10 @@
 	ipv6_statistics.Ip6OutRequests++;
 	dst->output(skb);
 
-	if (sk)
-		ip6_dst_store(sk, dst);
-	else
+	if (sk) {
+		if (sk->dst_cache == NULL)
+			ip6_dst_store(sk, dst);
+	} else
 		dst_release(dst);
 
 	return 0;
@@ -163,8 +157,6 @@
 
 	totlen = len + sizeof(struct ipv6hdr);
 
-	skb->mac.raw = skb->data;
-
 	hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
 	skb->nh.ipv6h = hdr;
 
@@ -211,7 +203,7 @@
 static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag,
 			 const void *data, struct dst_entry *dst,
 			 struct flowi *fl, struct ipv6_options *opt,
-			 int hlimit, int flags, unsigned short length)
+			 int hlimit, int flags, unsigned length)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	struct ipv6hdr *hdr;
@@ -245,8 +237,6 @@
 		payl_len += opt->opt_flen;
 	}
 
-	nfrags = payl_len / ((dst->pmtu - unfrag_len) & ~0x7);
-
 	/*
 	 *	Length of fragmented part on every packet but 
 	 *	the last must be an:
@@ -255,6 +245,8 @@
 
 	frag_len = (dst->pmtu - unfrag_len) & ~0x7;
 
+	nfrags = payl_len / frag_len;
+
 	/*
 	 *	We must send from end to start because of 
 	 *	UDP/ICMP checksums. We do a funny trick:
@@ -281,18 +273,9 @@
 		return err;
 
 	last_skb->dst = dst_clone(dst);
-	last_skb->dev = dst->dev;
-	last_skb->protocol = htons(ETH_P_IPV6);
 	last_skb->when = jiffies;
-	last_skb->arp = 0;
 
-	/* 
-	 * build the mac header... 
-	 */
-	if (dst->dev->hard_header_len) {
-		skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
-		ipv6_build_mac_hdr(last_skb, dst, unfrag_len + frag_len);
-	}
+	skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
 	
 	hdr = (struct ipv6hdr *) skb_put(last_skb, sizeof(struct ipv6hdr));
 	last_skb->nh.ipv6h = hdr;
@@ -335,7 +318,9 @@
 			
 			struct frag_hdr *fhdr2;
 				
+#if 0
 			printk(KERN_DEBUG "sending frag %d\n", nfrags);
+#endif
 			skb = skb_copy(last_skb, sk->allocation);
 
 			if (skb == NULL)
@@ -370,7 +355,9 @@
 		return -EFAULT;
 	}
 
+#if 0
 	printk(KERN_DEBUG "sending last frag \n");
+#endif
 
 	hdr->payload_len = htons(unfrag_len + last_len - 
 				 sizeof(struct ipv6hdr));
@@ -383,18 +370,6 @@
 	last_skb->tail += last_len;
 	last_skb->len += last_len;
 
-	/* 
-	 *	toss the mac header out and rebuild it.
-	 *	needed because of the different frame length.
-	 *	ie: not needed for an ethernet.
-	 */
-
-	if (dst->dev->type != ARPHRD_ETHER && last_len != frag_len) {
-		skb_pull(last_skb, (unsigned char *)last_skb->nh.ipv6h - 
-			 last_skb->data);
-		ipv6_build_mac_hdr(last_skb, dst, unfrag_len + last_len);
-	}
-
 	ipv6_statistics.Ip6OutRequests++;
 	dst->output(last_skb);
 
@@ -402,7 +377,7 @@
 }
 
 int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
-		   struct flowi *fl, unsigned short length,
+		   struct flowi *fl, unsigned length,
 		   struct ipv6_options *opt, int hlimit, int flags)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
@@ -419,8 +394,8 @@
 
 	dst = NULL;
 	
-	if (np->dst)
-		dst = dst_check(&np->dst, np->dst_cookie);
+	if (sk->dst_cache)
+		dst = dst_check(&sk->dst_cache, np->dst_cookie);
 
 	if (dst == NULL)
 		dst = ip6_route_output(sk, fl);
@@ -456,6 +431,16 @@
 		pktlength += sizeof(struct ipv6hdr);
 		if (opt)
 			pktlength += opt->opt_flen + opt->opt_nflen;
+
+		/* Due to conservative check made by caller,
+		   pktlength cannot overflow here.
+
+		   When (and if) jumbo option will be implemented
+		   we could try soemething sort of:
+
+		   if (pktlength < length) return -EMSGSIZE;
+
+		*/
 	}
 
 	if (pktlength <= dst->pmtu) {
@@ -475,15 +460,9 @@
 		dev = dst->dev;
 		skb->dst = dst_clone(dst);
 
-		skb->dev = dev;
-		skb->protocol = htons(ETH_P_IPV6);
 		skb->when = jiffies;
-		skb->arp = 0;
 
-		if (dev && dev->hard_header_len) {
-			skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-			ipv6_build_mac_hdr(skb, dst, pktlength);
-		}
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
 
 		hdr = (struct ipv6hdr *) skb->tail;
 		skb->nh.ipv6h = hdr;
@@ -516,9 +495,18 @@
 	} else {
 		if (sk->ip_hdrincl)
 			return -EMSGSIZE;
-		
+
+		/* pktlength includes IPv6 header, not included
+		   in IPv6 payload length.
+		   FIXME are non-fragmentable options included
+		   in packet after defragmentation? If not, we
+		   should subtract opt_nflen also. --ANK
+		 */
+		if (pktlength > 0xFFFF + sizeof(struct ipv6hdr))
+			return -EMSGSIZE;
+
 		err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, hlimit,
-				    flags, pktlength);
+				    flags, length);
 	}
 	
 	/*
@@ -526,7 +514,7 @@
 	 */
   out:
 	
-	if (np->dst)
+	if (sk->dst_cache)
 		ip6_dst_store(sk, dst);
 	else
 		dst_release(dst);
@@ -569,7 +557,7 @@
 	if (skb->dev == dst->dev && dst->neighbour) {
 		struct in6_addr *target = NULL;
 		struct rt6_info *rt;
-		struct nd_neigh *ndn = (struct nd_neigh *) dst->neighbour;
+		struct neighbour *n = dst->neighbour;
 
 		/*
 		 *	incoming and outgoing devices are the same
@@ -578,7 +566,7 @@
 
 		rt = (struct rt6_info *) dst;
 		if ((rt->rt6i_flags & RTF_GATEWAY))
-			target = &ndn->ndn_addr;
+			target = (struct in6_addr*)&n->primary_key;
 		else
 			target = &hdr->daddr;
 
@@ -593,41 +581,12 @@
 		return -EMSGSIZE;
 	}
 
-	skb->dev = dst->dev;
-
-	/*
-	 *	Rebuild the mac header
-	 */
-	if (skb_headroom(skb) < dst->dev->hard_header_len) {
-		struct sk_buff *buff;
-
-		buff = alloc_skb(dst->dev->hard_header_len + skb->len + 15,
-				 GFP_ATOMIC);
-
-		if (buff == NULL) {
-			kfree_skb(skb, FREE_WRITE);
-			return -ENOMEM;
-		}
-		
-		skb_reserve(buff, (dst->dev->hard_header_len + 15) & ~15);
-
-		buff->protocol = __constant_htons(ETH_P_IPV6);
-		buff->h.raw = skb_put(buff, size);
-		buff->dst = dst_clone(dst);
-		buff->dev = dst->dev;
-
-		memcpy(buff->h.raw, hdr, size);
-		buff->nh.ipv6h = (struct ipv6hdr *) buff->h.raw;
-		kfree_skb(skb, FREE_READ);
-		skb = buff;
-	} else {
-		skb_pull(skb, skb->nh.raw - skb->data);
+	if (skb_headroom(skb) < dst->dev->hard_header_len || skb_cloned(skb)) {
+		struct sk_buff *skb2;
+		skb2 = skb_realloc_headroom(skb, (dst->dev->hard_header_len + 15)&~15);
+		kfree_skb(skb, FREE_WRITE);
+		skb = skb2;
 	}
-
-	ipv6_build_mac_hdr(skb, dst, size);
-
-	if (dst->neighbour)
-		ndisc_event_send(dst->neighbour, skb);
 
 	ipv6_statistics.Ip6ForwDatagrams++;
 	dst->output(skb);

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