patch-2.1.30 linux/net/ipv6/ipv6_output.c

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

diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ipv6_output.c linux/net/ipv6/ipv6_output.c
@@ -1,969 +0,0 @@
-/*
- *	IPv6 output functions
- *	Linux INET6 implementation 
- *
- *	Authors:
- *	Pedro Roque		<roque@di.fc.ul.pt>	
- *
- *	Based on linux/net/ipv4/ip_output.c
- *
- *	$Id: ipv6_output.c,v 1.19 1996/10/16 18:34:16 roque Exp $
- *
- *	This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
- */
-
-/*
- *	Changes:
- *
- *	Andi Kleen		:	exception handling
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-static u32	ipv6_fragmentation_id = 1;
-int		ipv6_forwarding = 0;		/* default: host */
-
-static int __inline__ ipv6_build_mac_header(struct sk_buff *skb,
-					    struct device *dev,
-					    struct neighbour *neigh, 
-					    int len)
-{
-	int mac;
-	int hdrlen = 0;
-
-	skb->arp = 1;
-	skb->nexthop = neigh;
-
-	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-
-	if (dev->hard_header_len)
-	{
-
-		/*
-		 *	FIXME: use cached hardware header if availiable
-		 */
-		if (dev->hard_header)
-		{
-			mac = dev->hard_header(skb, dev, ETH_P_IPV6, 
-					       NULL, NULL, len);
-		
-			if (mac < 0)
-			{				
-				hdrlen = -mac;
-				skb->arp = 0;
-			}
-			else
-			{				
-				hdrlen = mac;
-			}
-		}
-		else
-			hdrlen = dev->hard_header_len;
-	}
-
-	skb->mac.raw = skb->data;
-	return hdrlen;
-}
-
-void ipv6_redo_mac_hdr(struct sk_buff *skb, struct neighbour *neigh, int len)
-{
-	struct device *dev = neigh->dev;
-	int mac;
-	
-	skb->dev = dev;
-	skb->nexthop = neigh;
-	skb->arp = 1;
-
-	skb_pull(skb, (unsigned char *) skb->nh.ipv6h - skb->data);
-
-	/*
-	 *	neighbour cache should have the ether address
-	 *	cached... use it
-	 */ 
-
-	if (dev->hard_header)
-	{
-		/*
-		 *	FIXME: use cached hardware header if availiable
-		 */
-
-		mac = dev->hard_header(skb, dev, ETH_P_IPV6, 
-				       NULL, NULL, len);
-		
-		if (mac < 0)
-		{				
-			skb->arp = 0;
-		}
-
-	}
-	skb->mac.raw = skb->data;
-}
-
-void default_output_method(struct sk_buff *skb, struct rt6_info *rt)
-{
-	struct sock *sk = skb->sk;
-	struct device *dev = skb->dev;
-
-	if (dev->flags & IFF_UP)
-	{
-		/*
-		 *	If we have an owner use its priority setting,
-		 *	otherwise use NORMAL
-		 */
-
-		dev_queue_xmit(skb);
-	}
-	else
-	{
-		if(sk)
-			sk->err = ENETDOWN;
-
-		ipv6_statistics.Ip6OutDiscards++;
-		
-		kfree_skb(skb, FREE_WRITE);
-	}
-}
-
-/*
- *	xmit an sk_buff (used by TCP)
- *	sk can be NULL (for sending RESETs)
- */
-int ipv6_xmit(struct sock *sk, struct sk_buff *skb, struct in6_addr *saddr,
-	      struct in6_addr *daddr, struct ipv6_options *opt, int proto)
-{
-	struct ipv6hdr *hdr;
-	struct dest_entry *dc;
-	struct ipv6_pinfo *np = NULL;
-	struct device *dev = skb->dev;
-	int seg_len;
-	int addr_type;
-	int rt_flags = 0;
-
-
-	addr_type = ipv6_addr_type(daddr);
-
-	if (addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_SITELOCAL))
-	{
-		/*
-		 *	force device match on route lookup
-		 */
-		
-		rt_flags |= RTI_DEVRT;
-	}
-
-	if (sk && sk->localroute)
-		rt_flags |= RTI_GATEWAY;
-
-	hdr = skb->nh.ipv6h;
-	
-
-	if (sk)
-	{
-		np = &sk->net_pinfo.af_inet6;
-	}
-	
-	if (np && np->dest)
-	{
-		dc = ipv6_dst_check(np->dest, daddr, np->dc_sernum, rt_flags);
-	}
-	else
-	{
-		dc = ipv6_dst_route(daddr, dev, rt_flags);
-	}
-
-	if (dc == NULL)
-	{
-		ipv6_statistics.Ip6OutNoRoutes++;
-		return(-ENETUNREACH);
-	}
-
-	dev = dc->rt.rt_dev;
-	
-	if (saddr == NULL)
-	{
-		struct inet6_ifaddr *ifa;
-		
-		ifa = ipv6_get_saddr((struct rt6_info *) dc, daddr);
-		
-		if (ifa == NULL)
-		{
-			printk(KERN_DEBUG 
-			       "ipv6_xmit: get_saddr failed\n");
-			return -ENETUNREACH;
-		}
-		
-		saddr = &ifa->addr;
-		
-		if (np)
-		{
-			ipv6_addr_copy(&np->saddr, saddr);
-		}
-	}
-
-	seg_len = skb->tail - ((unsigned char *) hdr);
-
-	/*
-	 *	Link Layer headers
-	 */
-
-	skb->protocol = __constant_htons(ETH_P_IPV6);
-	skb->dev = dev;
-	
-	ipv6_redo_mac_hdr(skb, dc->dc_nexthop, seg_len);
-	
-	/*
-	 *	Fill in the IPv6 header
-	 */
-
-	hdr->version = 6;
-	hdr->priority = np ? np->priority : 0;
-
-	if (np)
-		memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3);
-	else
-		memset(hdr->flow_lbl, 0, 3);
-
-	hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
-	hdr->nexthdr = proto;
-	hdr->hop_limit = np ? np->hop_limit : ipv6_hop_limit;
-	
-	memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
-	memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
-	
-	/*
-	 *	Options
-	 */
-
-
-	/*
-	 *	Output the packet
-	 */
-
-	ipv6_statistics.Ip6OutRequests++;
-
-	if (dc->rt.rt_output_method)
-	{
-		(*dc->rt.rt_output_method)(skb, (struct rt6_info *) dc);
-	}
-	else
-		default_output_method(skb, (struct rt6_info *) dc);
-
-	/*
-	 *	Update serial number of cached dest_entry or
-	 *	release destination cache entry
-	 */
-	
-	if (np)
-	{
-		np->dest = dc;
-		if (dc->rt.fib_node)
-		{
-			np->dc_sernum = dc->rt.fib_node->fn_sernum;
-		}
-	}
-	else
-	{
-		ipv6_dst_unlock(dc);
-	}
-
-	return 0;
-}
-
-/*
- *	To avoid extra problems ND packets are send through this
- *	routine. It's code duplication but i really want to avoid
- *	extra checks since ipv6_build_header is used by TCP (which
- *	is for us performace critical)
- */
-
-int ipv6_bld_hdr_2(struct sock *sk, struct sk_buff *skb, struct device *dev,
-		   struct neighbour *neigh,
-		   struct in6_addr *saddr, struct in6_addr *daddr,
-		   int proto, int len)
-{
-	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	struct ipv6hdr *hdr;
-	int hdrlen = 0;
-
-	skb->dev = dev;
-
-	/* build MAC header */
-	hdrlen += ipv6_build_mac_header(skb, dev, neigh, len);
-
-	/* build fixed IPv6 header */
-
-	if (proto == IPPROTO_RAW)
-		return hdrlen;
-
-	
-	hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
-	skb->nh.ipv6h = hdr;
-	
-	hdr->version  = 6;
-	hdr->priority = np->priority & 0x0f;
-
-	memset(hdr->flow_lbl, 0, 3);
-
-	hdr->hop_limit =  np->hop_limit;
-
-	if (saddr == NULL)
-	{
-		printk(KERN_DEBUG "bug: bld_hdr called with no saddr\n");
-		return -ENETUNREACH;
-	}
-
-	memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
-	memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-	
-	hdrlen += sizeof(struct ipv6hdr);
-
-	hdr->nexthdr = proto;
-
-	return hdrlen;
-}
-
-void ipv6_queue_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb,
-		     int free)
-{
-	struct ipv6hdr *hdr;
-	u32 seg_len;
-
-	hdr = skb->nh.ipv6h;
-	skb->protocol = __constant_htons(ETH_P_IPV6);
-
-	seg_len = skb->tail - ((unsigned char *) hdr);
-
-	hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
-
-	if (dev == NULL)
-	{
-		printk(KERN_DEBUG "ipv6_queue_xmit: unknown device\n");
-		return;
-	}
-
-	skb->dev = dev;
-	
-	ipv6_statistics.Ip6OutRequests++;
-
-
-	/*
-	 *	Multicast loopback
-	 */
-
-	if (dev->flags & IFF_UP)
-	{
-		/*
-		 *	If we have an owner use its priority setting,
-		 *	otherwise use NORMAL
-		 */
-
-		dev_queue_xmit(skb);
-	}
-	else
-	{
-		if(sk)
-			sk->err = ENETDOWN;
-
-		ipv6_statistics.Ip6OutDiscards++;
-
-		kfree_skb(skb, FREE_WRITE);
-	}
-
-}
-
-
-int ipv6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
-		    struct in6_addr *dest, unsigned short int length,
-		    struct in6_addr *saddr, struct device *dev,
-		    struct ipv6_options *opt, int proto, int hlimit,
-		    int noblock)
-{
-	rt6_output_method_t output_method = default_output_method;
-	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	struct dest_entry *dc = NULL;
-	struct in6_addr *daddr = dest;
-	struct ipv6hdr *hdr;
-	struct neighbour *neigh;	
-	int	addr_type;
-	int	pktlength;
-	int	pmtu = 0;
-	int	rt_flags = 0;
-	int	error; 
-	
-	if (opt && opt->srcrt)
-	{
-		struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
-		daddr = rt0->addr;
-	}
-
-	addr_type = ipv6_addr_type(daddr);
-	
-	if (hlimit < 1)
-	{
-		if (addr_type & IPV6_ADDR_MULTICAST)
-		{
-			hlimit = np->mcast_hops;
-			if (dev == NULL)
-			{
-				dev = np->mc_if;
-			}
-		}
-		else
-			hlimit = np->hop_limit;
-	}
-		
-	if (addr_type & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_SITELOCAL |
-			 IPV6_ADDR_MULTICAST))
-	{
-		/*
-		 *	force device match on route lookup
-		 */
-		
-		rt_flags |= RTI_DEVRT;
-	}
-
-	if (sk->localroute)
-	{
-		rt_flags |= RTI_GATEWAY;
-	}
-
-	if (np->dest)
-	{
-		np->dest = ipv6_dst_check(np->dest, daddr, np->dc_sernum,
-					  rt_flags);
-		
-		dc = np->dest;
-
-		if (dc && dc->rt.fib_node)
-		{
-			np->dc_sernum = dc->rt.fib_node->fn_sernum;
-		}
-		else
-		{
-			printk(KERN_WARNING "dc entry not in table\n");
-		}
-	}
-	else
-	{
-		dc = ipv6_dst_route(daddr, dev, rt_flags);
-	}
-
-	if (dc == NULL)
-	{
-		if ((addr_type & IPV6_ADDR_MULTICAST) && dev)
-		{
-			neigh = NULL;
-			pmtu = dev->mtu;
-		}
-		else
-		{
-			ipv6_statistics.Ip6OutNoRoutes++;
-			return(-ENETUNREACH);
-		}
-	}
-	else
-	{
-		neigh = dc->dc_nexthop;
-		dev = neigh->dev;
-
-		if (dc->rt.rt_output_method)
-		{
-			output_method = dc->rt.rt_output_method;
-		}
-
-		if (dc->dc_flags & DCF_PMTU)
-			pmtu = dc->dc_pmtu;
-		else
-			pmtu = dev->mtu;
-	}
-
-
-	if (saddr == NULL)
-	{
-		struct inet6_ifaddr *ifa;
-
-		ifa = ipv6_get_saddr((struct rt6_info *) dc, daddr);
-
-		if (ifa == NULL)
-		{
-			printk(KERN_DEBUG 
-			       "ipv6_build_xmit: get_saddr failed\n");
-			return -ENETUNREACH;
-		}
-
-		saddr = &ifa->addr;
-	}
-
-	if (dc && np->dest == NULL)
-	{
-		ipv6_dst_unlock(dc);
-	}
-
-	pktlength = length;
-
-	if (!sk->ip_hdrincl)
-	{ 
-		pktlength += sizeof(struct ipv6hdr);
-		if (opt)
-		{
-			pktlength += opt->opt_flen + opt->opt_nflen;
-		}
-	}
-		
-
-	dev_lock_list();
-
-	/*
-	 *	reminder: don't allow fragmentation for IPPROTO_RAW
-	 */
-
-
-	if (pktlength <= pmtu) 
-	{
-		struct sk_buff *skb =
-			sock_alloc_send_skb(sk, pktlength+15+
-					    dev->hard_header_len,
-					    0, noblock, &error);
-		
-		if (skb == NULL)
-		{
-			ipv6_statistics.Ip6OutDiscards++;
-			dev_unlock_list();
-			return error;
-
-		}
-
-		skb->dev=dev;
-		skb->protocol = htons(ETH_P_IPV6);
-		skb->when=jiffies;
-		skb->arp=0;
-
-		/* build the mac header... */
-		ipv6_build_mac_header(skb, dev, neigh, pktlength);
-
-		hdr = (struct ipv6hdr *) skb->tail;
-		skb->nh.ipv6h = hdr;
-
-		if (!sk->ip_hdrincl)
-		{
-			skb_put(skb, sizeof(struct ipv6hdr));
-
-			hdr->version = 6;
-			hdr->priority = np->priority;
-
-			memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
-
-			hdr->payload_len = htons(pktlength - 
-						 sizeof(struct ipv6hdr));
-			
-			hdr->hop_limit = hlimit;
-
-			memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
-			memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
-			if (opt && opt->srcrt)
-			{
-				hdr->nexthdr = ipv6opt_bld_rthdr(skb, opt,
-								 dest, proto);
-								 
-			}
-			else
-				hdr->nexthdr = proto;
-		}
-			
-		skb_put(skb, length);
-		error = getfrag(data, &hdr->saddr,
-				((char *) hdr) + (pktlength - length),
-				0, length);
-			
-		if (!error) 
-		{
-			ipv6_statistics.Ip6OutRequests++;
-			(*output_method)(skb, (struct rt6_info *) dc);
-		} else
-		{
-			error = -EFAULT;
-			kfree_skb(skb, FREE_WRITE);			
-		}
-
-		dev_unlock_list();
-		return error;
-	}
-	else
-	{
-		/*
-		 *	Fragmentation
-		 */
-
-		/*
-		 *	Extension header order:
-		 *	Hop-by-hop -> Routing -> Fragment -> rest (...)
-		 *	
-		 *	We must build the non-fragmented part that
-		 *	will be in every packet... this also means
-		 *	that other extension headers (Dest, Auth, etc)
-		 *	must be considered in the data to be fragmented
-		 */
-
-		struct sk_buff  *last_skb;
-		struct frag_hdr *fhdr;
-		int unfrag_len;
-		int payl_len;
-		int frag_len;
-		int last_len;
-		int nfrags;
-		int err;
-		int fhdr_dist;
-		__u32 id;
-
-		if (sk->ip_hdrincl)
-		{
-			return -EMSGSIZE;
-		}
-
-		id = ipv6_fragmentation_id++;
-
-		unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
-		payl_len = length;
-
-		if (opt)
-		{
-			unfrag_len += opt->opt_nflen;
-			payl_len += opt->opt_flen;
-		}
-
-		nfrags = payl_len / ((pmtu - unfrag_len) & ~0x7);
-
-		/* 
-		 * Length of fragmented part on every packet but 
-		 * the last must be an:
-		 * "integer multiple of 8 octects".
-		 */
-
-		frag_len = (pmtu - unfrag_len) & ~0x7;
-
-		/*
-		 *	We must send from end to start because of 
-		 *	UDP/ICMP checksums. We do a funny trick:
-		 *	fill the last skb first with the fixed
-		 *	header (and its data) and then use it
-		 *	to create the following segments and send it
-		 *	in the end. If the peer is checking the M_flag
-		 *	to trigger the reassembly code then this 
-		 *	might be a good idea.
-		 */
-
-		last_len = payl_len - (nfrags * frag_len);
-
-		if (last_len == 0)
-		{
-			last_len = frag_len;
-			nfrags--;
-		}
-		
-		last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
-					       dev->hard_header_len + 15,
-					       0, noblock, &err);
-
-		if (last_skb == NULL)
-		{
-			dev_unlock_list();
-			return err;
-		}
-
-		last_skb->dev=dev;
-		last_skb->protocol = htons(ETH_P_IPV6);
-		last_skb->when=jiffies;
-		last_skb->arp=0;
-
-		/* 
-		 * build the mac header... 
-		 */
-		ipv6_build_mac_header(last_skb, dev, neigh,
-				      unfrag_len + frag_len);
-
-		hdr = (struct ipv6hdr *) skb_put(last_skb, 
-						 sizeof(struct ipv6hdr));
-		last_skb->nh.ipv6h = hdr;
-
-		hdr->version = 6;
-		hdr->priority = np->priority;
-
-		memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
-		hdr->payload_len = htons(unfrag_len + frag_len - 
-					 sizeof(struct ipv6hdr));
-		
-		hdr->hop_limit = hlimit;
-
-		hdr->nexthdr = NEXTHDR_FRAGMENT;
-
-		memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
-		memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-		
-		if (opt && opt->srcrt)
-		{
-			hdr->nexthdr = ipv6opt_bld_rthdr(last_skb, opt, dest,
-							 NEXTHDR_FRAGMENT);
-		}
-	
-		fhdr = (struct frag_hdr *)
-			skb_put(last_skb, sizeof(struct frag_hdr));
-
-		memset(fhdr, 0, sizeof(struct frag_hdr));
-
-		fhdr->nexthdr  = proto;		
-		fhdr->frag_off = ntohs(nfrags * frag_len);
-		fhdr->identification = id;
-
-		fhdr_dist = (unsigned char *) fhdr - last_skb->data;
-
-		error = getfrag(data, &hdr->saddr, last_skb->tail,
-				nfrags * frag_len, last_len);
-		
-		if (!error)
-		{	
-			while (nfrags--)
-			{
-				struct sk_buff *skb;
-			
-				struct frag_hdr *fhdr2;
-				
-				printk(KERN_DEBUG "sending frag %d\n", nfrags);
-				skb = skb_copy(last_skb, sk->allocation);
-				
-				fhdr2 = (struct frag_hdr *)
-					(skb->data + fhdr_dist);
-				
-				/* more flag on */
-				fhdr2->frag_off = ntohs(nfrags * frag_len + 1);
-				
-				/*
-				 *	FIXME:
-				 *	if (nfrags == 0)
-				 *	put rest of headers
-				 */
-				
-				error = getfrag(data, &hdr->saddr,
-						skb_put(skb, frag_len), 
-						nfrags * frag_len, frag_len);
-			
-				if (error)
-				{
-					kfree_skb(skb, FREE_WRITE);
-					break;
-				}
-
-				ipv6_statistics.Ip6OutRequests++;
-				(*output_method)(skb, (struct rt6_info *) dc);
-			}
-		}
-
-		if (error)
-		{
-			kfree_skb(last_skb, FREE_WRITE);
-			dev_unlock_list();
-			return -EFAULT;
-		}
-
-		printk(KERN_DEBUG "sending last frag \n");
-
-		hdr->payload_len = htons(unfrag_len + last_len - 
-					 sizeof(struct ipv6hdr));
-
-		/*
-		 *	update last_skb to reflect the getfrag we did
-		 *	on start.
-		 */
-		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 (dev->type != ARPHRD_ETHER && last_len != frag_len)
-		{
-			ipv6_redo_mac_hdr(last_skb, neigh,
-					  unfrag_len + last_len);
-		}
-
-		ipv6_statistics.Ip6OutRequests++;
-		(*output_method)(last_skb, (struct rt6_info *) dc);
-
-		dev_unlock_list();
-		return 0;
-	}
-	return -1;
-}
-
-static int pri_values[4] =
-{
-	SOPRI_BACKGROUND,
-	SOPRI_NORMAL,
-	SOPRI_NORMAL,
-	SOPRI_INTERACTIVE
-};
-
-void ipv6_forward(struct sk_buff *skb, struct device *dev, int flags)
-{
-	struct neighbour *neigh;
-	struct dest_entry *dest;
-	int priority;
-	int rt_flags;
-	int size;
-	int pmtu;
-
-	if (skb->nh.ipv6h->hop_limit <= 1)
-	{
-		icmpv6_send(skb, ICMPV6_TIME_EXCEEDED, ICMPV6_EXC_HOPLIMIT,
-			    0, dev);
-
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-
-	skb->nh.ipv6h->hop_limit--;
-
-	if (ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)
-	{
-		printk(KERN_DEBUG "ipv6_forward: link local source addr\n");
-		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR,
-			    0, dev);		
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-
-	rt_flags = RTF_MODIFIED;
-
-	if ((flags & IP6_FW_STRICT))
-	{
-		rt_flags |= RTF_GATEWAY;
-	}
-
-	dest = ipv6_dst_route(&skb->nh.ipv6h->daddr, NULL, rt_flags);
-
-	if (dest == NULL)
-	{
-		int code;
-
-		if (flags & IP6_FW_STRICT)
-			code = ICMPV6_NOT_NEIGHBOUR;
-		else
-			code = ICMPV6_NOROUTE;
-			
-		icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, dev);
-			    
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-
-	neigh = dest->dc_nexthop;
-
-	if (neigh->dev == dev && (dev->flags & IFF_MULTICAST) &&
-	    !(flags & IP6_FW_SRCRT))
-	{
-		struct in6_addr *target = NULL;
-		struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-		
-		/* 
-		 *	outgoing device equal to incoming device
-		 *	send a redirect
-		 */
-		
-		if ((dest->dc_flags & RTF_GATEWAY))
-		{
-			target = &ndn->ndn_addr;
-		}
-		else
-		{
-			target = &skb->nh.ipv6h->daddr;
-		}
-
-		ndisc_send_redirect(skb, neigh, target);
-	}
-
-	pmtu = neigh->dev->mtu;
-
-	size = sizeof(struct ipv6hdr) + ntohs(skb->nh.ipv6h->payload_len);
-	
-	if (size > pmtu)
-	{
-		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, pmtu, dev);
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-
-	ipv6_dst_unlock(dest);
-
-	if (skb_headroom(skb) < neigh->dev->hard_header_len)
-	{
-		struct sk_buff *buff;
-
-		buff = alloc_skb(neigh->dev->hard_header_len + skb->len + 15,
-				 GFP_ATOMIC);
-
-		if (buff == NULL)
-		{
-			return;
-		}
-		
-		skb_reserve(buff, (neigh->dev->hard_header_len + 15) & ~15);
-
-		buff->protocol = __constant_htons(ETH_P_IPV6);
-		buff->h.raw = skb_put(buff, size);
-
-		memcpy(buff->h.raw, skb->nh.ipv6h, size);
-		buff->nh.ipv6h = (struct ipv6hdr *) buff->h.raw;
-		kfree_skb(skb, FREE_READ);
-		skb = buff;
-	}
-
-	ipv6_redo_mac_hdr(skb, neigh, size);
-
-	priority = skb->nh.ipv6h->priority;
-
-	priority = (priority & 0x7) >> 1;
-	priority = pri_values[priority];
-
-	if (dev->flags & IFF_UP)
-	{
-		skb->dev = neigh->dev;
-		dev_queue_xmit(skb);
-	}
-	else
-	{
-		ipv6_statistics.Ip6OutDiscards++;
-		kfree_skb(skb, FREE_READ);
-	}
-}
-
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */

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