2012-03-12 07:03:32 +00:00
# define pr_fmt(fmt) "IPsec: " fmt
2008-01-28 19:35:05 -08:00
# include <crypto/aead.h>
# include <crypto/authenc.h>
2006-07-30 15:41:01 +10:00
# include <linux/err.h>
2005-04-16 15:20:36 -07:00
# include <linux/module.h>
# include <net/ip.h>
# include <net/xfrm.h>
# include <net/esp.h>
2007-10-26 22:53:58 -07:00
# include <linux/scatterlist.h>
2005-10-10 21:11:08 -07:00
# include <linux/kernel.h>
2005-04-16 15:20:36 -07:00
# include <linux/pfkeyv2.h>
2008-01-28 19:35:05 -08:00
# include <linux/rtnetlink.h>
# include <linux/slab.h>
2007-10-09 13:33:35 -07:00
# include <linux/spinlock.h>
2007-12-10 16:53:05 -08:00
# include <linux/in6.h>
2005-04-16 15:20:36 -07:00
# include <net/icmp.h>
2005-12-27 02:43:12 -02:00
# include <net/protocol.h>
2005-04-16 15:20:36 -07:00
# include <net/udp.h>
2008-01-28 19:35:05 -08:00
struct esp_skb_cb {
struct xfrm_skb_cb xfrm ;
void * tmp ;
} ;
# define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0]))
2010-12-08 04:37:50 +00:00
static u32 esp4_get_mtu ( struct xfrm_state * x , int mtu ) ;
2008-01-28 19:35:05 -08:00
/*
* Allocate an AEAD request structure with extra space for SG and IV .
*
* For alignment considerations the IV is placed at the front , followed
* by the request and finally the SG list .
*
* TODO : Use spare space in skb for this where possible .
*/
2011-03-08 00:07:14 +00:00
static void * esp_alloc_tmp ( struct crypto_aead * aead , int nfrags , int seqhilen )
2008-01-28 19:35:05 -08:00
{
unsigned int len ;
2011-03-08 00:07:14 +00:00
len = seqhilen ;
len + = crypto_aead_ivsize ( aead ) ;
2008-01-28 19:35:05 -08:00
if ( len ) {
len + = crypto_aead_alignmask ( aead ) &
~ ( crypto_tfm_ctx_alignment ( ) - 1 ) ;
len = ALIGN ( len , crypto_tfm_ctx_alignment ( ) ) ;
}
len + = sizeof ( struct aead_givcrypt_request ) + crypto_aead_reqsize ( aead ) ;
len = ALIGN ( len , __alignof__ ( struct scatterlist ) ) ;
len + = sizeof ( struct scatterlist ) * nfrags ;
return kmalloc ( len , GFP_ATOMIC ) ;
}
2011-03-08 00:07:14 +00:00
static inline __be32 * esp_tmp_seqhi ( void * tmp )
{
return PTR_ALIGN ( ( __be32 * ) tmp , __alignof__ ( __be32 ) ) ;
}
static inline u8 * esp_tmp_iv ( struct crypto_aead * aead , void * tmp , int seqhilen )
2008-01-28 19:35:05 -08:00
{
return crypto_aead_ivsize ( aead ) ?
2011-03-08 00:07:14 +00:00
PTR_ALIGN ( ( u8 * ) tmp + seqhilen ,
crypto_aead_alignmask ( aead ) + 1 ) : tmp + seqhilen ;
2008-01-28 19:35:05 -08:00
}
static inline struct aead_givcrypt_request * esp_tmp_givreq (
struct crypto_aead * aead , u8 * iv )
{
struct aead_givcrypt_request * req ;
req = ( void * ) PTR_ALIGN ( iv + crypto_aead_ivsize ( aead ) ,
crypto_tfm_ctx_alignment ( ) ) ;
aead_givcrypt_set_tfm ( req , aead ) ;
return req ;
}
static inline struct aead_request * esp_tmp_req ( struct crypto_aead * aead , u8 * iv )
{
struct aead_request * req ;
req = ( void * ) PTR_ALIGN ( iv + crypto_aead_ivsize ( aead ) ,
crypto_tfm_ctx_alignment ( ) ) ;
aead_request_set_tfm ( req , aead ) ;
return req ;
}
static inline struct scatterlist * esp_req_sg ( struct crypto_aead * aead ,
struct aead_request * req )
{
return ( void * ) ALIGN ( ( unsigned long ) ( req + 1 ) +
crypto_aead_reqsize ( aead ) ,
__alignof__ ( struct scatterlist ) ) ;
}
static inline struct scatterlist * esp_givreq_sg (
struct crypto_aead * aead , struct aead_givcrypt_request * req )
{
return ( void * ) ALIGN ( ( unsigned long ) ( req + 1 ) +
crypto_aead_reqsize ( aead ) ,
__alignof__ ( struct scatterlist ) ) ;
}
static void esp_output_done ( struct crypto_async_request * base , int err )
{
struct sk_buff * skb = base - > data ;
kfree ( ESP_SKB_CB ( skb ) - > tmp ) ;
xfrm_output_resume ( skb , err ) ;
}
2005-04-16 15:20:36 -07:00
static int esp_output ( struct xfrm_state * x , struct sk_buff * skb )
{
int err ;
struct ip_esp_hdr * esph ;
2008-01-28 19:35:05 -08:00
struct crypto_aead * aead ;
struct aead_givcrypt_request * req ;
struct scatterlist * sg ;
struct scatterlist * asg ;
2005-04-16 15:20:36 -07:00
struct esp_data * esp ;
struct sk_buff * trailer ;
2008-01-28 19:35:05 -08:00
void * tmp ;
u8 * iv ;
2007-04-19 20:29:13 -07:00
u8 * tail ;
2005-04-16 15:20:36 -07:00
int blksize ;
int clen ;
int alen ;
2010-12-08 04:37:50 +00:00
int plen ;
int tfclen ;
2005-04-16 15:20:36 -07:00
int nfrags ;
2011-03-08 00:07:14 +00:00
int assoclen ;
int sglists ;
int seqhilen ;
__be32 * seqhi ;
2005-04-16 15:20:36 -07:00
2007-10-10 15:44:06 -07:00
/* skb is pure payload to encrypt */
2005-04-16 15:20:36 -07:00
err = - ENOMEM ;
esp = x - > data ;
2008-01-28 19:35:05 -08:00
aead = esp - > aead ;
alen = crypto_aead_authsize ( aead ) ;
2010-12-08 04:37:50 +00:00
tfclen = 0 ;
if ( x - > tfcpad ) {
struct xfrm_dst * dst = ( struct xfrm_dst * ) skb_dst ( skb ) ;
u32 padto ;
padto = min ( x - > tfcpad , esp4_get_mtu ( x , dst - > child_mtu_cached ) ) ;
if ( skb - > len < padto )
tfclen = padto - skb - > len ;
}
2008-01-28 19:35:05 -08:00
blksize = ALIGN ( crypto_aead_blocksize ( aead ) , 4 ) ;
2010-12-08 04:37:50 +00:00
clen = ALIGN ( skb - > len + 2 + tfclen , blksize ) ;
2008-01-28 19:35:05 -08:00
if ( esp - > padlen )
clen = ALIGN ( clen , esp - > padlen ) ;
2010-12-08 04:37:50 +00:00
plen = clen - skb - > len - tfclen ;
2008-01-28 19:35:05 -08:00
2010-12-08 04:37:50 +00:00
err = skb_cow_data ( skb , tfclen + plen + alen , & trailer ) ;
if ( err < 0 )
2008-01-28 19:35:05 -08:00
goto error ;
nfrags = err ;
2005-04-16 15:20:36 -07:00
2011-03-08 00:07:14 +00:00
assoclen = sizeof ( * esph ) ;
sglists = 1 ;
seqhilen = 0 ;
if ( x - > props . flags & XFRM_STATE_ESN ) {
sglists + = 2 ;
seqhilen + = sizeof ( __be32 ) ;
assoclen + = seqhilen ;
}
tmp = esp_alloc_tmp ( aead , nfrags + sglists , seqhilen ) ;
2008-01-28 19:35:05 -08:00
if ( ! tmp )
2005-04-16 15:20:36 -07:00
goto error ;
2011-03-08 00:07:14 +00:00
seqhi = esp_tmp_seqhi ( tmp ) ;
iv = esp_tmp_iv ( aead , tmp , seqhilen ) ;
2008-01-28 19:35:05 -08:00
req = esp_tmp_givreq ( aead , iv ) ;
asg = esp_givreq_sg ( aead , req ) ;
2011-03-08 00:07:14 +00:00
sg = asg + sglists ;
2008-01-28 19:35:05 -08:00
2005-04-16 15:20:36 -07:00
/* Fill padding... */
2007-04-19 20:29:13 -07:00
tail = skb_tail_pointer ( trailer ) ;
2010-12-08 04:37:50 +00:00
if ( tfclen ) {
memset ( tail , 0 , tfclen ) ;
tail + = tfclen ;
}
2005-04-16 15:20:36 -07:00
do {
int i ;
2010-12-08 04:37:50 +00:00
for ( i = 0 ; i < plen - 2 ; i + + )
2007-04-19 20:29:13 -07:00
tail [ i ] = i + 1 ;
2005-04-16 15:20:36 -07:00
} while ( 0 ) ;
2010-12-08 04:37:50 +00:00
tail [ plen - 2 ] = plen - 2 ;
tail [ plen - 1 ] = * skb_mac_header ( skb ) ;
2008-01-28 19:35:05 -08:00
pskb_put ( skb , trailer , clen - skb - > len + alen ) ;
2005-04-16 15:20:36 -07:00
2007-10-10 15:44:06 -07:00
skb_push ( skb , - skb_network_offset ( skb ) ) ;
2007-10-10 15:45:25 -07:00
esph = ip_esp_hdr ( skb ) ;
2007-10-10 15:44:44 -07:00
* skb_mac_header ( skb ) = IPPROTO_ESP ;
2005-04-16 15:20:36 -07:00
/* this is non-NULL only with UDP Encapsulation */
if ( x - > encap ) {
struct xfrm_encap_tmpl * encap = x - > encap ;
struct udphdr * uh ;
2006-11-08 00:23:14 -08:00
__be32 * udpdata32 ;
2008-03-17 22:50:23 -07:00
__be16 sport , dport ;
2008-01-28 19:35:05 -08:00
int encap_type ;
spin_lock_bh ( & x - > lock ) ;
sport = encap - > encap_sport ;
dport = encap - > encap_dport ;
encap_type = encap - > encap_type ;
spin_unlock_bh ( & x - > lock ) ;
2005-04-16 15:20:36 -07:00
uh = ( struct udphdr * ) esph ;
2008-01-28 19:35:05 -08:00
uh - > source = sport ;
uh - > dest = dport ;
uh - > len = htons ( skb - > len - skb_transport_offset ( skb ) ) ;
2005-04-16 15:20:36 -07:00
uh - > check = 0 ;
2008-01-28 19:35:05 -08:00
switch ( encap_type ) {
2005-04-16 15:20:36 -07:00
default :
case UDP_ENCAP_ESPINUDP :
esph = ( struct ip_esp_hdr * ) ( uh + 1 ) ;
break ;
case UDP_ENCAP_ESPINUDP_NON_IKE :
2006-11-08 00:23:14 -08:00
udpdata32 = ( __be32 * ) ( uh + 1 ) ;
2005-04-16 15:20:36 -07:00
udpdata32 [ 0 ] = udpdata32 [ 1 ] = 0 ;
esph = ( struct ip_esp_hdr * ) ( udpdata32 + 2 ) ;
break ;
}
2007-10-10 15:44:44 -07:00
* skb_mac_header ( skb ) = IPPROTO_UDP ;
}
2005-04-16 15:20:36 -07:00
esph - > spi = x - > id . spi ;
2011-03-08 00:06:31 +00:00
esph - > seq_no = htonl ( XFRM_SKB_CB ( skb ) - > seq . output . low ) ;
2005-04-16 15:20:36 -07:00
2008-01-28 19:35:05 -08:00
sg_init_table ( sg , nfrags ) ;
skb_to_sgvec ( skb , sg ,
esph - > enc_data + crypto_aead_ivsize ( aead ) - skb - > data ,
clen + alen ) ;
2011-03-08 00:07:14 +00:00
if ( ( x - > props . flags & XFRM_STATE_ESN ) ) {
sg_init_table ( asg , 3 ) ;
sg_set_buf ( asg , & esph - > spi , sizeof ( __be32 ) ) ;
* seqhi = htonl ( XFRM_SKB_CB ( skb ) - > seq . output . hi ) ;
sg_set_buf ( asg + 1 , seqhi , seqhilen ) ;
sg_set_buf ( asg + 2 , & esph - > seq_no , sizeof ( __be32 ) ) ;
} else
sg_init_one ( asg , esph , sizeof ( * esph ) ) ;
2008-01-28 19:35:05 -08:00
aead_givcrypt_set_callback ( req , 0 , esp_output_done , skb ) ;
aead_givcrypt_set_crypt ( req , sg , sg , clen , iv ) ;
2011-03-08 00:07:14 +00:00
aead_givcrypt_set_assoc ( req , asg , assoclen ) ;
2008-02-12 22:50:35 -08:00
aead_givcrypt_set_giv ( req , esph - > enc_data ,
2011-03-08 00:06:31 +00:00
XFRM_SKB_CB ( skb ) - > seq . output . low ) ;
2008-01-28 19:35:05 -08:00
ESP_SKB_CB ( skb ) - > tmp = tmp ;
err = crypto_aead_givencrypt ( req ) ;
if ( err = = - EINPROGRESS )
goto error ;
2005-04-16 15:20:36 -07:00
2008-01-28 19:35:05 -08:00
if ( err = = - EBUSY )
err = NET_XMIT_DROP ;
2005-04-16 15:20:36 -07:00
2008-01-28 19:35:05 -08:00
kfree ( tmp ) ;
2007-10-09 13:33:35 -07:00
2005-04-16 15:20:36 -07:00
error :
return err ;
}
2008-01-28 19:35:05 -08:00
static int esp_input_done2 ( struct sk_buff * skb , int err )
2005-04-16 15:20:36 -07:00
{
2011-04-22 04:53:02 +00:00
const struct iphdr * iph ;
2008-01-28 19:35:05 -08:00
struct xfrm_state * x = xfrm_input_state ( skb ) ;
2005-04-16 15:20:36 -07:00
struct esp_data * esp = x - > data ;
2008-01-28 19:35:05 -08:00
struct crypto_aead * aead = esp - > aead ;
int alen = crypto_aead_authsize ( aead ) ;
int hlen = sizeof ( struct ip_esp_hdr ) + crypto_aead_ivsize ( aead ) ;
int elen = skb - > len - hlen ;
2006-05-27 23:06:13 -07:00
int ihl ;
2006-02-27 13:00:01 -08:00
u8 nexthdr [ 2 ] ;
int padlen ;
2005-04-16 15:20:36 -07:00
2008-01-28 19:35:05 -08:00
kfree ( ESP_SKB_CB ( skb ) - > tmp ) ;
2007-11-13 21:45:58 -08:00
2006-07-30 15:41:01 +10:00
if ( unlikely ( err ) )
2007-12-16 15:55:02 -08:00
goto out ;
2005-04-16 15:20:36 -07:00
2006-02-27 13:00:01 -08:00
if ( skb_copy_bits ( skb , skb - > len - alen - 2 , nexthdr , 2 ) )
BUG ( ) ;
2005-04-16 15:20:36 -07:00
2007-12-16 15:55:02 -08:00
err = - EINVAL ;
2006-02-27 13:00:01 -08:00
padlen = nexthdr [ 0 ] ;
2008-01-28 19:35:05 -08:00
if ( padlen + 2 + alen > = elen )
2006-02-27 13:00:01 -08:00
goto out ;
2005-04-16 15:20:36 -07:00
2007-02-09 23:24:47 +09:00
/* ... check padding bits here. Silly. :-) */
2005-04-16 15:20:36 -07:00
2007-04-20 22:47:35 -07:00
iph = ip_hdr ( skb ) ;
2006-05-27 23:06:13 -07:00
ihl = iph - > ihl * 4 ;
2006-02-27 13:00:40 -08:00
if ( x - > encap ) {
struct xfrm_encap_tmpl * encap = x - > encap ;
2007-04-10 20:50:43 -07:00
struct udphdr * uh = ( void * ) ( skb_network_header ( skb ) + ihl ) ;
2006-02-27 13:00:40 -08:00
/*
* 1 ) if the NAT - T peer ' s IP or port changed then
* advertize the change to the keying daemon .
* This is an inbound SA , so just compare
* SRC ports .
*/
if ( iph - > saddr ! = x - > props . saddr . a4 | |
uh - > source ! = encap - > encap_sport ) {
xfrm_address_t ipaddr ;
ipaddr . a4 = iph - > saddr ;
km_new_mapping ( x , & ipaddr , uh - > source ) ;
2007-02-09 23:24:47 +09:00
2006-02-27 13:00:40 -08:00
/* XXX: perhaps add an extra
* policy check here , to see
* if we should allow or
* reject a packet from a
* different source
* address / port .
*/
2005-04-16 15:20:36 -07:00
}
2007-02-09 23:24:47 +09:00
2006-02-27 13:00:40 -08:00
/*
* 2 ) ignore UDP / TCP checksums in case
* of NAT - T in Transport Mode , or
* perform other post - processing fixes
* as per draft - ietf - ipsec - udp - encaps - 06 ,
* section 3.1 .2
*/
2007-10-10 15:41:41 -07:00
if ( x - > props . mode = = XFRM_MODE_TRANSPORT )
2006-02-27 13:00:40 -08:00
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
2005-04-16 15:20:36 -07:00
}
2006-02-27 13:00:01 -08:00
pskb_trim ( skb , skb - > len - alen - padlen - 2 ) ;
2008-01-28 19:35:05 -08:00
__skb_pull ( skb , hlen ) ;
2012-12-28 16:07:16 +08:00
if ( x - > props . mode = = XFRM_MODE_TUNNEL )
skb_reset_transport_header ( skb ) ;
else
skb_set_transport_header ( skb , - ihl ) ;
2006-02-27 13:00:01 -08:00
2008-01-28 19:35:05 -08:00
err = nexthdr [ 1 ] ;
/* RFC4303: Drop dummy packets without any error */
if ( err = = IPPROTO_NONE )
err = - EINVAL ;
out :
return err ;
}
static void esp_input_done ( struct crypto_async_request * base , int err )
{
struct sk_buff * skb = base - > data ;
xfrm_input_resume ( skb , esp_input_done2 ( skb , err ) ) ;
}
/*
* Note : detecting truncated vs . non - truncated authentication data is very
* expensive , so we only support truncated data , which is the recommended
* and common case .
*/
static int esp_input ( struct xfrm_state * x , struct sk_buff * skb )
{
struct ip_esp_hdr * esph ;
struct esp_data * esp = x - > data ;
struct crypto_aead * aead = esp - > aead ;
struct aead_request * req ;
struct sk_buff * trailer ;
int elen = skb - > len - sizeof ( * esph ) - crypto_aead_ivsize ( aead ) ;
int nfrags ;
2011-03-08 00:07:14 +00:00
int assoclen ;
int sglists ;
int seqhilen ;
__be32 * seqhi ;
2008-01-28 19:35:05 -08:00
void * tmp ;
u8 * iv ;
struct scatterlist * sg ;
struct scatterlist * asg ;
int err = - EINVAL ;
2008-03-27 16:08:03 -07:00
if ( ! pskb_may_pull ( skb , sizeof ( * esph ) + crypto_aead_ivsize ( aead ) ) )
2008-01-28 19:35:05 -08:00
goto out ;
if ( elen < = 0 )
goto out ;
if ( ( err = skb_cow_data ( skb , 0 , & trailer ) ) < 0 )
goto out ;
nfrags = err ;
2011-03-08 00:07:14 +00:00
assoclen = sizeof ( * esph ) ;
sglists = 1 ;
seqhilen = 0 ;
if ( x - > props . flags & XFRM_STATE_ESN ) {
sglists + = 2 ;
seqhilen + = sizeof ( __be32 ) ;
assoclen + = seqhilen ;
}
2008-01-28 19:35:05 -08:00
err = - ENOMEM ;
2011-03-08 00:07:14 +00:00
tmp = esp_alloc_tmp ( aead , nfrags + sglists , seqhilen ) ;
2008-01-28 19:35:05 -08:00
if ( ! tmp )
goto out ;
ESP_SKB_CB ( skb ) - > tmp = tmp ;
2011-03-08 00:07:14 +00:00
seqhi = esp_tmp_seqhi ( tmp ) ;
iv = esp_tmp_iv ( aead , tmp , seqhilen ) ;
2008-01-28 19:35:05 -08:00
req = esp_tmp_req ( aead , iv ) ;
asg = esp_req_sg ( aead , req ) ;
2011-03-08 00:07:14 +00:00
sg = asg + sglists ;
2008-01-28 19:35:05 -08:00
skb - > ip_summed = CHECKSUM_NONE ;
esph = ( struct ip_esp_hdr * ) skb - > data ;
/* Get ivec. This can be wrong, check against another impls. */
iv = esph - > enc_data ;
sg_init_table ( sg , nfrags ) ;
skb_to_sgvec ( skb , sg , sizeof ( * esph ) + crypto_aead_ivsize ( aead ) , elen ) ;
2011-03-08 00:07:14 +00:00
if ( ( x - > props . flags & XFRM_STATE_ESN ) ) {
sg_init_table ( asg , 3 ) ;
sg_set_buf ( asg , & esph - > spi , sizeof ( __be32 ) ) ;
* seqhi = XFRM_SKB_CB ( skb ) - > seq . input . hi ;
sg_set_buf ( asg + 1 , seqhi , seqhilen ) ;
sg_set_buf ( asg + 2 , & esph - > seq_no , sizeof ( __be32 ) ) ;
} else
sg_init_one ( asg , esph , sizeof ( * esph ) ) ;
2008-01-28 19:35:05 -08:00
aead_request_set_callback ( req , 0 , esp_input_done , skb ) ;
aead_request_set_crypt ( req , sg , sg , elen , iv ) ;
2011-03-08 00:07:14 +00:00
aead_request_set_assoc ( req , asg , assoclen ) ;
2008-01-28 19:35:05 -08:00
err = crypto_aead_decrypt ( req ) ;
if ( err = = - EINPROGRESS )
goto out ;
err = esp_input_done2 ( skb , err ) ;
2005-04-16 15:20:36 -07:00
out :
2007-12-16 15:55:02 -08:00
return err ;
2005-04-16 15:20:36 -07:00
}
2007-04-09 11:47:18 -07:00
static u32 esp4_get_mtu ( struct xfrm_state * x , int mtu )
2005-04-16 15:20:36 -07:00
{
struct esp_data * esp = x - > data ;
2008-01-28 19:35:05 -08:00
u32 blksize = ALIGN ( crypto_aead_blocksize ( esp - > aead ) , 4 ) ;
u32 align = max_t ( u32 , blksize , esp - > padlen ) ;
2012-05-24 11:32:38 +00:00
unsigned int net_adj ;
2006-10-03 23:47:05 -07:00
switch ( x - > props . mode ) {
case XFRM_MODE_TRANSPORT :
case XFRM_MODE_BEET :
2012-05-24 11:32:38 +00:00
net_adj = sizeof ( struct iphdr ) ;
2006-10-03 23:47:05 -07:00
break ;
2012-05-24 11:32:38 +00:00
case XFRM_MODE_TUNNEL :
net_adj = 0 ;
break ;
default :
BUG ( ) ;
2005-04-16 15:20:36 -07:00
}
2006-10-03 23:47:05 -07:00
2012-05-24 11:32:38 +00:00
return ( ( mtu - x - > props . header_len - crypto_aead_authsize ( esp - > aead ) -
net_adj ) & ~ ( align - 1 ) ) + ( net_adj - 2 ) ;
2005-04-16 15:20:36 -07:00
}
static void esp4_err ( struct sk_buff * skb , u32 info )
{
2008-11-25 17:59:27 -08:00
struct net * net = dev_net ( skb - > dev ) ;
2011-04-22 04:53:02 +00:00
const struct iphdr * iph = ( const struct iphdr * ) skb - > data ;
2008-11-03 00:23:42 -08:00
struct ip_esp_hdr * esph = ( struct ip_esp_hdr * ) ( skb - > data + ( iph - > ihl < < 2 ) ) ;
2005-04-16 15:20:36 -07:00
struct xfrm_state * x ;
2012-07-11 21:27:49 -07:00
switch ( icmp_hdr ( skb ) - > type ) {
case ICMP_DEST_UNREACH :
if ( icmp_hdr ( skb ) - > code ! = ICMP_FRAG_NEEDED )
return ;
case ICMP_REDIRECT :
break ;
default :
2005-04-16 15:20:36 -07:00
return ;
2012-07-11 21:27:49 -07:00
}
2005-04-16 15:20:36 -07:00
2011-04-22 04:53:02 +00:00
x = xfrm_state_lookup ( net , skb - > mark , ( const xfrm_address_t * ) & iph - > daddr ,
esph - > spi , IPPROTO_ESP , AF_INET ) ;
2005-04-16 15:20:36 -07:00
if ( ! x )
return ;
2012-07-11 21:27:49 -07:00
2013-01-15 13:38:53 +01:00
if ( icmp_hdr ( skb ) - > type = = ICMP_DEST_UNREACH ) {
atomic_inc ( & flow_cache_genid ) ;
rt_genid_bump ( net ) ;
2012-07-11 21:27:49 -07:00
ipv4_update_pmtu ( skb , net , info , 0 , 0 , IPPROTO_ESP , 0 ) ;
2013-01-15 13:38:53 +01:00
} else
2012-07-11 21:27:49 -07:00
ipv4_redirect ( skb , net , 0 , 0 , IPPROTO_ESP , 0 ) ;
2005-04-16 15:20:36 -07:00
xfrm_state_put ( x ) ;
}
static void esp_destroy ( struct xfrm_state * x )
{
struct esp_data * esp = x - > data ;
if ( ! esp )
return ;
2008-01-28 19:35:05 -08:00
crypto_free_aead ( esp - > aead ) ;
2005-04-16 15:20:36 -07:00
kfree ( esp ) ;
}
2008-01-28 19:37:29 -08:00
static int esp_init_aead ( struct xfrm_state * x )
2005-04-16 15:20:36 -07:00
{
2008-01-28 19:37:29 -08:00
struct esp_data * esp = x - > data ;
struct crypto_aead * aead ;
int err ;
aead = crypto_alloc_aead ( x - > aead - > alg_name , 0 , 0 ) ;
err = PTR_ERR ( aead ) ;
if ( IS_ERR ( aead ) )
goto error ;
esp - > aead = aead ;
err = crypto_aead_setkey ( aead , x - > aead - > alg_key ,
( x - > aead - > alg_key_len + 7 ) / 8 ) ;
if ( err )
goto error ;
err = crypto_aead_setauthsize ( aead , x - > aead - > alg_icv_len / 8 ) ;
if ( err )
goto error ;
error :
return err ;
}
static int esp_init_authenc ( struct xfrm_state * x )
{
struct esp_data * esp = x - > data ;
2008-01-28 19:35:05 -08:00
struct crypto_aead * aead ;
struct crypto_authenc_key_param * param ;
struct rtattr * rta ;
char * key ;
char * p ;
char authenc_name [ CRYPTO_MAX_ALG_NAME ] ;
unsigned int keylen ;
int err ;
2005-04-16 15:20:36 -07:00
2008-01-28 19:37:29 -08:00
err = - EINVAL ;
2005-04-16 15:20:36 -07:00
if ( x - > ealg = = NULL )
2008-01-28 19:37:29 -08:00
goto error ;
2008-01-28 19:35:05 -08:00
2008-01-28 19:37:29 -08:00
err = - ENAMETOOLONG ;
2011-03-08 00:07:14 +00:00
if ( ( x - > props . flags & XFRM_STATE_ESN ) ) {
if ( snprintf ( authenc_name , CRYPTO_MAX_ALG_NAME ,
" authencesn(%s,%s) " ,
x - > aalg ? x - > aalg - > alg_name : " digest_null " ,
x - > ealg - > alg_name ) > = CRYPTO_MAX_ALG_NAME )
goto error ;
} else {
if ( snprintf ( authenc_name , CRYPTO_MAX_ALG_NAME ,
" authenc(%s,%s) " ,
x - > aalg ? x - > aalg - > alg_name : " digest_null " ,
x - > ealg - > alg_name ) > = CRYPTO_MAX_ALG_NAME )
goto error ;
}
2008-01-28 19:35:05 -08:00
aead = crypto_alloc_aead ( authenc_name , 0 , 0 ) ;
err = PTR_ERR ( aead ) ;
if ( IS_ERR ( aead ) )
goto error ;
esp - > aead = aead ;
keylen = ( x - > aalg ? ( x - > aalg - > alg_key_len + 7 ) / 8 : 0 ) +
( x - > ealg - > alg_key_len + 7 ) / 8 + RTA_SPACE ( sizeof ( * param ) ) ;
err = - ENOMEM ;
key = kmalloc ( keylen , GFP_KERNEL ) ;
if ( ! key )
goto error ;
p = key ;
rta = ( void * ) p ;
rta - > rta_type = CRYPTO_AUTHENC_KEYA_PARAM ;
rta - > rta_len = RTA_LENGTH ( sizeof ( * param ) ) ;
param = RTA_DATA ( rta ) ;
p + = RTA_SPACE ( sizeof ( * param ) ) ;
2005-04-16 15:20:36 -07:00
if ( x - > aalg ) {
struct xfrm_algo_desc * aalg_desc ;
2008-01-28 19:35:05 -08:00
memcpy ( p , x - > aalg - > alg_key , ( x - > aalg - > alg_key_len + 7 ) / 8 ) ;
p + = ( x - > aalg - > alg_key_len + 7 ) / 8 ;
2005-04-16 15:20:36 -07:00
aalg_desc = xfrm_aalg_get_byname ( x - > aalg - > alg_name , 0 ) ;
BUG_ON ( ! aalg_desc ) ;
2008-01-28 19:35:05 -08:00
err = - EINVAL ;
2005-04-16 15:20:36 -07:00
if ( aalg_desc - > uinfo . auth . icv_fullbits / 8 ! =
2008-01-28 19:35:05 -08:00
crypto_aead_authsize ( aead ) ) {
2005-08-09 20:50:53 -07:00
NETDEBUG ( KERN_INFO " ESP: %s digestsize %u != %hu \n " ,
x - > aalg - > alg_name ,
2008-01-28 19:35:05 -08:00
crypto_aead_authsize ( aead ) ,
2005-08-09 20:50:53 -07:00
aalg_desc - > uinfo . auth . icv_fullbits / 8 ) ;
2008-01-28 19:35:05 -08:00
goto free_key ;
2005-04-16 15:20:36 -07:00
}
2008-01-28 19:35:05 -08:00
err = crypto_aead_setauthsize (
2009-11-25 00:29:53 +00:00
aead , x - > aalg - > alg_trunc_len / 8 ) ;
2008-01-28 19:35:05 -08:00
if ( err )
goto free_key ;
2005-04-16 15:20:36 -07:00
}
2007-10-08 17:13:44 -07:00
2008-01-28 19:35:05 -08:00
param - > enckeylen = cpu_to_be32 ( ( x - > ealg - > alg_key_len + 7 ) / 8 ) ;
memcpy ( p , x - > ealg - > alg_key , ( x - > ealg - > alg_key_len + 7 ) / 8 ) ;
err = crypto_aead_setkey ( aead , key , keylen ) ;
free_key :
kfree ( key ) ;
2008-01-28 19:37:29 -08:00
error :
return err ;
}
static int esp_init_state ( struct xfrm_state * x )
{
struct esp_data * esp ;
struct crypto_aead * aead ;
u32 align ;
int err ;
esp = kzalloc ( sizeof ( * esp ) , GFP_KERNEL ) ;
if ( esp = = NULL )
return - ENOMEM ;
x - > data = esp ;
if ( x - > aead )
err = esp_init_aead ( x ) ;
else
err = esp_init_authenc ( x ) ;
2008-01-28 19:35:05 -08:00
if ( err )
2005-04-16 15:20:36 -07:00
goto error ;
2008-01-28 19:35:05 -08:00
2008-01-28 19:37:29 -08:00
aead = esp - > aead ;
esp - > padlen = 0 ;
2008-01-28 19:35:05 -08:00
x - > props . header_len = sizeof ( struct ip_esp_hdr ) +
crypto_aead_ivsize ( aead ) ;
2006-09-22 15:05:15 -07:00
if ( x - > props . mode = = XFRM_MODE_TUNNEL )
2005-04-16 15:20:36 -07:00
x - > props . header_len + = sizeof ( struct iphdr ) ;
2008-08-06 02:39:30 -07:00
else if ( x - > props . mode = = XFRM_MODE_BEET & & x - > sel . family ! = AF_INET6 )
2007-04-09 11:47:58 -07:00
x - > props . header_len + = IPV4_BEET_PHMAXLEN ;
2005-04-16 15:20:36 -07:00
if ( x - > encap ) {
struct xfrm_encap_tmpl * encap = x - > encap ;
switch ( encap - > encap_type ) {
default :
goto error ;
case UDP_ENCAP_ESPINUDP :
x - > props . header_len + = sizeof ( struct udphdr ) ;
break ;
case UDP_ENCAP_ESPINUDP_NON_IKE :
x - > props . header_len + = sizeof ( struct udphdr ) + 2 * sizeof ( u32 ) ;
break ;
}
}
2008-01-28 19:35:05 -08:00
align = ALIGN ( crypto_aead_blocksize ( aead ) , 4 ) ;
if ( esp - > padlen )
align = max_t ( u32 , align , esp - > padlen ) ;
x - > props . trailer_len = align + 1 + crypto_aead_authsize ( esp - > aead ) ;
2005-04-16 15:20:36 -07:00
error :
2008-01-28 19:35:05 -08:00
return err ;
2005-04-16 15:20:36 -07:00
}
2008-01-30 19:11:50 -08:00
static const struct xfrm_type esp_type =
2005-04-16 15:20:36 -07:00
{
. description = " ESP4 " ,
. owner = THIS_MODULE ,
. proto = IPPROTO_ESP ,
2007-10-08 17:25:53 -07:00
. flags = XFRM_TYPE_REPLAY_PROT ,
2005-04-16 15:20:36 -07:00
. init_state = esp_init_state ,
. destructor = esp_destroy ,
2007-04-09 11:47:18 -07:00
. get_mtu = esp4_get_mtu ,
2005-04-16 15:20:36 -07:00
. input = esp_input ,
. output = esp_output
} ;
2009-09-14 12:21:47 +00:00
static const struct net_protocol esp4_protocol = {
2005-04-16 15:20:36 -07:00
. handler = xfrm4_rcv ,
. err_handler = esp4_err ,
. no_policy = 1 ,
2008-11-25 17:59:27 -08:00
. netns_ok = 1 ,
2005-04-16 15:20:36 -07:00
} ;
static int __init esp4_init ( void )
{
if ( xfrm_register_type ( & esp_type , AF_INET ) < 0 ) {
2012-03-11 18:36:11 +00:00
pr_info ( " %s: can't add xfrm type \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return - EAGAIN ;
}
if ( inet_add_protocol ( & esp4_protocol , IPPROTO_ESP ) < 0 ) {
2012-03-11 18:36:11 +00:00
pr_info ( " %s: can't add protocol \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
xfrm_unregister_type ( & esp_type , AF_INET ) ;
return - EAGAIN ;
}
return 0 ;
}
static void __exit esp4_fini ( void )
{
if ( inet_del_protocol ( & esp4_protocol , IPPROTO_ESP ) < 0 )
2012-03-11 18:36:11 +00:00
pr_info ( " %s: can't remove protocol \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
if ( xfrm_unregister_type ( & esp_type , AF_INET ) < 0 )
2012-03-11 18:36:11 +00:00
pr_info ( " %s: can't remove xfrm type \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( esp4_init ) ;
module_exit ( esp4_fini ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-06-26 23:57:49 -07:00
MODULE_ALIAS_XFRM_TYPE ( AF_INET , XFRM_PROTO_ESP ) ;