2005-04-16 15:20:36 -07:00
/*
* xfrm4_input . c
*
* Changes :
* YOSHIFUJI Hideaki @ USAGI
* Split up af - specific portion
* Derek Atkins < derek @ ihtfp . com >
* Add Encapsulation support
2007-02-09 23:24:47 +09:00
*
2005-04-16 15:20:36 -07:00
*/
# include <linux/module.h>
# include <linux/string.h>
2006-01-06 23:03:34 -08:00
# include <linux/netfilter.h>
# include <linux/netfilter_ipv4.h>
2005-04-16 15:20:36 -07:00
# include <net/ip.h>
# include <net/xfrm.h>
2006-09-27 18:47:59 -07:00
static int xfrm4_parse_spi ( struct sk_buff * skb , u8 nexthdr , __be32 * spi , __be32 * seq )
2005-04-16 15:20:36 -07:00
{
switch ( nexthdr ) {
case IPPROTO_IPIP :
2007-02-13 12:54:47 -08:00
case IPPROTO_IPV6 :
2007-04-20 22:47:35 -07:00
* spi = ip_hdr ( skb ) - > saddr ;
2005-04-16 15:20:36 -07:00
* seq = 0 ;
return 0 ;
}
return xfrm_parse_spi ( skb , nexthdr , spi , seq ) ;
}
2006-01-06 23:03:34 -08:00
# ifdef CONFIG_NETFILTER
static inline int xfrm4_rcv_encap_finish ( struct sk_buff * skb )
{
if ( skb - > dst = = NULL ) {
2007-04-20 22:47:35 -07:00
const struct iphdr * iph = ip_hdr ( skb ) ;
2006-01-06 23:03:34 -08:00
if ( ip_route_input ( skb , iph - > daddr , iph - > saddr , iph - > tos ,
2007-02-09 23:24:47 +09:00
skb - > dev ) )
2006-01-06 23:03:34 -08:00
goto drop ;
}
return dst_input ( skb ) ;
drop :
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}
# endif
2007-07-05 17:08:05 -07:00
static int xfrm4_rcv_encap ( struct sk_buff * skb , __u16 encap_type )
2005-04-16 15:20:36 -07:00
{
2006-09-27 18:47:59 -07:00
__be32 spi , seq ;
2006-04-01 00:54:16 -08:00
struct xfrm_state * xfrm_vec [ XFRM_MAX_DEPTH ] ;
2005-04-16 15:20:36 -07:00
struct xfrm_state * x ;
int xfrm_nr = 0 ;
int decaps = 0 ;
2007-04-20 22:47:35 -07:00
int err = xfrm4_parse_spi ( skb , ip_hdr ( skb ) - > protocol , & spi , & seq ) ;
2005-04-16 15:20:36 -07:00
2007-04-20 22:47:35 -07:00
if ( err ! = 0 )
2005-04-16 15:20:36 -07:00
goto drop ;
do {
2007-04-20 22:47:35 -07:00
const struct iphdr * iph = ip_hdr ( skb ) ;
2005-04-16 15:20:36 -07:00
if ( xfrm_nr = = XFRM_MAX_DEPTH )
goto drop ;
2007-02-13 12:54:47 -08:00
x = xfrm_state_lookup ( ( xfrm_address_t * ) & iph - > daddr , spi ,
iph - > protocol ! = IPPROTO_IPV6 ? iph - > protocol : IPPROTO_IPIP , AF_INET ) ;
2005-04-16 15:20:36 -07:00
if ( x = = NULL )
goto drop ;
spin_lock ( & x - > lock ) ;
if ( unlikely ( x - > km . state ! = XFRM_STATE_VALID ) )
goto drop_unlock ;
2006-04-04 12:51:05 -07:00
if ( ( x - > encap ? x - > encap - > encap_type : 0 ) ! = encap_type )
2006-04-01 00:52:46 -08:00
goto drop_unlock ;
2005-04-16 15:20:36 -07:00
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 00:52:46 -08:00
if ( x - > type - > input ( x , skb ) )
2005-04-16 15:20:36 -07:00
goto drop_unlock ;
/* only the first xfrm gets the encap type */
encap_type = 0 ;
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 00:54:16 -08:00
xfrm_vec [ xfrm_nr + + ] = x ;
2005-04-16 15:20:36 -07:00
2006-05-27 23:05:54 -07:00
if ( x - > mode - > input ( x , skb ) )
goto drop ;
2005-04-16 15:20:36 -07:00
2006-09-22 15:05:15 -07:00
if ( x - > props . mode = = XFRM_MODE_TUNNEL ) {
2005-04-16 15:20:36 -07:00
decaps = 1 ;
break ;
}
2007-04-20 22:47:35 -07:00
err = xfrm_parse_spi ( skb , ip_hdr ( skb ) - > protocol , & spi , & seq ) ;
if ( err < 0 )
2005-04-16 15:20:36 -07:00
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 00:54:16 -08:00
memcpy ( skb - > sp - > xvec + skb - > sp - > len , xfrm_vec ,
xfrm_nr * sizeof ( xfrm_vec [ 0 ] ) ) ;
2005-04-16 15:20:36 -07:00
skb - > sp - > len + = xfrm_nr ;
2006-01-06 23:03:34 -08:00
nf_reset ( skb ) ;
2005-04-16 15:20:36 -07:00
if ( decaps ) {
2007-05-29 13:03:17 -07:00
dst_release ( skb - > dst ) ;
skb - > dst = NULL ;
2005-04-16 15:20:36 -07:00
netif_rx ( skb ) ;
return 0 ;
} else {
2006-01-06 23:03:34 -08:00
# ifdef CONFIG_NETFILTER
2007-04-10 20:50:43 -07:00
__skb_push ( skb , skb - > data - skb_network_header ( skb ) ) ;
2007-04-20 22:47:35 -07:00
ip_hdr ( skb ) - > tot_len = htons ( skb - > len ) ;
ip_send_check ( ip_hdr ( skb ) ) ;
2006-01-06 23:03:34 -08:00
NF_HOOK ( PF_INET , NF_IP_PRE_ROUTING , skb , skb - > dev , NULL ,
2007-02-09 23:24:47 +09:00
xfrm4_rcv_encap_finish ) ;
2006-01-06 23:03:34 -08:00
return 0 ;
# else
2007-04-20 22:47:35 -07:00
return - ip_hdr ( skb ) - > protocol ;
2006-01-06 23:03:34 -08:00
# endif
2005-04-16 15:20:36 -07:00
}
drop_unlock :
spin_unlock ( & x - > lock ) ;
xfrm_state_put ( x ) ;
drop :
while ( - - xfrm_nr > = 0 )
2006-04-01 00:54:16 -08:00
xfrm_state_put ( xfrm_vec [ xfrm_nr ] ) ;
2005-04-16 15:20:36 -07:00
kfree_skb ( skb ) ;
return 0 ;
}
2007-07-05 17:08:05 -07:00
/* If it's a keepalive packet, then just eat it.
* If it ' s an encapsulated packet , then pass it to the
* IPsec xfrm input .
* Returns 0 if skb passed to xfrm or was dropped .
* Returns > 0 if skb should be passed to UDP .
* Returns < 0 if skb should be resubmitted ( - ret is protocol )
*/
int xfrm4_udp_encap_rcv ( struct sock * sk , struct sk_buff * skb )
{
struct udp_sock * up = udp_sk ( sk ) ;
struct udphdr * uh ;
struct iphdr * iph ;
int iphlen , len ;
int ret ;
__u8 * udpdata ;
__be32 * udpdata32 ;
__u16 encap_type = up - > encap_type ;
/* if this is not encapsulated socket, then just return now */
if ( ! encap_type )
return 1 ;
/* If this is a paged skb, make sure we pull up
* whatever data we need to look at . */
len = skb - > len - sizeof ( struct udphdr ) ;
if ( ! pskb_may_pull ( skb , sizeof ( struct udphdr ) + min ( len , 8 ) ) )
return 1 ;
/* Now we can get the pointers */
uh = udp_hdr ( skb ) ;
udpdata = ( __u8 * ) uh + sizeof ( struct udphdr ) ;
udpdata32 = ( __be32 * ) udpdata ;
switch ( encap_type ) {
default :
case UDP_ENCAP_ESPINUDP :
/* Check if this is a keepalive packet. If so, eat it. */
if ( len = = 1 & & udpdata [ 0 ] = = 0xff ) {
goto drop ;
} else if ( len > sizeof ( struct ip_esp_hdr ) & & udpdata32 [ 0 ] ! = 0 ) {
/* ESP Packet without Non-ESP header */
len = sizeof ( struct udphdr ) ;
} else
/* Must be an IKE packet.. pass it through */
return 1 ;
break ;
case UDP_ENCAP_ESPINUDP_NON_IKE :
/* Check if this is a keepalive packet. If so, eat it. */
if ( len = = 1 & & udpdata [ 0 ] = = 0xff ) {
goto drop ;
} else if ( len > 2 * sizeof ( u32 ) + sizeof ( struct ip_esp_hdr ) & &
udpdata32 [ 0 ] = = 0 & & udpdata32 [ 1 ] = = 0 ) {
/* ESP Packet with Non-IKE marker */
len = sizeof ( struct udphdr ) + 2 * sizeof ( u32 ) ;
} else
/* Must be an IKE packet.. pass it through */
return 1 ;
break ;
}
/* At this point we are sure that this is an ESPinUDP packet,
* so we need to remove ' len ' bytes from the packet ( the UDP
* header and optional ESP marker bytes ) and then modify the
* protocol to ESP , and then call into the transform receiver .
*/
if ( skb_cloned ( skb ) & & pskb_expand_head ( skb , 0 , 0 , GFP_ATOMIC ) )
goto drop ;
/* Now we can update and verify the packet length... */
iph = ip_hdr ( skb ) ;
iphlen = iph - > ihl < < 2 ;
iph - > tot_len = htons ( ntohs ( iph - > tot_len ) - len ) ;
if ( skb - > len < iphlen + len ) {
/* packet is too small!?! */
goto drop ;
}
/* pull the data buffer up to the ESP header and set the
* transport header to point to ESP . Keep UDP on the stack
* for later .
*/
__skb_pull ( skb , len ) ;
skb_reset_transport_header ( skb ) ;
/* modify the protocol (it's ESP!) */
iph - > protocol = IPPROTO_ESP ;
/* process ESP */
ret = xfrm4_rcv_encap ( skb , encap_type ) ;
return ret ;
drop :
kfree_skb ( skb ) ;
return 0 ;
}
int xfrm4_rcv ( struct sk_buff * skb )
{
return xfrm4_rcv_encap ( skb , 0 ) ;
}
EXPORT_SYMBOL ( xfrm4_rcv ) ;