patch-1.3.36 linux/net/ipv4/ip_forward.c

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

diff -u --recursive --new-file v1.3.35/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
@@ -0,0 +1,444 @@
+/*
+ * 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.
+ */
+
+#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 <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>
+#include <net/checksum.h>
+#include <linux/route.h>
+#include <net/route.h>
+ 
+#ifdef CONFIG_IP_FORWARD
+#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);
+	iph->id		=	htons(ip_id_count++);
+	ip_send_check(iph);
+
+	skb->dev = out;
+	skb->arp = 1;
+	skb->raddr=daddr;
+	/*
+	 *	Now add the physical header (driver will push it down).
+	 */
+	if (out->hard_header && out->hard_header(skb, out, ETH_P_IP, NULL, NULL, 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;	/* Route we use */
+	unsigned char *ptr;	/* Data pointer */
+	unsigned long raddr;	/* Router IP address */
+	struct   options * opt	= (struct options*)skb->proto_priv;
+	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
+	
+	/* 
+	 *	See if we are allowed to forward this.
+ 	 *	Note: demasqueraded fragments are always 'back'warded.
+	 */
+
+	
+	if(!(is_frag&4))
+	{
+		fw_res=call_fw_firewall(PF_INET, skb, skb->h.iph);
+		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:
+			return -1;
+		}
+	}
+#endif
+	/*
+	 *	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;
+	iph->ttl--;
+
+	/*
+	 *	Re-compute the IP header checksum.
+	 *	This is inefficient. We know what has happened to the header
+	 *	and could thus adjust the checksum as Phil Karn does in KA9Q
+	 */
+
+	iph->check = ntohs(iph->check) + 0x0100;
+	if ((iph->check & 0xFF00) == 0)
+		iph->check++;		/* carry overflow */
+	iph->check = htons(iph->check);
+
+	if (iph->ttl <= 0)
+	{
+		/* Tell the sender its packet died... */
+		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
+		return -1;
+	}
+
+#ifdef CONFIG_IP_MROUTE
+	if(!(is_frag&8))
+	{
+#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, NULL, 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 (raddr != 0)
+		{
+			/*
+			 *	Strict routing permits no gatewaying
+			 */
+	
+		        if (opt->is_strictroute)
+			{
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
+				return -1;
+			}
+		
+			/*
+			 *	There is a gateway so find the correct route for it.
+			 *	Gateways cannot in turn be gatewayed.
+			 */
+		}
+		else
+			raddr = target_addr;
+
+		/*
+		 *	Having picked a route we can now send the frame out.
+		 */
+
+		dev2 = rt->rt_dev;
+		/*
+		 *	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^iph->daddr)&dev->pa_mask) &&
+		    (rt->rt_flags&RTF_MODIFIED) && !opt->srr)
+			icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, raddr, dev);
+#endif
+#ifdef CONFIG_IP_MROUTE
+	}
+	else
+	{
+		/*
+		 *	Multicast route forward. Routing is already done
+		 */
+		dev2=skb->dev;
+		raddr=skb->raddr;
+		if(is_frag&16)		/* VIFF_TUNNEL mode */
+			encap=20;
+	}
+#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
+		/*
+		 * If this fragment needs masquerading, make it so...
+		 * (Dont masquerade de-masqueraded fragments)
+		 */
+		if (!(is_frag&4) && fw_res==FW_MASQUERADE)
+			ip_fw_masquerade(&skb, dev2);
+#endif
+		IS_SKB(skb);
+
+		if (skb->len+encap > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
+		  ip_statistics.IpFragFails++;
+		  icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev2->mtu, dev);
+		  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"));
+				return -1;
+			}
+		
+			IS_SKB(skb2);
+			/*
+			 *	Add the physical headers.
+			 */
+#ifdef CONFIG_IP_MROUTE
+			if(is_frag&16)
+			{
+				skb_reserve(skb,(encap+dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
+				ip_encap(skb2,skb->len, dev2, raddr);
+			}
+			else
+#endif			
+		 		ip_send(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&16)
+				ip_encap(skb,skb->len, dev2, raddr);
+			else
+			{
+#endif
+				skb->arp=1;
+				skb->raddr=raddr;
+				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			
+			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("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,dev,ip_acct_chain,IP_FW_F_ACCEPT,1);
+#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
+		return -1;
+	
+	/*
+	 *	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;
+}
+
+
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this