OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [net/] [ipv4/] [ip_forward.c] - Rev 1765

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 * INET		An implementation of the TCP/IP protocol suite for the LINUX
 *		operating system.  INET is implemented using the  BSD Socket
 *		interface as the means of communication with the user level.
 *
 *		The IP forwarding functionality.
 *		
 * Authors:	see ip.c
 *
 * Fixes:
 *		Many		:	Split from ip.c , see ip_input.c for 
 *					history.
 *		Dave Gregorich	:	NULL ip_rt_put fix for multicast 
 *					routing.
 *		Jos Vos		:	Add call_out_firewall before sending,
 *					use output device for accounting.
 *		Jos Vos		:	Call forward firewall after routing
 *					(always use output device).
 *		Philip Gladstone:	Add some missing ip_rt_put()
 */
 
#include <linux/config.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/firewall.h>
#include <linux/ip_fw.h>
#ifdef CONFIG_IP_MASQUERADE
#include <net/ip_masq.h>
#endif
#include <net/checksum.h>
#include <linux/route.h>
#include <net/route.h>
 
#ifdef CONFIG_IP_FORWARD /* set the default */
int sysctl_ip_forward = 1; 
#else
int sysctl_ip_forward = 0; 
#endif
 
#ifdef CONFIG_IP_MROUTE
 
/*
 * 	Encapsulate a packet by attaching a valid IPIP header to it.
 *	This avoids tunnel drivers and other mess and gives us the speed so
 *	important for multicast video.
 */
 
static void ip_encap(struct sk_buff *skb, int len, struct device *out, __u32 daddr)
{
	/*
	 *	There is space for the IPIP header and MAC left.
	 *
	 *	Firstly push down and install the IPIP header.
	 */
	struct iphdr *iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
 
	if(len>65515)
		len=65515;
 
 
	iph->version	= 	4;
	iph->tos	=	skb->ip_hdr->tos;
	iph->ttl	=	skb->ip_hdr->ttl;
	iph->frag_off	=	0;
	iph->daddr	=	daddr;
	iph->saddr	=	out->pa_addr;
	iph->protocol	=	IPPROTO_IPIP;
	iph->ihl	=	5;
	iph->tot_len	=	htons(skb->len + len);  /* Anand, ernet */
	iph->id		=	htons(ip_id_count++);
	ip_send_check(iph);
 
	skb->dev = out;
	skb->arp = 1;
	skb->raddr=daddr;  /* Router address is not destination address. The
			    * correct value is given eventually. I have not
			    * removed this statement. But could have. 
			    * Anand, ernet.
			    */ 
	/*
	 *	Now add the physical header (driver will push it down).
	 */
 
	/* The last parameter of out->hard_header() needed skb->len + len.
 	 * Anand, ernet.
	 */
	if (out->hard_header && out->hard_header(skb, out, ETH_P_IP, NULL, NULL, 
	skb->len + len)<0)
			skb->arp=0;
	/*
	 *	Read to queue for transmission.
	 */
}
 
#endif
 
/*
 *	Forward an IP datagram to its next destination.
 */
 
int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
	       __u32 target_addr)
{
	struct device *dev2;	/* Output device */
	struct iphdr *iph;	/* Our header */
	struct sk_buff *skb2;	/* Output packet */
	struct rtable *rt = NULL;	/* Route we use */
	unsigned char *ptr;	/* Data pointer */
	unsigned long raddr;	/* Router IP address */
	struct   options * opt	= (struct options*)skb->proto_priv;
	struct hh_cache *hh = NULL;
	int encap = 0;		/* Encap length */
#ifdef CONFIG_FIREWALL
	int fw_res = 0;		/* Forwarding result */	
#ifdef CONFIG_IP_MASQUERADE	
	struct sk_buff *skb_in = skb;	/* So we can remember if the masquerader did some swaps */
#endif /* CONFIG_IP_MASQUERADE */
#endif /* CONFIG_FIREWALL */
 
	/*
	 *	According to the RFC, we must first decrease the TTL field. If
	 *	that reaches zero, we must reply an ICMP control message telling
	 *	that the packet's lifetime expired.
	 *
	 *	Exception:
	 *	We may not generate an ICMP for an ICMP. icmp_send does the
	 *	enforcement of this so we can forget it here. It is however
	 *	sometimes VERY important.
	 */
 
	iph = skb->h.iph;
	if (!(is_frag&IPFWD_NOTTLDEC))
	{
		unsigned long checksum = iph->check;
		iph->ttl--;
 
	/*
	 *	Re-compute the IP header checksum.
	 *	This is efficient. We know what has happened to the header
	 *	and can thus adjust the checksum as Phil Karn does in KA9Q
	 *	except we do this in "network byte order".
	 */
		checksum += htons(0x0100);
		/* carry overflow? */
		checksum += checksum >> 16;
		iph->check = checksum;
	}
 
	if (iph->ttl <= 0)
	{
		/* Tell the sender its packet died... */
		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
		return -1;
	}
 
	/* If IPFWD_MULTITUNNEL flag is set, then we have to perform routing
	 * decision so as to reach the other end of the tunnel. This condition
	 * also means that we are dealing with a unicast IP packet "in a way". 
	 * Anand, ernet.
	 */
 
#ifdef CONFIG_IP_MROUTE
	if(!(is_frag&IPFWD_MULTICASTING) || (is_frag&IPFWD_MULTITUNNEL))
	{
#endif	
		/*
		 * OK, the packet is still valid.  Fetch its destination address,
		 * and give it to the IP sender for further processing.
		 */
 
		rt = ip_rt_route(target_addr, 0, NULL);
 
		if (rt == NULL)
		{
			/*
			 *	Tell the sender its packet cannot be delivered. Again
			 *	ICMP is screened later.
			 */
			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);
			return -1;
		}
 
 
		/*
		 * Gosh.  Not only is the packet valid; we even know how to
		 * forward it onto its final destination.  Can we say this
		 * is being plain lucky?
		 * If the router told us that there is no GW, use the dest.
		 * IP address itself- we seem to be connected directly...
		 */
 
		raddr = rt->rt_gateway;
 
		if (opt->is_strictroute && (rt->rt_flags & RTF_GATEWAY)) {
			/*
			 *	Strict routing permits no gatewaying
			 */
 
			ip_rt_put(rt);
			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
			return -1;
		}
 
		/*
		 *	Having picked a route we can now send the frame out
		 *	after asking the firewall permission to do so.
		 */
 
		dev2 = rt->rt_dev;
		hh = rt->rt_hh;
		/*
		 *	In IP you never have to forward a frame on the interface that it 
		 *	arrived upon. We now generate an ICMP HOST REDIRECT giving the route
		 *	we calculated.
		 */
#ifndef CONFIG_IP_NO_ICMP_REDIRECT
		if (dev == dev2 && 
			!((iph->saddr^dev->pa_addr)&dev->pa_mask) &&
			/* The daddr!=raddr test isn't obvious - what it's doing
			   is avoiding sending a frame the receiver will not 
			   believe anyway.. */
			iph->daddr != raddr/*ANK*/ && !opt->srr)
				icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, raddr, dev);
#endif
#ifdef CONFIG_IP_MROUTE
 
		/* This is for ip encap. Anand, ernet.*/
 
		if (is_frag&IPFWD_MULTITUNNEL)
		{
			encap=20;
		}
	}
	else
	{
		/*
		 *	Multicast route forward. Routing is already done
		 */
		dev2=skb->dev;
		raddr=skb->raddr;
		if(is_frag&IPFWD_MULTITUNNEL)	/* VIFF_TUNNEL mode */
			encap=20;
		rt=NULL;
	}
#endif	
 
	/* 
	 *	See if we are allowed to forward this.
 	 *	Note: demasqueraded fragments are always 'back'warded.
	 */
 
#ifdef CONFIG_FIREWALL
	if(!(is_frag&IPFWD_MASQUERADED))
	{
#ifdef CONFIG_IP_MASQUERADE
		/* 
		 *	Check that any ICMP packets are not for a 
		 *	masqueraded connection.  If so rewrite them
		 *	and skip the firewall checks
		 */
		if (iph->protocol == IPPROTO_ICMP)
		{
#ifdef CONFIG_IP_MASQUERADE_ICMP
#define icmph ((struct icmphdr *)((char *)iph + (iph->ihl<<2)))
                     if ((icmph->type==ICMP_DEST_UNREACH)||
                         (icmph->type==ICMP_SOURCE_QUENCH)||
                         (icmph->type==ICMP_TIME_EXCEEDED))
                       {
#endif
			if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0)
			{
				if (rt)
					ip_rt_put(rt);
				/* Problem - ie bad checksum */
				return -1;
			}
 
			if (fw_res)
				/* ICMP matched - skip firewall */
				goto skip_call_fw_firewall;
#ifdef CONFIG_IP_MASQUERADE_ICMP
                       }
#endif				
		}
#endif
		fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL);
		switch (fw_res) {
		case FW_ACCEPT:
		case FW_MASQUERADE:
			break;
		case FW_REJECT:
			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
			/* fall thru */
		default:
			if (rt)
				ip_rt_put(rt);
			return -1;
		}
 
#ifdef CONFIG_IP_MASQUERADE
		skip_call_fw_firewall:
#endif		
	}
#endif
 
	/*
	 * We now may allocate a new buffer, and copy the datagram into it.
	 * If the indicated interface is up and running, kick it.
	 */
 
	if (dev2->flags & IFF_UP)
	{
#ifdef CONFIG_IP_MASQUERADE
		__u32	premasq_saddr = iph->saddr;
		__u16	premasq_sport = 0;
		__u16	*portptr=NULL;
		long	premasq_len_diff = skb->len;
 
		if (iph->protocol==IPPROTO_UDP ||
                    iph->protocol==IPPROTO_TCP) {
			portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
			premasq_sport = portptr[0];
		}
 
		/*
		 * If this fragment needs masquerading, make it so...
		 * (Don't masquerade de-masqueraded fragments)
		 */
		if (!(is_frag&IPFWD_MASQUERADED) && fw_res==FW_MASQUERADE)
			if (ip_fw_masquerade(&skb, dev2) < 0)
			{
				/*
				 * Masquerading failed; silently discard this packet.
				 */
				if (rt)
					ip_rt_put(rt);
				return -1;
			}
#endif
		IS_SKB(skb);
 
		if (skb->len+encap > dev2->mtu && (iph->frag_off & htons(IP_DF))) 
		{
			ip_statistics.IpFragFails++;
#ifdef CONFIG_IP_MASQUERADE
			/* If we're demasquerading, put the correct daddr back */
			if (is_frag&IPFWD_MASQUERADED)
				iph->daddr = dev->pa_addr;
 
			/* If we're masquerading, put the correct source back */
			else if (fw_res==FW_MASQUERADE) {
				iph->saddr = premasq_saddr;
				if (premasq_sport)
					portptr[0] = premasq_sport;
			}
 
			/* If the packet has got larger and this has caused it to
			   exceed the MTU, then we'll claim that our MTU just got
			   smaller and hope it works */
			premasq_len_diff -= skb->len;
 
			if (premasq_len_diff < 0)
				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
					  htonl(dev2->mtu+premasq_len_diff), dev);
			else
#endif
			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
			if(rt)
				ip_rt_put(rt);
			return -1;
		}
 
#ifdef CONFIG_IP_MROUTE
		if(skb_headroom(skb)-encap<dev2->hard_header_len)
		{
			skb2 = alloc_skb(dev2->hard_header_len + skb->len + encap + 15, GFP_ATOMIC);
#else
		if(skb_headroom(skb)<dev2->hard_header_len)
		{
			skb2 = alloc_skb(dev2->hard_header_len + skb->len + 15, GFP_ATOMIC);
#endif		
			/*
			 *	This is rare and since IP is tolerant of network failures
			 *	quite harmless.
			 */
 
			if (skb2 == NULL)
			{
				NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
				if(rt)
					ip_rt_put(rt);
				return -1;
			}
 
			IS_SKB(skb2);
			/*
			 *	Add the physical headers.
			 */
			skb2->protocol=htons(ETH_P_IP);
#ifdef CONFIG_IP_MROUTE
			if(is_frag&IPFWD_MULTITUNNEL)
			{
				skb_reserve(skb2,(encap+dev2->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
 
/* We need to pass on IP information of the incoming packet to ip_encap() 
 * to fillin ttl, and tos fields.The destination should be target_addr. 
 *  Anand, ernet. 
 */
 
				skb2->ip_hdr = skb->ip_hdr; 
 
				ip_encap(skb2,skb->len, dev2, target_addr);
 
/*  The router address is got earlier that to take us to the remote tunnel
 *  Anand, ernet.
 */
				skb2->raddr = rt->rt_gateway;
			}
			else
#endif			
		 		ip_send(rt,skb2,raddr,skb->len,dev2,dev2->pa_addr);
 
			/*
			 *	We have to copy the bytes over as the new header wouldn't fit
			 *	the old buffer. This should be very rare.
			 */		 
 
			ptr = skb_put(skb2,skb->len);
			skb2->free = 1;
			skb2->h.raw = ptr;
			/*
			 *	Copy the packet data into the new buffer.
			 */
			memcpy(ptr, skb->h.raw, skb->len);
			memcpy(skb2->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
			iph = skb2->ip_hdr = skb2->h.iph;
		}
		else
		{
			/* 
			 *	Build a new MAC header. 
			 */
 
			skb2 = skb;		
			skb2->dev=dev2;
#ifdef CONFIG_IP_MROUTE
			if(is_frag&IPFWD_MULTITUNNEL)
				ip_encap(skb, 0, dev2, target_addr);
			else
			{
#endif
				skb->arp=1;
				skb->raddr=raddr;
				if (hh)
				{
					memcpy(skb_push(skb, dev2->hard_header_len), hh->hh_data, dev2->hard_header_len);
					if (!hh->hh_uptodate)
					{
#if RT_CACHE_DEBUG >= 2
						printk("ip_forward: hh miss %08x via %08x\n", target_addr, rt->rt_gateway);
#endif						
						skb->arp = 0;
					}
				}
				else if (dev2->hard_header)
				{
					if(dev2->hard_header(skb, dev2, ETH_P_IP, NULL, NULL, skb->len)<0)
						skb->arp=0;
				}
#ifdef CONFIG_IP_MROUTE
			}				
#endif			
		}
#ifdef CONFIG_FIREWALL
		if((fw_res = call_out_firewall(PF_INET, skb2->dev, iph, NULL)) < FW_ACCEPT)
		{
			/* FW_ACCEPT and FW_MASQUERADE are treated equal:
			   masquerading is only supported via forward rules */
			if (fw_res == FW_REJECT)
				icmp_send(skb2, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
			if (skb != skb2)
				kfree_skb(skb2,FREE_WRITE);
			if (rt)
				ip_rt_put(rt);
			return -1;
		}
#endif
		ip_statistics.IpForwDatagrams++;
 
		if (opt->optlen) 
		{
			unsigned char * optptr;
			if (opt->rr_needaddr) 
			{
				optptr = (unsigned char *)iph + opt->rr;
				memcpy(&optptr[optptr[2]-5], &dev2->pa_addr, 4);
				opt->is_changed = 1;
			}
			if (opt->srr_is_hit) 
			{
				int srrptr, srrspace;
 
				optptr = (unsigned char *)iph + opt->srr;
 
				for ( srrptr=optptr[2], srrspace = optptr[1];
				      srrptr <= srrspace;
				     srrptr += 4
				    ) 
				{
					if (srrptr + 3 > srrspace)
						break;
					if (memcmp(&target_addr, &optptr[srrptr-1], 4) == 0)
						break;
				}
				if (srrptr + 3 <= srrspace) 
				{
					opt->is_changed = 1;
					memcpy(&optptr[srrptr-1], &dev2->pa_addr, 4);
					iph->daddr = target_addr;
					optptr[2] = srrptr+4;
				}
				else
				        printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
			}
			if (opt->ts_needaddr) 
			{
				optptr = (unsigned char *)iph + opt->ts;
				memcpy(&optptr[optptr[2]-9], &dev2->pa_addr, 4);
				opt->is_changed = 1;
			}
			if (opt->is_changed) 
			{
				opt->is_changed = 0;
				ip_send_check(iph);
			}
		}
/*
 * ANK:  this is point of "no return", we cannot send an ICMP,
 *       because we changed SRR option.
 */
 
		/*
		 *	See if it needs fragmenting. Note in ip_rcv we tagged
		 *	the fragment type. This must be right so that
		 *	the fragmenter does the right thing.
		 */
 
		if(skb2->len > dev2->mtu + dev2->hard_header_len)
		{
			ip_fragment(NULL,skb2,dev2, is_frag);
			kfree_skb(skb2,FREE_WRITE);
		}
		else
		{
#ifdef CONFIG_IP_ACCT		
			/*
			 *	Count mapping we shortcut
			 */
 
			ip_fw_chk(iph,dev2,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
#endif			
 
			/*
			 *	Map service types to priority. We lie about
			 *	throughput being low priority, but it's a good
			 *	choice to help improve general usage.
			 */
			if(iph->tos & IPTOS_LOWDELAY)
				dev_queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
			else if(iph->tos & IPTOS_THROUGHPUT)
				dev_queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
			else
				dev_queue_xmit(skb2, dev2, SOPRI_NORMAL);
		}
	}
	else
	{
	        if(rt)
	        	ip_rt_put(rt);
		return -1;
	}
	if(rt)
		ip_rt_put(rt);
 
	/*
	 *	Tell the caller if their buffer is free.
	 */	 
 
	if(skb==skb2)
		return 0;	
 
#ifdef CONFIG_IP_MASQUERADE	
	/*
	 *	The original is free. Free our copy and
	 *	tell the caller not to free.
	 */
	if(skb!=skb_in)
	{
		kfree_skb(skb_in, FREE_WRITE);
		return 0;
	}
#endif	
	return 1;
}
 
 
 
 
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.