2005-08-10 06:39:00 +04:00
# include <linux/kernel.h>
2006-01-10 03:43:13 +03:00
# include <linux/init.h>
2005-08-10 06:39:00 +04:00
# include <linux/ipv6.h>
2005-08-10 06:42:34 +04:00
# include <linux/netfilter.h>
# include <linux/netfilter_ipv6.h>
2005-08-10 06:39:00 +04:00
# include <net/dst.h>
# include <net/ipv6.h>
# include <net/ip6_route.h>
2006-01-07 10:04:54 +03:00
# include <net/xfrm.h>
2006-04-08 02:00:06 +04:00
# include <net/ip6_checksum.h>
2007-12-05 12:24:48 +03:00
# include <net/netfilter/nf_queue.h>
2005-08-10 06:39:00 +04:00
int ip6_route_me_harder ( struct sk_buff * skb )
{
2008-10-15 09:55:21 +04:00
struct net * net = dev_net ( skb - > dst - > dev ) ;
2007-04-26 04:54:47 +04:00
struct ipv6hdr * iph = ipv6_hdr ( skb ) ;
2005-08-10 06:39:00 +04:00
struct dst_entry * dst ;
struct flowi fl = {
. oif = skb - > sk ? skb - > sk - > sk_bound_dev_if : 0 ,
2007-03-05 03:00:32 +03:00
. mark = skb - > mark ,
2005-08-10 06:39:00 +04:00
. nl_u =
{ . ip6_u =
{ . daddr = iph - > daddr ,
. saddr = iph - > saddr , } } ,
} ;
2008-10-15 09:55:21 +04:00
dst = ip6_route_output ( net , skb - > sk , & fl ) ;
2005-08-10 06:39:00 +04:00
2006-01-07 10:04:54 +03:00
# ifdef CONFIG_XFRM
if ( ! ( IP6CB ( skb ) - > flags & IP6SKB_XFRM_TRANSFORMED ) & &
xfrm_decode_session ( skb , & fl , AF_INET6 ) = = 0 )
if ( xfrm_lookup ( & skb - > dst , & fl , skb - > sk , 0 ) )
return - 1 ;
# endif
2005-08-10 06:39:00 +04:00
if ( dst - > error ) {
2008-10-15 09:55:21 +04:00
IP6_INC_STATS ( net , ip6_dst_idev ( dst ) , IPSTATS_MIB_OUTNOROUTES ) ;
2005-08-10 07:50:53 +04:00
LIMIT_NETDEBUG ( KERN_DEBUG " ip6_route_me_harder: No more route. \n " ) ;
2005-08-10 06:39:00 +04:00
dst_release ( dst ) ;
return - EINVAL ;
}
/* Drop old route. */
dst_release ( skb - > dst ) ;
skb - > dst = dst ;
return 0 ;
}
EXPORT_SYMBOL ( ip6_route_me_harder ) ;
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 ip6_rt_info {
struct in6_addr daddr ;
struct in6_addr saddr ;
} ;
2007-12-05 12:26:33 +03:00
static void nf_ip6_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 ip6_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-26 04:54:47 +04:00
struct ipv6hdr * iph = ipv6_hdr ( skb ) ;
2005-08-10 06:42:34 +04:00
rt_info - > daddr = iph - > daddr ;
rt_info - > saddr = iph - > saddr ;
}
}
2007-12-05 12:26:33 +03:00
static int nf_ip6_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
struct ip6_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
struct ipv6hdr * iph = ipv6_hdr ( skb ) ;
2005-08-10 06:42:34 +04:00
if ( ! ipv6_addr_equal ( & iph - > daddr , & rt_info - > daddr ) | |
! ipv6_addr_equal ( & iph - > saddr , & rt_info - > saddr ) )
2007-10-15 11:53:15 +04:00
return ip6_route_me_harder ( skb ) ;
2005-08-10 06:42:34 +04:00
}
return 0 ;
}
2007-12-05 12:22:05 +03:00
static int nf_ip6_route ( struct dst_entry * * dst , struct flowi * fl )
{
2008-03-05 21:48:10 +03:00
* dst = ip6_route_output ( & init_net , NULL , fl ) ;
2007-12-05 12:22:05 +03:00
return ( * dst ) - > error ;
}
2006-11-15 08:40:42 +03:00
__sum16 nf_ip6_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-26 04:54:47 +04:00
struct ipv6hdr * ip6h = ipv6_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 ;
if ( ! csum_ipv6_magic ( & ip6h - > saddr , & ip6h - > daddr ,
2007-02-09 17:24:49 +03:00
skb - > len - dataoff , protocol ,
2006-04-07 01:18:43 +04:00
csum_sub ( skb - > csum ,
skb_checksum ( skb , 0 ,
dataoff , 0 ) ) ) ) {
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
break ;
}
/* fall through */
case CHECKSUM_NONE :
2006-11-15 08:35:48 +03:00
skb - > csum = ~ csum_unfold (
csum_ipv6_magic ( & ip6h - > saddr , & ip6h - > daddr ,
2006-04-07 01:18:43 +04:00
skb - > len - dataoff ,
protocol ,
csum_sub ( 0 ,
skb_checksum ( skb , 0 ,
2007-02-09 17:24:49 +03:00
dataoff , 0 ) ) ) ) ;
2006-04-07 01:18:43 +04:00
csum = __skb_checksum_complete ( skb ) ;
}
return csum ;
}
EXPORT_SYMBOL ( nf_ip6_checksum ) ;
2008-03-20 17:15:53 +03:00
static __sum16 nf_ip6_checksum_partial ( struct sk_buff * skb , unsigned int hook ,
unsigned int dataoff , unsigned int len ,
u_int8_t protocol )
{
struct ipv6hdr * ip6h = ipv6_hdr ( skb ) ;
__wsum hsum ;
__sum16 csum = 0 ;
switch ( skb - > ip_summed ) {
case CHECKSUM_COMPLETE :
if ( len = = skb - > len - dataoff )
return nf_ip6_checksum ( skb , hook , dataoff , protocol ) ;
/* fall through */
case CHECKSUM_NONE :
hsum = skb_checksum ( skb , 0 , dataoff , 0 ) ;
skb - > csum = ~ csum_unfold ( csum_ipv6_magic ( & ip6h - > saddr ,
& ip6h - > daddr ,
skb - > len - dataoff ,
protocol ,
csum_sub ( 0 , hsum ) ) ) ;
skb - > ip_summed = CHECKSUM_NONE ;
csum = __skb_checksum_complete_head ( skb , dataoff + len ) ;
if ( ! csum )
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
}
return csum ;
} ;
2007-12-18 09:42:27 +03:00
static const struct nf_afinfo nf_ip6_afinfo = {
2008-03-20 17:15:53 +03:00
. family = AF_INET6 ,
. checksum = nf_ip6_checksum ,
. checksum_partial = nf_ip6_checksum_partial ,
. route = nf_ip6_route ,
. saveroute = nf_ip6_saveroute ,
. reroute = nf_ip6_reroute ,
. route_key_size = sizeof ( struct ip6_rt_info ) ,
2005-08-10 06:42:34 +04:00
} ;
int __init ipv6_netfilter_init ( void )
{
2006-04-07 01:18:09 +04:00
return nf_register_afinfo ( & nf_ip6_afinfo ) ;
2005-08-10 06:42:34 +04:00
}
2006-01-11 08:02:21 +03:00
/* This can be called from inet6_init() on errors, so it cannot
* be marked __exit . - DaveM
*/
void ipv6_netfilter_fini ( void )
2005-08-10 06:42:34 +04:00
{
2006-04-07 01:18:09 +04:00
nf_unregister_afinfo ( & nf_ip6_afinfo ) ;
2005-08-10 06:42:34 +04:00
}