2005-04-17 02:20:36 +04:00
/*
* xfrm6_input . c : based on net / ipv4 / xfrm4_input . c
*
* Authors :
* Mitsuru KANDA @ USAGI
* Kazunori MIYAZAWA @ USAGI
* Kunihiro Ishiguro < kunihiro @ ipinfusion . com >
* YOSHIFUJI Hideaki @ USAGI
* IPv6 support
*/
# include <linux/module.h>
# include <linux/string.h>
2006-01-07 10:03:34 +03:00
# include <linux/netfilter.h>
# include <linux/netfilter_ipv6.h>
2005-04-17 02:20:36 +04:00
# include <net/ipv6.h>
# include <net/xfrm.h>
2007-11-14 08:41:28 +03:00
int xfrm6_extract_input ( struct xfrm_state * x , struct sk_buff * skb )
{
return xfrm6_extract_header ( skb ) ;
}
2007-10-18 08:29:25 +04:00
int xfrm6_rcv_spi ( struct sk_buff * skb , int nexthdr , __be32 spi )
2005-04-17 02:20:36 +04:00
{
int err ;
2006-09-28 05:47:59 +04:00
__be32 seq ;
2006-04-01 12:54:16 +04:00
struct xfrm_state * xfrm_vec [ XFRM_MAX_DEPTH ] ;
2005-04-17 02:20:36 +04:00
struct xfrm_state * x ;
int xfrm_nr = 0 ;
int decaps = 0 ;
unsigned int nhoff ;
2006-01-07 10:02:34 +03:00
nhoff = IP6CB ( skb ) - > nhoff ;
2005-04-17 02:20:36 +04:00
seq = 0 ;
if ( ! spi & & ( err = xfrm_parse_spi ( skb , nexthdr , & spi , & seq ) ) ! = 0 )
goto drop ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
do {
2007-04-26 04:54:47 +04:00
struct ipv6hdr * iph = ipv6_hdr ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( xfrm_nr = = XFRM_MAX_DEPTH )
goto drop ;
2007-02-13 23:55:55 +03:00
x = xfrm_state_lookup ( ( xfrm_address_t * ) & iph - > daddr , spi ,
2007-10-18 08:29:25 +04:00
nexthdr , AF_INET6 ) ;
2005-04-17 02:20:36 +04:00
if ( x = = NULL )
goto drop ;
spin_lock ( & x - > lock ) ;
if ( unlikely ( x - > km . state ! = XFRM_STATE_VALID ) )
goto drop_unlock ;
if ( x - > props . replay_window & & xfrm_replay_check ( x , seq ) )
goto drop_unlock ;
if ( xfrm_state_check_expire ( x ) )
goto drop_unlock ;
2006-04-01 12:52:46 +04:00
nexthdr = x - > type - > input ( x , skb ) ;
2005-04-17 02:20:36 +04:00
if ( nexthdr < = 0 )
goto drop_unlock ;
2007-04-11 07:50:43 +04:00
skb_network_header ( skb ) [ nhoff ] = nexthdr ;
2005-04-17 02:20:36 +04:00
if ( x - > props . replay_window )
xfrm_replay_advance ( x , seq ) ;
x - > curlft . bytes + = skb - > len ;
x - > curlft . packets + + ;
spin_unlock ( & x - > lock ) ;
2006-04-01 12:54:16 +04:00
xfrm_vec [ xfrm_nr + + ] = x ;
2005-04-17 02:20:36 +04:00
2007-11-14 08:41:28 +03:00
if ( x - > inner_mode - > input ( x , skb ) )
2006-05-28 10:05:54 +04:00
goto drop ;
2007-10-18 08:35:51 +04:00
if ( x - > outer_mode - > flags & XFRM_MODE_FLAG_TUNNEL ) {
2005-04-17 02:20:36 +04:00
decaps = 1 ;
break ;
}
if ( ( err = xfrm_parse_spi ( skb , nexthdr , & spi , & seq ) ) < 0 )
goto drop ;
} while ( ! err ) ;
/* Allocate new secpath or COW existing one. */
if ( ! skb - > sp | | atomic_read ( & skb - > sp - > refcnt ) ! = 1 ) {
struct sec_path * sp ;
sp = secpath_dup ( skb - > sp ) ;
if ( ! sp )
goto drop ;
if ( skb - > sp )
secpath_put ( skb - > sp ) ;
skb - > sp = sp ;
}
if ( xfrm_nr + skb - > sp - > len > XFRM_MAX_DEPTH )
goto drop ;
2006-04-01 12:54:16 +04:00
memcpy ( skb - > sp - > xvec + skb - > sp - > len , xfrm_vec ,
xfrm_nr * sizeof ( xfrm_vec [ 0 ] ) ) ;
2005-04-17 02:20:36 +04:00
skb - > sp - > len + = xfrm_nr ;
2006-01-07 10:03:34 +03:00
nf_reset ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( decaps ) {
2007-05-30 00:03:17 +04:00
dst_release ( skb - > dst ) ;
skb - > dst = NULL ;
2005-04-17 02:20:36 +04:00
netif_rx ( skb ) ;
return - 1 ;
} else {
2006-01-07 10:03:34 +03:00
# ifdef CONFIG_NETFILTER
2007-04-26 04:54:47 +04:00
ipv6_hdr ( skb ) - > payload_len = htons ( skb - > len ) ;
2007-04-11 07:50:43 +04:00
__skb_push ( skb , skb - > data - skb_network_header ( skb ) ) ;
2006-01-07 10:03:34 +03:00
NF_HOOK ( PF_INET6 , NF_IP6_PRE_ROUTING , skb , skb - > dev , NULL ,
2007-02-09 17:24:49 +03:00
ip6_rcv_finish ) ;
2006-01-07 10:03:34 +03:00
return - 1 ;
# else
2005-04-17 02:20:36 +04:00
return 1 ;
2006-01-07 10:03:34 +03:00
# endif
2005-04-17 02:20:36 +04:00
}
drop_unlock :
spin_unlock ( & x - > lock ) ;
xfrm_state_put ( x ) ;
drop :
while ( - - xfrm_nr > = 0 )
2006-04-01 12:54:16 +04:00
xfrm_state_put ( xfrm_vec [ xfrm_nr ] ) ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
return - 1 ;
}
EXPORT_SYMBOL ( xfrm6_rcv_spi ) ;
2007-10-15 23:50:28 +04:00
int xfrm6_rcv ( struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
2007-10-18 08:29:25 +04:00
return xfrm6_rcv_spi ( skb , skb_network_header ( skb ) [ IP6CB ( skb ) - > nhoff ] ,
0 ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-24 05:08:21 +04:00
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( xfrm6_rcv ) ;
2006-08-24 05:08:21 +04:00
int xfrm6_input_addr ( struct sk_buff * skb , xfrm_address_t * daddr ,
xfrm_address_t * saddr , u8 proto )
{
2007-02-09 17:24:49 +03:00
struct xfrm_state * x = NULL ;
int wildcard = 0 ;
2006-08-24 05:08:21 +04:00
xfrm_address_t * xany ;
struct xfrm_state * xfrm_vec_one = NULL ;
2007-02-09 17:24:49 +03:00
int nh = 0 ;
2006-08-24 05:08:21 +04:00
int i = 0 ;
2007-04-24 15:44:50 +04:00
xany = ( xfrm_address_t * ) & in6addr_any ;
2006-08-24 05:08:21 +04:00
for ( i = 0 ; i < 3 ; i + + ) {
xfrm_address_t * dst , * src ;
switch ( i ) {
case 0 :
dst = daddr ;
src = saddr ;
break ;
case 1 :
/* lookup state with wild-card source address */
wildcard = 1 ;
dst = daddr ;
src = xany ;
break ;
case 2 :
default :
2007-02-09 17:24:49 +03:00
/* lookup state with wild-card addresses */
2006-08-24 05:08:21 +04:00
wildcard = 1 ; /* XXX */
dst = xany ;
src = xany ;
break ;
2007-02-09 17:24:49 +03:00
}
2006-08-24 05:08:21 +04:00
x = xfrm_state_lookup_byaddr ( dst , src , proto , AF_INET6 ) ;
if ( ! x )
continue ;
spin_lock ( & x - > lock ) ;
if ( wildcard ) {
if ( ( x - > props . flags & XFRM_STATE_WILDRECV ) = = 0 ) {
spin_unlock ( & x - > lock ) ;
xfrm_state_put ( x ) ;
x = NULL ;
continue ;
}
}
if ( unlikely ( x - > km . state ! = XFRM_STATE_VALID ) ) {
spin_unlock ( & x - > lock ) ;
xfrm_state_put ( x ) ;
2007-02-09 17:24:49 +03:00
x = NULL ;
continue ;
2006-08-24 05:08:21 +04:00
}
if ( xfrm_state_check_expire ( x ) ) {
spin_unlock ( & x - > lock ) ;
xfrm_state_put ( x ) ;
x = NULL ;
continue ;
}
nh = x - > type - > input ( x , skb ) ;
if ( nh < = 0 ) {
spin_unlock ( & x - > lock ) ;
xfrm_state_put ( x ) ;
x = NULL ;
continue ;
}
x - > curlft . bytes + = skb - > len ;
x - > curlft . packets + + ;
spin_unlock ( & x - > lock ) ;
xfrm_vec_one = x ;
break ;
}
if ( ! xfrm_vec_one )
goto drop ;
/* Allocate new secpath or COW existing one. */
if ( ! skb - > sp | | atomic_read ( & skb - > sp - > refcnt ) ! = 1 ) {
struct sec_path * sp ;
sp = secpath_dup ( skb - > sp ) ;
if ( ! sp )
goto drop ;
if ( skb - > sp )
secpath_put ( skb - > sp ) ;
skb - > sp = sp ;
}
if ( 1 + skb - > sp - > len > XFRM_MAX_DEPTH )
goto drop ;
skb - > sp - > xvec [ skb - > sp - > len ] = xfrm_vec_one ;
skb - > sp - > len + + ;
return 1 ;
drop :
if ( xfrm_vec_one )
xfrm_state_put ( xfrm_vec_one ) ;
return - 1 ;
}
2007-02-22 16:05:40 +03:00
EXPORT_SYMBOL ( xfrm6_input_addr ) ;