2005-08-10 06:42:34 +04:00
/* IPv4 specific functions of netfilter core */
2005-08-10 06:39:00 +04:00
# include <linux/kernel.h>
# include <linux/netfilter.h>
2005-08-10 06:42:34 +04:00
# include <linux/netfilter_ipv4.h>
2006-01-07 10:04:54 +03:00
# include <linux/ip.h>
2007-10-14 11:39:55 +04:00
# include <linux/skbuff.h>
2005-08-10 06:39:00 +04:00
# include <net/route.h>
2006-01-07 10:04:54 +03:00
# include <net/xfrm.h>
# include <net/ip.h>
2007-12-05 12:24:48 +03:00
# include <net/netfilter/nf_queue.h>
2005-08-10 06:39:00 +04:00
/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
2007-10-15 11:53:15 +04:00
int ip_route_me_harder ( struct sk_buff * skb , unsigned addr_type )
2005-08-10 06:39:00 +04:00
{
2007-10-15 11:53:15 +04:00
const struct iphdr * iph = ip_hdr ( skb ) ;
2005-08-10 06:39:00 +04:00
struct rtable * rt ;
struct flowi fl = { } ;
struct dst_entry * odst ;
unsigned int hh_len ;
2007-01-04 23:15:34 +03:00
unsigned int type ;
2005-08-10 06:39:00 +04:00
2008-01-10 14:25:28 +03:00
type = inet_addr_type ( & init_net , iph - > saddr ) ;
2006-10-03 03:11:13 +04:00
if ( addr_type = = RTN_UNSPEC )
2007-01-04 23:15:34 +03:00
addr_type = type ;
2006-10-03 03:11:13 +04:00
2005-08-10 06:39:00 +04:00
/* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
2007-11-20 05:53:30 +03:00
* packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook .
2005-08-10 06:39:00 +04:00
*/
2006-10-03 03:11:13 +04:00
if ( addr_type = = RTN_LOCAL ) {
2005-08-10 06:39:00 +04:00
fl . nl_u . ip4_u . daddr = iph - > daddr ;
2007-01-04 23:15:34 +03:00
if ( type = = RTN_LOCAL )
fl . nl_u . ip4_u . saddr = iph - > saddr ;
2005-08-10 06:39:00 +04:00
fl . nl_u . ip4_u . tos = RT_TOS ( iph - > tos ) ;
2007-10-15 11:53:15 +04:00
fl . oif = skb - > sk ? skb - > sk - > sk_bound_dev_if : 0 ;
fl . mark = skb - > mark ;
2008-01-23 09:07:34 +03:00
if ( ip_route_output_key ( & init_net , & rt , & fl ) ! = 0 )
2005-08-10 06:39:00 +04:00
return - 1 ;
/* Drop old route. */
2007-10-15 11:53:15 +04:00
dst_release ( skb - > dst ) ;
skb - > dst = & rt - > u . dst ;
2005-08-10 06:39:00 +04:00
} else {
/* non-local src, find valid iif to satisfy
* rp - filter when calling ip_route_input . */
fl . nl_u . ip4_u . daddr = iph - > saddr ;
2008-01-23 09:07:34 +03:00
if ( ip_route_output_key ( & init_net , & rt , & fl ) ! = 0 )
2005-08-10 06:39:00 +04:00
return - 1 ;
2007-10-15 11:53:15 +04:00
odst = skb - > dst ;
if ( ip_route_input ( skb , iph - > daddr , iph - > saddr ,
2005-08-10 06:39:00 +04:00
RT_TOS ( iph - > tos ) , rt - > u . dst . dev ) ! = 0 ) {
dst_release ( & rt - > u . dst ) ;
return - 1 ;
}
dst_release ( & rt - > u . dst ) ;
dst_release ( odst ) ;
}
2007-02-09 17:24:47 +03:00
2007-10-15 11:53:15 +04:00
if ( skb - > dst - > error )
2005-08-10 06:39:00 +04:00
return - 1 ;
2006-01-07 10:04:54 +03:00
# ifdef CONFIG_XFRM
2007-10-15 11:53:15 +04:00
if ( ! ( IPCB ( skb ) - > flags & IPSKB_XFRM_TRANSFORMED ) & &
xfrm_decode_session ( skb , & fl , AF_INET ) = = 0 )
if ( xfrm_lookup ( & skb - > dst , & fl , skb - > sk , 0 ) )
2006-01-07 10:04:54 +03:00
return - 1 ;
# endif
2005-08-10 06:39:00 +04:00
/* Change in oif may mean change in hh_len. */
2007-10-15 11:53:15 +04:00
hh_len = skb - > dst - > dev - > hard_header_len ;
if ( skb_headroom ( skb ) < hh_len & &
pskb_expand_head ( skb , hh_len - skb_headroom ( skb ) , 0 , GFP_ATOMIC ) )
2007-10-14 11:39:55 +04:00
return - 1 ;
2005-08-10 06:39:00 +04:00
return 0 ;
}
EXPORT_SYMBOL ( ip_route_me_harder ) ;
2005-08-10 06:42:34 +04:00
2006-02-15 12:34:23 +03:00
# ifdef CONFIG_XFRM
2007-10-15 11:53:15 +04:00
int ip_xfrm_me_harder ( struct sk_buff * skb )
2006-02-15 12:34:23 +03:00
{
struct flowi fl ;
unsigned int hh_len ;
struct dst_entry * dst ;
2007-10-15 11:53:15 +04:00
if ( IPCB ( skb ) - > flags & IPSKB_XFRM_TRANSFORMED )
2006-02-15 12:34:23 +03:00
return 0 ;
2007-10-15 11:53:15 +04:00
if ( xfrm_decode_session ( skb , & fl , AF_INET ) < 0 )
2006-02-15 12:34:23 +03:00
return - 1 ;
2007-10-15 11:53:15 +04:00
dst = skb - > dst ;
2006-02-15 12:34:23 +03:00
if ( dst - > xfrm )
dst = ( ( struct xfrm_dst * ) dst ) - > route ;
dst_hold ( dst ) ;
2007-10-15 11:53:15 +04:00
if ( xfrm_lookup ( & dst , & fl , skb - > sk , 0 ) < 0 )
2006-02-15 12:34:23 +03:00
return - 1 ;
2007-10-15 11:53:15 +04:00
dst_release ( skb - > dst ) ;
skb - > dst = dst ;
2006-02-15 12:34:23 +03:00
/* Change in oif may mean change in hh_len. */
2007-10-15 11:53:15 +04:00
hh_len = skb - > dst - > dev - > hard_header_len ;
if ( skb_headroom ( skb ) < hh_len & &
pskb_expand_head ( skb , hh_len - skb_headroom ( skb ) , 0 , GFP_ATOMIC ) )
2007-10-14 11:39:55 +04:00
return - 1 ;
2006-02-15 12:34:23 +03:00
return 0 ;
}
EXPORT_SYMBOL ( ip_xfrm_me_harder ) ;
# endif
2006-01-07 10:06:30 +03:00
void ( * ip_nat_decode_session ) ( struct sk_buff * , struct flowi * ) ;
EXPORT_SYMBOL ( ip_nat_decode_session ) ;
2005-08-10 06:42:34 +04:00
/*
* Extra routing may needed on local out , as the QUEUE target never
* returns control to the table .
*/
struct ip_rt_info {
2006-09-29 01:21:07 +04:00
__be32 daddr ;
__be32 saddr ;
2005-08-10 06:42:34 +04:00
u_int8_t tos ;
} ;
2007-12-05 12:26:33 +03:00
static void nf_ip_saveroute ( const struct sk_buff * skb ,
struct nf_queue_entry * entry )
2005-08-10 06:42:34 +04:00
{
2007-12-05 12:26:33 +03:00
struct ip_rt_info * rt_info = nf_queue_entry_reroute ( entry ) ;
2005-08-10 06:42:34 +04:00
2007-12-05 12:26:33 +03:00
if ( entry - > hook = = NF_INET_LOCAL_OUT ) {
2007-04-21 09:47:35 +04:00
const struct iphdr * iph = ip_hdr ( skb ) ;
2005-08-10 06:42:34 +04:00
rt_info - > tos = iph - > tos ;
rt_info - > daddr = iph - > daddr ;
rt_info - > saddr = iph - > saddr ;
}
}
2007-12-05 12:26:33 +03:00
static int nf_ip_reroute ( struct sk_buff * skb ,
const struct nf_queue_entry * entry )
2005-08-10 06:42:34 +04:00
{
2007-12-05 12:26:33 +03:00
const struct ip_rt_info * rt_info = nf_queue_entry_reroute ( entry ) ;
2005-08-10 06:42:34 +04:00
2007-12-05 12:26:33 +03:00
if ( entry - > hook = = NF_INET_LOCAL_OUT ) {
2007-10-15 11:53:15 +04:00
const struct iphdr * iph = ip_hdr ( skb ) ;
2005-08-10 06:42:34 +04:00
if ( ! ( iph - > tos = = rt_info - > tos
& & iph - > daddr = = rt_info - > daddr
& & iph - > saddr = = rt_info - > saddr ) )
2007-10-15 11:53:15 +04:00
return ip_route_me_harder ( skb , RTN_UNSPEC ) ;
2005-08-10 06:42:34 +04:00
}
return 0 ;
}
2006-11-15 08:40:42 +03:00
__sum16 nf_ip_checksum ( struct sk_buff * skb , unsigned int hook ,
2006-04-07 01:18:43 +04:00
unsigned int dataoff , u_int8_t protocol )
{
2007-04-21 09:47:35 +04:00
const struct iphdr * iph = ip_hdr ( skb ) ;
2006-11-15 08:40:42 +03:00
__sum16 csum = 0 ;
2006-04-07 01:18:43 +04:00
switch ( skb - > ip_summed ) {
2006-08-30 03:44:56 +04:00
case CHECKSUM_COMPLETE :
2007-11-20 05:53:30 +03:00
if ( hook ! = NF_INET_PRE_ROUTING & & hook ! = NF_INET_LOCAL_IN )
2006-04-07 01:18:43 +04:00
break ;
2006-11-15 08:24:49 +03:00
if ( ( protocol = = 0 & & ! csum_fold ( skb - > csum ) ) | |
2006-04-07 01:18:43 +04:00
! csum_tcpudp_magic ( iph - > saddr , iph - > daddr ,
2007-02-09 17:24:47 +03:00
skb - > len - dataoff , protocol ,
2006-04-07 01:18:43 +04:00
skb - > csum ) ) {
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
break ;
}
/* fall through */
case CHECKSUM_NONE :
if ( protocol = = 0 )
skb - > csum = 0 ;
else
skb - > csum = csum_tcpudp_nofold ( iph - > saddr , iph - > daddr ,
skb - > len - dataoff ,
protocol , 0 ) ;
csum = __skb_checksum_complete ( skb ) ;
}
return csum ;
}
EXPORT_SYMBOL ( nf_ip_checksum ) ;
2007-12-05 12:22:05 +03:00
static int nf_ip_route ( struct dst_entry * * dst , struct flowi * fl )
{
2008-01-23 09:07:34 +03:00
return ip_route_output_key ( & init_net , ( struct rtable * * ) dst , fl ) ;
2007-12-05 12:22:05 +03:00
}
2007-12-18 09:42:27 +03:00
static const struct nf_afinfo nf_ip_afinfo = {
2006-04-07 01:18:09 +04:00
. family = AF_INET ,
2006-04-07 01:18:43 +04:00
. checksum = nf_ip_checksum ,
2007-12-05 12:22:05 +03:00
. route = nf_ip_route ,
2006-04-07 01:18:09 +04:00
. saveroute = nf_ip_saveroute ,
. reroute = nf_ip_reroute ,
. route_key_size = sizeof ( struct ip_rt_info ) ,
2005-08-10 06:42:34 +04:00
} ;
2006-03-29 04:37:06 +04:00
static int ipv4_netfilter_init ( void )
2005-08-10 06:42:34 +04:00
{
2006-04-07 01:18:09 +04:00
return nf_register_afinfo ( & nf_ip_afinfo ) ;
2005-08-10 06:42:34 +04:00
}
2006-03-29 04:37:06 +04:00
static void ipv4_netfilter_fini ( void )
2005-08-10 06:42:34 +04:00
{
2006-04-07 01:18:09 +04:00
nf_unregister_afinfo ( & nf_ip_afinfo ) ;
2005-08-10 06:42:34 +04:00
}
2006-03-29 04:37:06 +04:00
module_init ( ipv4_netfilter_init ) ;
module_exit ( ipv4_netfilter_fini ) ;
2008-01-15 10:48:39 +03:00
# ifdef CONFIG_SYSCTL
struct ctl_path nf_net_ipv4_netfilter_sysctl_path [ ] = {
{ . procname = " net " , . ctl_name = CTL_NET , } ,
{ . procname = " ipv4 " , . ctl_name = NET_IPV4 , } ,
{ . procname = " netfilter " , . ctl_name = NET_IPV4_NETFILTER , } ,
{ }
} ;
EXPORT_SYMBOL_GPL ( nf_net_ipv4_netfilter_sysctl_path ) ;
# endif /* CONFIG_SYSCTL */