2005-04-17 02:20:36 +04:00
/*
* 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.48 2000 / 12 / 13 18 : 31 : 48 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/netfilter_ipv4.h>
# include <net/checksum.h>
# include <linux/route.h>
# include <net/route.h>
# include <net/xfrm.h>
static inline int ip_forward_finish ( struct sk_buff * skb )
{
struct ip_options * opt = & ( IPCB ( skb ) - > opt ) ;
IP_INC_STATS_BH ( IPSTATS_MIB_OUTFORWDATAGRAMS ) ;
if ( unlikely ( opt - > optlen ) )
ip_forward_options ( skb ) ;
return dst_output ( skb ) ;
}
int ip_forward ( struct sk_buff * skb )
{
struct iphdr * iph ; /* Our header */
struct rtable * rt ; /* Route we use */
struct ip_options * opt = & ( IPCB ( skb ) - > opt ) ;
if ( ! xfrm4_policy_check ( NULL , XFRM_POLICY_FWD , skb ) )
goto drop ;
if ( IPCB ( skb ) - > opt . router_alert & & ip_call_ra_chain ( skb ) )
return NET_RX_SUCCESS ;
if ( skb - > pkt_type ! = PACKET_HOST )
goto drop ;
skb - > ip_summed = CHECKSUM_NONE ;
/*
* 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 .
*/
2005-08-21 04:27:09 +04:00
if ( skb - > nh . iph - > ttl < = 1 )
2005-04-17 02:20:36 +04:00
goto too_many_hops ;
if ( ! xfrm4_route_forward ( skb ) )
goto drop ;
rt = ( struct rtable * ) skb - > dst ;
if ( opt - > is_strictroute & & rt - > rt_dst ! = rt - > rt_gateway )
goto sr_failed ;
/* We are about to mangle packet. Copy it! */
if ( skb_cow ( skb , LL_RESERVED_SPACE ( rt - > u . dst . dev ) + rt - > u . dst . header_len ) )
goto drop ;
iph = skb - > nh . iph ;
/* Decrease ttl after skb cow done */
ip_decrease_ttl ( iph ) ;
/*
* 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 ) ;
skb - > priority = rt_tos2priority ( iph - > tos ) ;
return NF_HOOK ( PF_INET , NF_IP_FORWARD , skb , skb - > dev , rt - > u . dst . dev ,
ip_forward_finish ) ;
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 NET_RX_DROP ;
}