patch-2.1.15 linux/net/ipv4/ip_forward.c
Next file: linux/net/ipv4/ip_fragment.c
Previous file: linux/net/ipv4/ip_alias.c
Back to the patch index
Back to the overall index
- Lines: 658
- Date:
Thu Dec 12 16:54:23 1996
- Orig file:
v2.1.14/linux/net/ipv4/ip_forward.c
- Orig date:
Tue Nov 19 15:54:00 1996
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
@@ -16,7 +16,6 @@
* use output device for accounting.
* Jos Vos : Call forward firewall after routing
* (always use output device).
- * Alan Cox : Unshare buffer on forward.
*/
#include <linux/config.h>
@@ -41,534 +40,199 @@
#include <linux/route.h>
#include <net/route.h>
-#ifdef CONFIG_IP_FORWARD
-#ifdef CONFIG_IP_MROUTE
-
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
/*
- * 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.
+ * Check the packet against our socket administration to see
+ * if it is related to a connection on our system.
+ * Needed for transparent proxying.
*/
-
-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.
- */
+int ip_chksock(struct sk_buff *skb)
+{
+ switch (skb->nh.iph->protocol) {
+ case IPPROTO_ICMP:
+ return icmp_chkaddr(skb);
+ case IPPROTO_TCP:
+ return tcp_chkaddr(skb);
+ case IPPROTO_UDP:
+ return udp_chkaddr(skb);
+ default:
+ return 0;
+ }
}
-
#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)
+int ip_forward(struct sk_buff *skb)
{
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;
- 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 */
+ struct ip_options * opt = &(IPCB(skb)->opt);
+#if defined(CONFIG_FIREWALL) || defined(CONFIG_IP_MASQUERADE)
+ int fw_res = 0;
+#endif
- /*
- * We may be sharing the buffer with a snooper. That won't do
- */
-
- if((skb=skb_unshare(skb, GFP_ATOMIC,FREE_READ))==NULL)
- return -1;
+ if (skb->pkt_type != PACKET_HOST) {
+ kfree_skb(skb,FREE_WRITE);
+ return 0;
+ }
/*
* 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--;
+ iph = skb->nh.iph;
+ rt = (struct rtable*)skb->dst;
- /*
- * 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;
- }
+#ifdef CONFIG_TRANSPARENT_PROXY
+ if (ip_chk_sock(skb))
+ return ip_local_deliver(skb);
+#endif
- if (iph->ttl <= 0)
- {
+ if (ip_decrease_ttl(iph) <= 0) {
/* Tell the sender its packet died... */
- icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
+ icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
+ kfree_skb(skb, FREE_WRITE);
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
+ if (opt->is_strictroute && (rt->rt_flags&RTF_GATEWAY)) {
/*
- * OK, the packet is still valid. Fetch its destination address,
- * and give it to the IP sender for further processing.
+ * Strict routing permits no gatewaying
*/
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
+ kfree_skb(skb, FREE_WRITE);
+ return -1;
+ }
- rt = ip_rt_route(target_addr, 0);
- 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...
- */
+ /*
+ * Having picked a route we can now send the frame out
+ * after asking the firewall permission to do so.
+ */
- raddr = rt->rt_gateway;
-
- if (opt->is_strictroute && (rt->rt_flags & RTF_GATEWAY)) {
- /*
- * Strict routing permits no gatewaying
- */
+ skb->priority = rt->u.dst.priority;
+ dev2 = rt->u.dst.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.
+ */
+ if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr)
+ ip_rt_send_redirect(skb);
- ip_rt_put(rt);
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
+ /*
+ * 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) {
+ if (skb->len > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
+ ip_statistics.IpFragFails++;
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu));
+ kfree_skb(skb, FREE_WRITE);
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 (rt->rt_flags&RTCF_NAT) {
+ if (ip_do_nat(skb)) {
+ kfree_skb(skb, FREE_WRITE);
+ return -1;
+ }
+ }
- 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)
- {
- if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0)
- {
- if (rt)
- ip_rt_put(rt);
- /* Problem - ie bad checksum */
+ if(!(IPCB(skb)->flags&IPSKB_MASQUERADED)) {
+
+ if (rt->rt_flags&RTCF_VALVE) {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PKT_FILTERED, 0);
+ kfree_skb(skb, FREE_READ);
return -1;
}
- if (fw_res)
- /* ICMP matched - skip firewall */
+ /*
+ * 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) {
+ if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0) {
+ kfree_skb(skb, FREE_READ);
+ return -1;
+ }
+
+ if (fw_res)
+ /* ICMP matched - skip firewall */
+ goto skip_call_fw_firewall;
+ }
+ if (rt->rt_flags&RTCF_MASQ)
goto skip_call_fw_firewall;
}
#endif
+#ifdef CONFIG_FIREWALL
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);
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
/* fall thru */
default:
+ kfree_skb(skb, FREE_READ);
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
+skip_call_fw_firewall:
/*
* 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);
+ if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) &&
+ (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) {
+ if (ip_fw_masquerade(&skb, dev2) < 0) {
+ kfree_skb(skb, FREE_READ);
return -1;
}
+ }
#endif
- IS_SKB(skb);
- if (skb->len+encap > dev2->mtu && (iph->frag_off & htons(IP_DF)))
- {
- ip_statistics.IpFragFails++;
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
- if(rt)
- ip_rt_put(rt);
- return -1;
- }
+ if (skb_headroom(skb) < dev2->hard_header_len) {
+ struct sk_buff *skb2;
+ skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
+ kfree_skb(skb, FREE_WRITE);
-#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)
- {
+ 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;
+ skb = skb2;
+ iph = skb2->nh.iph;
}
- else
- {
- /*
- * Build a new MAC header.
- */
- skb2 = skb;
- skb2->dev=dev2;
-#ifdef CONFIG_IP_MROUTE
- if(is_frag&IPFWD_MULTITUNNEL)
- ip_encap(skb,skb->len, dev2, raddr);
- 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)
- {
+ if ((fw_res = call_out_firewall(PF_INET, skb->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);
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+ kfree_skb(skb,FREE_WRITE);
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.
- */
+ ip_statistics.IpForwDatagrams++;
- 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;
+ if (opt->optlen)
+ ip_forward_options(skb);
-#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;
+ ip_send(skb);
}
-#endif
- return 1;
+ return 0;
}
-
-
-#endif
-
-
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov