diff options
Diffstat (limited to 'pfinet/linux-src/net/ipv4/ip_forward.c')
-rw-r--r-- | pfinet/linux-src/net/ipv4/ip_forward.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/pfinet/linux-src/net/ipv4/ip_forward.c b/pfinet/linux-src/net/ipv4/ip_forward.c new file mode 100644 index 00000000..08ebbc2f --- /dev/null +++ b/pfinet/linux-src/net/ipv4/ip_forward.c @@ -0,0 +1,297 @@ +/* + * 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. + * + * Version: $Id: ip_forward.c,v 1.43 1999/03/21 05:22:37 davem Exp $ + * + * 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). + * Mike McLagan : Routing by source + */ + +#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/tcp.h> +#include <net/udp.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_TRANSPARENT_PROXY +/* + * Check the packet against our socket administration to see + * if it is related to a connection on our system. + * Needed for transparent proxying. + */ + +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 + + +int ip_forward(struct sk_buff *skb) +{ + struct device *dev2; /* Output device */ + struct iphdr *iph; /* Our header */ + struct rtable *rt; /* Route we use */ + struct ip_options * opt = &(IPCB(skb)->opt); + unsigned short mtu; +#if defined(CONFIG_FIREWALL) || defined(CONFIG_IP_MASQUERADE) + int fw_res = 0; +#endif + + if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb)) + return 0; + + if (skb->pkt_type != PACKET_HOST) + goto drop; + + /* + * 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. + */ + + iph = skb->nh.iph; + rt = (struct rtable*)skb->dst; + +#ifdef CONFIG_CPU_IS_SLOW + if (net_cpu_congestion > 1 && !(iph->tos&IPTOS_RELIABILITY) && + IPTOS_PREC(iph->tos) < IPTOS_PREC_INTERNETCONTROL) { + if (((xtime.tv_usec&0xF)<<net_cpu_congestion) > 0x1C) + goto drop; + } +#endif + + +#ifdef CONFIG_IP_TRANSPARENT_PROXY + if (ip_chksock(skb)) + goto local_pkt; +#endif + + if (iph->ttl <= 1) + goto too_many_hops; + + if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway) + goto sr_failed; + + /* + * Having picked a route we can now send the frame out + * after asking the firewall permission to do so. + */ + + skb->priority = rt_tos2priority(iph->tos); + dev2 = rt->u.dst.dev; + mtu = rt->u.dst.pmtu; + +#ifdef CONFIG_NET_SECURITY + call_fw_firewall(PF_SECURITY, dev2, NULL, &mtu, NULL); +#endif + + /* + * 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); + + /* We are about to mangle packet. Copy it! */ + if ((skb = skb_cow(skb, dev2->hard_header_len)) == NULL) + return -1; + iph = skb->nh.iph; + opt = &(IPCB(skb)->opt); + + /* Decrease ttl after skb cow done */ + ip_decrease_ttl(iph); + + /* + * We now may allocate a new buffer, and copy the datagram into it. + * If the indicated interface is up and running, kick it. + */ + + if (skb->len > mtu && (ntohs(iph->frag_off) & IP_DF)) + goto frag_needed; + +#ifdef CONFIG_IP_ROUTE_NAT + if (rt->rt_flags & RTCF_NAT) { + if (ip_do_nat(skb)) { + kfree_skb(skb); + return -1; + } + } +#endif + +#ifdef CONFIG_IP_MASQUERADE + if(!(IPCB(skb)->flags&IPSKB_MASQUERADED)) { + /* + * 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) { + __u32 maddr; +#ifdef CONFIG_IP_MASQUERADE_ICMP + struct icmphdr *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 + maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE); + fw_res = ip_fw_masq_icmp(&skb, maddr); + if (fw_res < 0) { + kfree_skb(skb); + return -1; + } + + if (fw_res) + /* ICMP matched - skip firewall */ + goto skip_call_fw_firewall; +#ifdef CONFIG_IP_MASQUERADE_ICMP + } +#endif + } + if (rt->rt_flags&RTCF_MASQ) + goto skip_call_fw_firewall; +#endif /* CONFIG_IP_MASQUERADE */ + +#ifdef CONFIG_FIREWALL + fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL, &skb); + switch (fw_res) { + case FW_ACCEPT: + case FW_MASQUERADE: + break; + case FW_REJECT: + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); + /* fall thru */ + default: + kfree_skb(skb); + return -1; + } +#endif + +#ifdef CONFIG_IP_MASQUERADE + } + +skip_call_fw_firewall: + /* + * If this fragment needs masquerading, make it so... + * (Don't masquerade de-masqueraded fragments) + */ + if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) && + (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) { + u32 maddr; + +#ifdef CONFIG_IP_ROUTE_NAT + maddr = (rt->rt_flags&RTCF_MASQ) ? rt->rt_src_map : 0; + + if (maddr == 0) +#endif + maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE); + + if (ip_fw_masquerade(&skb, maddr) < 0) { + kfree_skb(skb); + return -1; + } else { + /* + * Masquerader may have changed skb + */ + iph = skb->nh.iph; + opt = &(IPCB(skb)->opt); + } + } +#endif + + +#ifdef CONFIG_FIREWALL + if ((fw_res = call_out_firewall(PF_INET, dev2, iph, NULL,&skb)) < FW_ACCEPT) { + /* FW_ACCEPT and FW_MASQUERADE are treated equal: + masquerading is only supported via forward rules */ + if (fw_res == FW_REJECT) + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0); + kfree_skb(skb); + return -1; + } +#endif + + ip_statistics.IpForwDatagrams++; + + if (opt->optlen == 0) { +#ifdef CONFIG_NET_FASTROUTE + if (rt->rt_flags&RTCF_FAST && !netdev_fastroute_obstacles) { + unsigned h = ((*(u8*)&rt->key.dst)^(*(u8*)&rt->key.src))&NETDEV_FASTROUTE_HMASK; + /* Time to switch to functional programming :-) */ + dst_release_irqwait(xchg(&skb->dev->fastpath[h], dst_clone(&rt->u.dst))); + } +#endif + ip_send(skb); + return 0; + } + + ip_forward_options(skb); + ip_send(skb); + return 0; + +#ifdef CONFIG_IP_TRANSPARENT_PROXY +local_pkt: + return ip_local_deliver(skb); +#endif + +frag_needed: + ip_statistics.IpFragFails++; + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); + goto drop; + +sr_failed: + /* + * Strict routing permits no gatewaying + */ + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0); + goto drop; + +too_many_hops: + /* Tell the sender its packet died... */ + icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0); +drop: + kfree_skb(skb); + return -1; +} |