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 .
2007-02-09 17:24:47 +03:00
*
2005-04-17 02:20:36 +04:00
* Version : $ Id : ip_forward . c , v 1.48 2000 / 12 / 13 18 : 31 : 48 davem Exp $
*
* Authors : see ip . c
*
* Fixes :
2007-02-09 17:24:47 +03:00
* Many : Split from ip . c , see ip_input . c for
2005-04-17 02:20:36 +04:00
* history .
2007-02-09 17:24:47 +03:00
* Dave Gregorich : NULL ip_rt_put fix for multicast
2005-04-17 02:20:36 +04:00
* 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/types.h>
# include <linux/mm.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 ;
2007-03-27 10:22:20 +04:00
skb_forward_csum ( skb ) ;
2007-02-09 17:24:47 +03:00
2005-04-17 02:20:36 +04:00
/*
* 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 .
*/
2007-04-21 09:47:35 +04:00
if ( ip_hdr ( skb ) - > ttl < = 1 )
2007-02-09 17:24:47 +03:00
goto too_many_hops ;
2005-04-17 02:20:36 +04:00
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 ;
2007-03-26 10:32:29 +04:00
if ( unlikely ( skb - > len > dst_mtu ( & rt - > u . dst ) & &
2007-07-19 05:43:47 +04:00
( ip_hdr ( skb ) - > frag_off & htons ( IP_DF ) ) ) & & ! skb - > local_df ) {
2007-03-26 10:32:29 +04:00
IP_INC_STATS ( IPSTATS_MIB_FRAGFAILS ) ;
icmp_send ( skb , ICMP_DEST_UNREACH , ICMP_FRAG_NEEDED ,
htonl ( dst_mtu ( & rt - > u . dst ) ) ) ;
goto drop ;
}
2005-04-17 02:20:36 +04:00
/* 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 ;
2007-04-21 09:47:35 +04:00
iph = ip_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
/* 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 :
2007-02-09 17:24:47 +03:00
/*
2005-04-17 02:20:36 +04:00
* Strict routing permits no gatewaying
*/
2007-02-09 17:24:47 +03:00
icmp_send ( skb , ICMP_DEST_UNREACH , ICMP_SR_FAILED , 0 ) ;
goto drop ;
2005-04-17 02:20:36 +04:00
too_many_hops :
2007-02-09 17:24:47 +03:00
/* Tell the sender its packet died... */
IP_INC_STATS_BH ( IPSTATS_MIB_INHDRERRORS ) ;
icmp_send ( skb , ICMP_TIME_EXCEEDED , ICMP_EXC_TTL , 0 ) ;
2005-04-17 02:20:36 +04:00
drop :
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}