2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2002 USAGI / WIDE Project
2007-02-09 17:24:49 +03:00
*
2005-04-17 02:20:36 +04:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
2007-02-09 17:24:49 +03:00
*
2005-04-17 02:20:36 +04:00
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2007-02-09 17:24:49 +03:00
*
2005-04-17 02:20:36 +04:00
* You should have received a copy of the GNU General Public License
2013-12-06 21:13:39 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2005-04-17 02:20:36 +04:00
*
* Authors
*
2007-02-09 17:24:49 +03:00
* Mitsuru KANDA @ USAGI : IPv6 Support
2014-08-25 00:53:10 +04:00
* Kazunori MIYAZAWA @ USAGI :
* Kunihiro Ishiguro < kunihiro @ ipinfusion . com >
2007-02-09 17:24:49 +03:00
*
2014-08-25 00:53:10 +04:00
* This file is derived from net / ipv4 / esp . c
2005-04-17 02:20:36 +04:00
*/
2012-05-15 18:11:53 +04:00
# define pr_fmt(fmt) "IPv6: " fmt
2008-01-29 06:35:05 +03:00
# include <crypto/aead.h>
# include <crypto/authenc.h>
2006-07-30 09:41:01 +04:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <net/ip.h>
# include <net/xfrm.h>
# include <net/esp.h>
2007-10-27 09:53:58 +04:00
# include <linux/scatterlist.h>
2005-10-11 08:11:08 +04:00
# include <linux/kernel.h>
2005-04-17 02:20:36 +04:00
# include <linux/pfkeyv2.h>
# include <linux/random.h>
2008-01-29 06:35:05 +03:00
# include <linux/slab.h>
2007-10-10 00:33:35 +04:00
# include <linux/spinlock.h>
2012-06-16 01:54:11 +04:00
# include <net/ip6_route.h>
2005-04-17 02:20:36 +04:00
# include <net/icmp.h>
# include <net/ipv6.h>
2005-12-27 07:43:12 +03:00
# include <net/protocol.h>
2005-04-17 02:20:36 +04:00
# include <linux/icmpv6.h>
2017-01-17 12:23:03 +03:00
# include <linux/highmem.h>
2008-01-29 06:35:05 +03: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 07:37:51 +03:00
static u32 esp6_get_mtu ( struct xfrm_state * x , int mtu ) ;
2008-01-29 06:35:05 +03:00
/*
* Allocate an AEAD request structure with extra space for SG and IV .
*
2011-03-08 03:07:51 +03:00
* For alignment considerations the upper 32 bits of the sequence number are
* placed at the front , if present . Followed by the IV , the request and finally
* the SG list .
2008-01-29 06:35:05 +03:00
*
* TODO : Use spare space in skb for this where possible .
*/
2011-03-08 03:07:51 +03:00
static void * esp_alloc_tmp ( struct crypto_aead * aead , int nfrags , int seqihlen )
2008-01-29 06:35:05 +03:00
{
unsigned int len ;
2011-03-08 03:07:51 +03:00
len = seqihlen ;
len + = crypto_aead_ivsize ( aead ) ;
2008-01-29 06:35:05 +03:00
if ( len ) {
len + = crypto_aead_alignmask ( aead ) &
~ ( crypto_tfm_ctx_alignment ( ) - 1 ) ;
len = ALIGN ( len , crypto_tfm_ctx_alignment ( ) ) ;
}
2015-05-27 11:03:47 +03:00
len + = sizeof ( struct aead_request ) + crypto_aead_reqsize ( aead ) ;
2008-01-29 06:35:05 +03:00
len = ALIGN ( len , __alignof__ ( struct scatterlist ) ) ;
len + = sizeof ( struct scatterlist ) * nfrags ;
return kmalloc ( len , GFP_ATOMIC ) ;
}
2011-03-08 03:07:51 +03: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-29 06:35:05 +03:00
{
return crypto_aead_ivsize ( aead ) ?
2011-03-08 03:07:51 +03:00
PTR_ALIGN ( ( u8 * ) tmp + seqhilen ,
crypto_aead_alignmask ( aead ) + 1 ) : tmp + seqhilen ;
2008-01-29 06:35:05 +03:00
}
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 ) ) ;
}
2017-01-17 12:23:03 +03:00
static void esp_ssg_unref ( struct xfrm_state * x , void * tmp )
{
__be32 * seqhi ;
struct crypto_aead * aead = x - > data ;
int seqhilen = 0 ;
u8 * iv ;
struct aead_request * req ;
struct scatterlist * sg ;
if ( x - > props . flags & XFRM_STATE_ESN )
seqhilen + = sizeof ( __be32 ) ;
seqhi = esp_tmp_seqhi ( tmp ) ;
iv = esp_tmp_iv ( aead , tmp , seqhilen ) ;
req = esp_tmp_req ( aead , iv ) ;
/* Unref skb_frag_pages in the src scatterlist if necessary.
* Skip the first sg which comes from skb - > data .
*/
if ( req - > src ! = req - > dst )
for ( sg = sg_next ( req - > src ) ; sg ; sg = sg_next ( sg ) )
put_page ( sg_page ( sg ) ) ;
}
2008-01-29 06:35:05 +03:00
static void esp_output_done ( struct crypto_async_request * base , int err )
{
struct sk_buff * skb = base - > data ;
2017-01-17 12:23:03 +03:00
void * tmp ;
struct dst_entry * dst = skb_dst ( skb ) ;
struct xfrm_state * x = dst - > xfrm ;
2008-01-29 06:35:05 +03:00
2017-01-17 12:23:03 +03:00
tmp = ESP_SKB_CB ( skb ) - > tmp ;
esp_ssg_unref ( x , tmp ) ;
kfree ( tmp ) ;
2008-01-29 06:35:05 +03:00
xfrm_output_resume ( skb , err ) ;
}
2015-05-27 11:03:47 +03:00
/* Move ESP header back into place. */
static void esp_restore_header ( struct sk_buff * skb , unsigned int offset )
{
struct ip_esp_hdr * esph = ( void * ) ( skb - > data + offset ) ;
void * tmp = ESP_SKB_CB ( skb ) - > tmp ;
__be32 * seqhi = esp_tmp_seqhi ( tmp ) ;
esph - > seq_no = esph - > spi ;
esph - > spi = * seqhi ;
}
static void esp_output_restore_header ( struct sk_buff * skb )
{
esp_restore_header ( skb , skb_transport_offset ( skb ) - sizeof ( __be32 ) ) ;
}
2017-01-17 12:23:03 +03:00
static struct ip_esp_hdr * esp_output_set_esn ( struct sk_buff * skb ,
struct ip_esp_hdr * esph ,
__be32 * seqhi )
{
struct xfrm_state * x = skb_dst ( skb ) - > xfrm ;
/* For ESN we move the header forward by 4 bytes to
* accomodate the high bits . We will move it back after
* encryption .
*/
if ( ( x - > props . flags & XFRM_STATE_ESN ) ) {
esph = ( void * ) ( skb_transport_header ( skb ) - sizeof ( __be32 ) ) ;
* seqhi = esph - > spi ;
esph - > seq_no = htonl ( XFRM_SKB_CB ( skb ) - > seq . output . hi ) ;
}
esph - > spi = x - > id . spi ;
return esph ;
}
2015-05-27 11:03:47 +03:00
static void esp_output_done_esn ( struct crypto_async_request * base , int err )
{
struct sk_buff * skb = base - > data ;
esp_output_restore_header ( skb ) ;
esp_output_done ( base , err ) ;
}
2017-01-17 12:23:08 +03:00
static void esp_output_fill_trailer ( u8 * tail , int tfclen , int plen , __u8 proto )
{
/* Fill padding... */
if ( tfclen ) {
memset ( tail , 0 , tfclen ) ;
tail + = tfclen ;
}
do {
int i ;
for ( i = 0 ; i < plen - 2 ; i + + )
tail [ i ] = i + 1 ;
} while ( 0 ) ;
tail [ plen - 2 ] = plen - 2 ;
tail [ plen - 1 ] = proto ;
}
2005-04-17 02:20:36 +04:00
static int esp6_output ( struct xfrm_state * x , struct sk_buff * skb )
{
int err ;
2007-10-11 02:45:25 +04:00
struct ip_esp_hdr * esph ;
2008-01-29 06:35:05 +03:00
struct crypto_aead * aead ;
2015-05-27 11:03:47 +03:00
struct aead_request * req ;
2017-01-17 12:23:03 +03:00
struct scatterlist * sg , * dsg ;
2005-04-17 02:20:36 +04:00
struct sk_buff * trailer ;
2017-01-17 12:23:03 +03:00
struct page * page ;
2008-01-29 06:35:05 +03:00
void * tmp ;
2005-04-17 02:20:36 +04:00
int blksize ;
int clen ;
int alen ;
2010-12-08 07:37:51 +03:00
int plen ;
2015-05-27 11:03:47 +03:00
int ivlen ;
2010-12-08 07:37:51 +03:00
int tfclen ;
2005-04-17 02:20:36 +04:00
int nfrags ;
2011-03-08 03:07:51 +03:00
int assoclen ;
int seqhilen ;
2017-01-17 12:23:03 +03:00
int tailen ;
2008-01-29 06:35:05 +03:00
u8 * iv ;
2007-04-20 07:29:13 +04:00
u8 * tail ;
2017-01-17 12:23:03 +03:00
u8 * vaddr ;
2011-03-08 03:07:51 +03:00
__be32 * seqhi ;
2015-05-27 11:03:47 +03:00
__be64 seqno ;
2017-01-17 12:23:03 +03:00
__u8 proto = * skb_mac_header ( skb ) ;
2005-04-17 02:20:36 +04:00
2007-10-11 02:44:06 +04:00
/* skb is pure payload to encrypt */
2013-10-18 14:09:05 +04:00
aead = x - > data ;
2008-01-29 06:35:05 +03:00
alen = crypto_aead_authsize ( aead ) ;
2015-05-27 11:03:47 +03:00
ivlen = crypto_aead_ivsize ( aead ) ;
2008-01-29 06:35:05 +03:00
2010-12-08 07:37:51 +03:00
tfclen = 0 ;
if ( x - > tfcpad ) {
struct xfrm_dst * dst = ( struct xfrm_dst * ) skb_dst ( skb ) ;
u32 padto ;
padto = min ( x - > tfcpad , esp6_get_mtu ( x , dst - > child_mtu_cached ) ) ;
if ( skb - > len < padto )
tfclen = padto - skb - > len ;
}
2008-01-29 06:35:05 +03:00
blksize = ALIGN ( crypto_aead_blocksize ( aead ) , 4 ) ;
2010-12-08 07:37:51 +03:00
clen = ALIGN ( skb - > len + 2 + tfclen , blksize ) ;
plen = clen - skb - > len - tfclen ;
2017-01-17 12:23:03 +03:00
tailen = tfclen + plen + alen ;
2008-01-29 06:35:05 +03:00
2011-03-08 03:07:51 +03:00
assoclen = sizeof ( * esph ) ;
seqhilen = 0 ;
if ( x - > props . flags & XFRM_STATE_ESN ) {
seqhilen + = sizeof ( __be32 ) ;
assoclen + = seqhilen ;
}
2017-01-17 12:23:03 +03:00
* skb_mac_header ( skb ) = IPPROTO_ESP ;
esph = ip_esp_hdr ( skb ) ;
if ( ! skb_cloned ( skb ) ) {
if ( tailen < = skb_availroom ( skb ) ) {
nfrags = 1 ;
trailer = skb ;
tail = skb_tail_pointer ( trailer ) ;
goto skip_cow ;
} else if ( ( skb_shinfo ( skb ) - > nr_frags < MAX_SKB_FRAGS )
& & ! skb_has_frag_list ( skb ) ) {
int allocsize ;
struct sock * sk = skb - > sk ;
struct page_frag * pfrag = & x - > xfrag ;
allocsize = ALIGN ( tailen , L1_CACHE_BYTES ) ;
spin_lock_bh ( & x - > lock ) ;
if ( unlikely ( ! skb_page_frag_refill ( allocsize , pfrag , GFP_ATOMIC ) ) ) {
spin_unlock_bh ( & x - > lock ) ;
goto cow ;
}
page = pfrag - > page ;
get_page ( page ) ;
vaddr = kmap_atomic ( page ) ;
tail = vaddr + pfrag - > offset ;
2017-01-17 12:23:08 +03:00
esp_output_fill_trailer ( tail , tfclen , plen , proto ) ;
2017-01-17 12:23:03 +03:00
kunmap_atomic ( vaddr ) ;
nfrags = skb_shinfo ( skb ) - > nr_frags ;
__skb_fill_page_desc ( skb , nfrags , page , pfrag - > offset ,
tailen ) ;
skb_shinfo ( skb ) - > nr_frags = + + nfrags ;
pfrag - > offset = pfrag - > offset + allocsize ;
nfrags + + ;
skb - > len + = tailen ;
skb - > data_len + = tailen ;
skb - > truesize + = tailen ;
if ( sk )
atomic_add ( tailen , & sk - > sk_wmem_alloc ) ;
skb_push ( skb , - skb_network_offset ( skb ) ) ;
esph - > seq_no = htonl ( XFRM_SKB_CB ( skb ) - > seq . output . low ) ;
esph - > spi = x - > id . spi ;
tmp = esp_alloc_tmp ( aead , nfrags + 2 , seqhilen ) ;
if ( ! tmp ) {
spin_unlock_bh ( & x - > lock ) ;
err = - ENOMEM ;
goto error ;
}
seqhi = esp_tmp_seqhi ( tmp ) ;
iv = esp_tmp_iv ( aead , tmp , seqhilen ) ;
req = esp_tmp_req ( aead , iv ) ;
sg = esp_req_sg ( aead , req ) ;
dsg = & sg [ nfrags ] ;
esph = esp_output_set_esn ( skb , esph , seqhi ) ;
sg_init_table ( sg , nfrags ) ;
skb_to_sgvec ( skb , sg ,
( unsigned char * ) esph - skb - > data ,
assoclen + ivlen + clen + alen ) ;
allocsize = ALIGN ( skb - > data_len , L1_CACHE_BYTES ) ;
if ( unlikely ( ! skb_page_frag_refill ( allocsize , pfrag , GFP_ATOMIC ) ) ) {
spin_unlock_bh ( & x - > lock ) ;
err = - ENOMEM ;
goto error ;
}
skb_shinfo ( skb ) - > nr_frags = 1 ;
page = pfrag - > page ;
get_page ( page ) ;
/* replace page frags in skb with new page */
__skb_fill_page_desc ( skb , 0 , page , pfrag - > offset , skb - > data_len ) ;
pfrag - > offset = pfrag - > offset + allocsize ;
sg_init_table ( dsg , skb_shinfo ( skb ) - > nr_frags + 1 ) ;
skb_to_sgvec ( skb , dsg ,
( unsigned char * ) esph - skb - > data ,
assoclen + ivlen + clen + alen ) ;
spin_unlock_bh ( & x - > lock ) ;
goto skip_cow2 ;
}
2012-08-29 10:49:12 +04:00
}
2008-01-29 06:35:05 +03:00
2017-01-17 12:23:03 +03:00
cow :
err = skb_cow_data ( skb , tailen , & trailer ) ;
if ( err < 0 )
goto error ;
nfrags = err ;
2005-04-17 02:20:36 +04:00
2007-04-20 07:29:13 +04:00
tail = skb_tail_pointer ( trailer ) ;
2017-01-17 12:23:03 +03:00
esph = ip_esp_hdr ( skb ) ;
skip_cow :
2017-01-17 12:23:08 +03:00
esp_output_fill_trailer ( tail , tfclen , plen , proto ) ;
2005-04-17 02:20:36 +04:00
2017-01-17 12:23:08 +03:00
pskb_put ( skb , trailer , clen - skb - > len + alen ) ;
2007-10-11 02:44:06 +04:00
skb_push ( skb , - skb_network_offset ( skb ) ) ;
2005-04-17 02:20:36 +04:00
2011-03-08 03:06:31 +03:00
esph - > seq_no = htonl ( XFRM_SKB_CB ( skb ) - > seq . output . low ) ;
2017-01-17 12:23:03 +03:00
esph - > spi = x - > id . spi ;
2005-04-17 02:20:36 +04:00
2017-01-17 12:23:03 +03:00
tmp = esp_alloc_tmp ( aead , nfrags , seqhilen ) ;
if ( ! tmp ) {
err = - ENOMEM ;
goto error ;
2015-05-27 11:03:47 +03:00
}
2017-01-17 12:23:03 +03:00
seqhi = esp_tmp_seqhi ( tmp ) ;
iv = esp_tmp_iv ( aead , tmp , seqhilen ) ;
req = esp_tmp_req ( aead , iv ) ;
sg = esp_req_sg ( aead , req ) ;
dsg = sg ;
esph = esp_output_set_esn ( skb , esph , seqhi ) ;
2015-05-27 11:03:47 +03:00
2008-01-29 06:35:05 +03:00
sg_init_table ( sg , nfrags ) ;
skb_to_sgvec ( skb , sg ,
2015-05-27 11:03:47 +03:00
( unsigned char * ) esph - skb - > data ,
assoclen + ivlen + clen + alen ) ;
2011-03-08 03:07:51 +03:00
2017-01-17 12:23:03 +03:00
skip_cow2 :
if ( ( x - > props . flags & XFRM_STATE_ESN ) )
aead_request_set_callback ( req , 0 , esp_output_done_esn , skb ) ;
else
aead_request_set_callback ( req , 0 , esp_output_done , skb ) ;
aead_request_set_crypt ( req , sg , dsg , ivlen + clen , iv ) ;
2015-05-27 11:03:47 +03:00
aead_request_set_ad ( req , assoclen ) ;
seqno = cpu_to_be64 ( XFRM_SKB_CB ( skb ) - > seq . output . low +
( ( u64 ) XFRM_SKB_CB ( skb ) - > seq . output . hi < < 32 ) ) ;
memset ( iv , 0 , ivlen ) ;
memcpy ( iv + ivlen - min ( ivlen , 8 ) , ( u8 * ) & seqno + 8 - min ( ivlen , 8 ) ,
min ( ivlen , 8 ) ) ;
2005-04-17 02:20:36 +04:00
2008-01-29 06:35:05 +03:00
ESP_SKB_CB ( skb ) - > tmp = tmp ;
2015-05-27 11:03:47 +03:00
err = crypto_aead_encrypt ( req ) ;
switch ( err ) {
case - EINPROGRESS :
2008-01-29 06:35:05 +03:00
goto error ;
2005-04-17 02:20:36 +04:00
2015-05-27 11:03:47 +03:00
case - EBUSY :
2008-01-29 06:35:05 +03:00
err = NET_XMIT_DROP ;
2015-05-27 11:03:47 +03:00
break ;
case 0 :
if ( ( x - > props . flags & XFRM_STATE_ESN ) )
esp_output_restore_header ( skb ) ;
}
2008-01-29 06:35:05 +03:00
2017-01-17 12:23:03 +03:00
if ( sg ! = dsg )
esp_ssg_unref ( x , tmp ) ;
2008-01-29 06:35:05 +03:00
kfree ( tmp ) ;
error :
return err ;
}
static int esp_input_done2 ( struct sk_buff * skb , int err )
{
struct xfrm_state * x = xfrm_input_state ( skb ) ;
2013-10-18 14:09:05 +04:00
struct crypto_aead * aead = x - > data ;
2008-01-29 06:35:05 +03:00
int alen = crypto_aead_authsize ( aead ) ;
int hlen = sizeof ( struct ip_esp_hdr ) + crypto_aead_ivsize ( aead ) ;
int elen = skb - > len - hlen ;
int hdr_len = skb_network_header_len ( skb ) ;
int padlen ;
u8 nexthdr [ 2 ] ;
kfree ( ESP_SKB_CB ( skb ) - > tmp ) ;
2005-04-17 02:20:36 +04:00
2006-07-30 09:41:01 +04:00
if ( unlikely ( err ) )
2008-01-29 06:35:05 +03:00
goto out ;
2006-07-30 09:41:01 +04:00
2008-01-29 06:35:05 +03:00
if ( skb_copy_bits ( skb , skb - > len - alen - 2 , nexthdr , 2 ) )
BUG ( ) ;
2005-04-17 02:20:36 +04:00
2008-01-29 06:35:05 +03:00
err = - EINVAL ;
padlen = nexthdr [ 0 ] ;
if ( padlen + 2 + alen > = elen ) {
2014-11-11 21:59:17 +03:00
net_dbg_ratelimited ( " ipsec esp packet is garbage padlen=%d, elen=%d \n " ,
padlen + 2 , elen - alen ) ;
2008-01-29 06:35:05 +03:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-01-29 06:35:05 +03:00
/* ... check padding bits here. Silly. :-) */
2007-10-10 00:33:35 +04:00
2008-01-29 06:35:05 +03:00
pskb_trim ( skb , skb - > len - alen - padlen - 2 ) ;
__skb_pull ( skb , hlen ) ;
2013-01-08 11:41:12 +04:00
if ( x - > props . mode = = XFRM_MODE_TUNNEL )
skb_reset_transport_header ( skb ) ;
else
skb_set_transport_header ( skb , - hdr_len ) ;
2008-01-29 06:35:05 +03:00
err = nexthdr [ 1 ] ;
/* RFC4303: Drop dummy packets without any error */
if ( err = = IPPROTO_NONE )
err = - EINVAL ;
out :
2005-04-17 02:20:36 +04:00
return err ;
}
2008-01-29 06:35:05 +03:00
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 ) ) ;
}
2015-05-27 11:03:47 +03:00
static void esp_input_restore_header ( struct sk_buff * skb )
{
esp_restore_header ( skb , 0 ) ;
__skb_pull ( skb , 4 ) ;
}
2017-01-17 12:23:03 +03:00
static void esp_input_set_header ( struct sk_buff * skb , __be32 * seqhi )
{
struct xfrm_state * x = xfrm_input_state ( skb ) ;
struct ip_esp_hdr * esph = ( struct ip_esp_hdr * ) skb - > data ;
/* For ESN we move the header forward by 4 bytes to
* accomodate the high bits . We will move it back after
* decryption .
*/
if ( ( x - > props . flags & XFRM_STATE_ESN ) ) {
esph = ( void * ) skb_push ( skb , 4 ) ;
* seqhi = esph - > spi ;
esph - > spi = esph - > seq_no ;
esph - > seq_no = XFRM_SKB_CB ( skb ) - > seq . input . hi ;
}
}
2015-05-27 11:03:47 +03:00
static void esp_input_done_esn ( struct crypto_async_request * base , int err )
{
struct sk_buff * skb = base - > data ;
esp_input_restore_header ( skb ) ;
esp_input_done ( base , err ) ;
}
2006-04-01 12:52:46 +04:00
static int esp6_input ( struct xfrm_state * x , struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
2007-10-11 02:45:25 +04:00
struct ip_esp_hdr * esph ;
2013-10-18 14:09:05 +04:00
struct crypto_aead * aead = x - > data ;
2008-01-29 06:35:05 +03:00
struct aead_request * req ;
2005-04-17 02:20:36 +04:00
struct sk_buff * trailer ;
2015-05-27 11:03:47 +03:00
int ivlen = crypto_aead_ivsize ( aead ) ;
int elen = skb - > len - sizeof ( * esph ) - ivlen ;
2005-04-17 02:20:36 +04:00
int nfrags ;
2011-03-08 03:07:51 +03:00
int assoclen ;
int seqhilen ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
2008-01-29 06:35:05 +03:00
void * tmp ;
2011-03-08 03:07:51 +03:00
__be32 * seqhi ;
2008-01-29 06:35:05 +03:00
u8 * iv ;
struct scatterlist * sg ;
2005-04-17 02:20:36 +04:00
2015-05-27 11:03:47 +03:00
if ( ! pskb_may_pull ( skb , sizeof ( * esph ) + ivlen ) ) {
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
2006-05-28 10:06:13 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-01-29 06:35:05 +03:00
if ( elen < = 0 ) {
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
2006-05-28 10:06:13 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
}
2011-03-08 03:07:51 +03:00
assoclen = sizeof ( * esph ) ;
seqhilen = 0 ;
if ( x - > props . flags & XFRM_STATE_ESN ) {
seqhilen + = sizeof ( __be32 ) ;
assoclen + = seqhilen ;
}
2017-01-17 12:23:03 +03:00
if ( ! skb_cloned ( skb ) ) {
if ( ! skb_is_nonlinear ( skb ) ) {
nfrags = 1 ;
goto skip_cow ;
} else if ( ! skb_has_frag_list ( skb ) ) {
nfrags = skb_shinfo ( skb ) - > nr_frags ;
nfrags + + ;
goto skip_cow ;
}
}
nfrags = skb_cow_data ( skb , 0 , & trailer ) ;
if ( nfrags < 0 ) {
ret = - EINVAL ;
goto out ;
}
skip_cow :
ret = - ENOMEM ;
2015-05-27 11:03:47 +03:00
tmp = esp_alloc_tmp ( aead , nfrags , seqhilen ) ;
2008-01-29 06:35:05 +03:00
if ( ! tmp )
goto out ;
2005-04-17 02:20:36 +04:00
2008-01-29 06:35:05 +03:00
ESP_SKB_CB ( skb ) - > tmp = tmp ;
2011-03-08 03:07:51 +03:00
seqhi = esp_tmp_seqhi ( tmp ) ;
iv = esp_tmp_iv ( aead , tmp , seqhilen ) ;
2008-01-29 06:35:05 +03:00
req = esp_tmp_req ( aead , iv ) ;
2015-05-27 11:03:47 +03:00
sg = esp_req_sg ( aead , req ) ;
2005-04-17 02:20:36 +04:00
2017-01-17 12:23:03 +03:00
esp_input_set_header ( skb , seqhi ) ;
2005-04-17 02:20:36 +04:00
2017-01-17 12:23:03 +03:00
sg_init_table ( sg , nfrags ) ;
skb_to_sgvec ( skb , sg , 0 , skb - > len ) ;
2005-04-17 02:20:36 +04:00
2017-01-17 12:23:03 +03:00
skb - > ip_summed = CHECKSUM_NONE ;
2011-03-08 03:07:51 +03:00
2017-01-17 12:23:03 +03:00
if ( ( x - > props . flags & XFRM_STATE_ESN ) )
2015-05-27 11:03:47 +03:00
aead_request_set_callback ( req , 0 , esp_input_done_esn , skb ) ;
2017-01-17 12:23:03 +03:00
else
aead_request_set_callback ( req , 0 , esp_input_done , skb ) ;
2015-05-27 11:03:47 +03:00
aead_request_set_crypt ( req , sg , sg , elen + ivlen , iv ) ;
aead_request_set_ad ( req , assoclen ) ;
2005-04-17 02:20:36 +04:00
2008-01-29 06:35:05 +03:00
ret = crypto_aead_decrypt ( req ) ;
if ( ret = = - EINPROGRESS )
goto out ;
2007-12-11 03:53:29 +03:00
2015-05-27 11:03:47 +03:00
if ( ( x - > props . flags & XFRM_STATE_ESN ) )
esp_input_restore_header ( skb ) ;
2008-01-29 06:35:05 +03:00
ret = esp_input_done2 ( skb , ret ) ;
2005-04-17 02:20:36 +04:00
out :
return ret ;
}
2007-04-09 22:47:18 +04:00
static u32 esp6_get_mtu ( struct xfrm_state * x , int mtu )
2005-04-17 02:20:36 +04:00
{
2013-10-18 14:09:05 +04:00
struct crypto_aead * aead = x - > data ;
u32 blksize = ALIGN ( crypto_aead_blocksize ( aead ) , 4 ) ;
2012-05-24 15:32:38 +04:00
unsigned int net_adj ;
2005-04-17 02:20:36 +04:00
2012-05-24 15:32:38 +04:00
if ( x - > props . mode ! = XFRM_MODE_TUNNEL )
net_adj = sizeof ( struct ipv6hdr ) ;
else
net_adj = 0 ;
2007-04-09 22:47:18 +04:00
2013-10-18 14:09:05 +04:00
return ( ( mtu - x - > props . header_len - crypto_aead_authsize ( aead ) -
2013-10-18 14:09:04 +04:00
net_adj ) & ~ ( blksize - 1 ) ) + net_adj - 2 ;
2005-04-17 02:20:36 +04:00
}
2014-03-14 10:28:07 +04:00
static int esp6_err ( struct sk_buff * skb , struct inet6_skb_parm * opt ,
u8 type , u8 code , int offset , __be32 info )
2005-04-17 02:20:36 +04:00
{
2008-11-26 04:59:27 +03:00
struct net * net = dev_net ( skb - > dev ) ;
2011-04-22 08:53:02 +04:00
const struct ipv6hdr * iph = ( const struct ipv6hdr * ) skb - > data ;
2007-10-11 02:45:25 +04:00
struct ip_esp_hdr * esph = ( struct ip_esp_hdr * ) ( skb - > data + offset ) ;
2005-04-17 02:20:36 +04:00
struct xfrm_state * x ;
2013-09-10 15:43:09 +04:00
if ( type ! = ICMPV6_PKT_TOOBIG & &
2012-07-12 11:25:15 +04:00
type ! = NDISC_REDIRECT )
2014-03-14 10:28:07 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2011-04-22 08:53:02 +04:00
x = xfrm_state_lookup ( net , skb - > mark , ( const xfrm_address_t * ) & iph - > daddr ,
esph - > spi , IPPROTO_ESP , AF_INET6 ) ;
2005-04-17 02:20:36 +04:00
if ( ! x )
2014-03-14 10:28:07 +04:00
return 0 ;
2012-07-12 11:25:15 +04:00
if ( type = = NDISC_REDIRECT )
2016-11-03 20:23:43 +03:00
ip6_redirect ( skb , net , skb - > dev - > ifindex , 0 ,
sock_net_uid ( net , NULL ) ) ;
2012-07-12 11:25:15 +04:00
else
2016-11-03 20:23:43 +03:00
ip6_update_pmtu ( skb , net , info , 0 , 0 , sock_net_uid ( net , NULL ) ) ;
2005-04-17 02:20:36 +04:00
xfrm_state_put ( x ) ;
2014-03-14 10:28:07 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static void esp6_destroy ( struct xfrm_state * x )
{
2013-10-18 14:09:05 +04:00
struct crypto_aead * aead = x - > data ;
2005-04-17 02:20:36 +04:00
2013-10-18 14:09:05 +04:00
if ( ! aead )
2005-04-17 02:20:36 +04:00
return ;
2013-10-18 14:09:05 +04:00
crypto_free_aead ( aead ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-29 06:37:29 +03:00
static int esp_init_aead ( struct xfrm_state * x )
{
2015-05-27 11:03:47 +03:00
char aead_name [ CRYPTO_MAX_ALG_NAME ] ;
2008-01-29 06:37:29 +03:00
struct crypto_aead * aead ;
int err ;
2015-05-27 11:03:47 +03:00
err = - ENAMETOOLONG ;
if ( snprintf ( aead_name , CRYPTO_MAX_ALG_NAME , " %s(%s) " ,
x - > geniv , x - > aead - > alg_name ) > = CRYPTO_MAX_ALG_NAME )
goto error ;
aead = crypto_alloc_aead ( aead_name , 0 , 0 ) ;
2008-01-29 06:37:29 +03:00
err = PTR_ERR ( aead ) ;
if ( IS_ERR ( aead ) )
goto error ;
2013-10-18 14:09:05 +04:00
x - > data = aead ;
2008-01-29 06:37:29 +03:00
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 )
2005-04-17 02:20:36 +04:00
{
2008-01-29 06:35:05 +03: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-17 02:20:36 +04:00
2008-01-29 06:37:29 +03:00
err = - EINVAL ;
2015-03-29 16:00:04 +03:00
if ( ! x - > ealg )
2008-01-29 06:37:29 +03:00
goto error ;
2008-01-29 06:35:05 +03:00
2008-01-29 06:37:29 +03:00
err = - ENAMETOOLONG ;
2011-03-08 03:07:51 +03:00
if ( ( x - > props . flags & XFRM_STATE_ESN ) ) {
if ( snprintf ( authenc_name , CRYPTO_MAX_ALG_NAME ,
2015-05-27 11:03:47 +03:00
" %s%sauthencesn(%s,%s)%s " ,
x - > geniv ? : " " , x - > geniv ? " ( " : " " ,
2011-03-08 03:07:51 +03:00
x - > aalg ? x - > aalg - > alg_name : " digest_null " ,
2015-05-27 11:03:47 +03:00
x - > ealg - > alg_name ,
x - > geniv ? " ) " : " " ) > = CRYPTO_MAX_ALG_NAME )
2011-03-08 03:07:51 +03:00
goto error ;
} else {
if ( snprintf ( authenc_name , CRYPTO_MAX_ALG_NAME ,
2015-05-27 11:03:47 +03:00
" %s%sauthenc(%s,%s)%s " ,
x - > geniv ? : " " , x - > geniv ? " ( " : " " ,
2011-03-08 03:07:51 +03:00
x - > aalg ? x - > aalg - > alg_name : " digest_null " ,
2015-05-27 11:03:47 +03:00
x - > ealg - > alg_name ,
x - > geniv ? " ) " : " " ) > = CRYPTO_MAX_ALG_NAME )
2011-03-08 03:07:51 +03:00
goto error ;
}
2008-01-29 06:35:05 +03:00
aead = crypto_alloc_aead ( authenc_name , 0 , 0 ) ;
err = PTR_ERR ( aead ) ;
if ( IS_ERR ( aead ) )
goto error ;
2013-10-18 14:09:05 +04:00
x - > data = aead ;
2008-01-29 06:35:05 +03:00
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-17 02:20:36 +04:00
if ( x - > aalg ) {
struct xfrm_algo_desc * aalg_desc ;
2008-01-29 06:35:05 +03:00
memcpy ( p , x - > aalg - > alg_key , ( x - > aalg - > alg_key_len + 7 ) / 8 ) ;
p + = ( x - > aalg - > alg_key_len + 7 ) / 8 ;
2007-02-09 17:24:49 +03:00
2005-04-17 02:20:36 +04:00
aalg_desc = xfrm_aalg_get_byname ( x - > aalg - > alg_name , 0 ) ;
BUG_ON ( ! aalg_desc ) ;
2007-02-09 17:24:49 +03:00
2008-01-29 06:35:05 +03:00
err = - EINVAL ;
2014-11-06 02:36:08 +03:00
if ( aalg_desc - > uinfo . auth . icv_fullbits / 8 ! =
2008-01-29 06:35:05 +03:00
crypto_aead_authsize ( aead ) ) {
2014-11-06 02:36:08 +03:00
pr_info ( " ESP: %s digestsize %u != %hu \n " ,
x - > aalg - > alg_name ,
crypto_aead_authsize ( aead ) ,
aalg_desc - > uinfo . auth . icv_fullbits / 8 ) ;
2008-01-29 06:35:05 +03:00
goto free_key ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 17:24:49 +03:00
2008-01-29 06:35:05 +03:00
err = crypto_aead_setauthsize (
2009-11-25 03:29:53 +03:00
aead , x - > aalg - > alg_trunc_len / 8 ) ;
2008-01-29 06:35:05 +03:00
if ( err )
goto free_key ;
2005-04-17 02:20:36 +04:00
}
2008-01-29 06:35:05 +03: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-29 06:37:29 +03:00
error :
return err ;
}
static int esp6_init_state ( struct xfrm_state * x )
{
struct crypto_aead * aead ;
u32 align ;
int err ;
if ( x - > encap )
return - EINVAL ;
2013-10-18 14:09:05 +04:00
x - > data = NULL ;
2008-01-29 06:37:29 +03:00
if ( x - > aead )
err = esp_init_aead ( x ) ;
else
err = esp_init_authenc ( x ) ;
2008-01-29 06:35:05 +03:00
if ( err )
2005-04-17 02:20:36 +04:00
goto error ;
2008-01-29 06:35:05 +03:00
2013-10-18 14:09:05 +04:00
aead = x - > data ;
2008-01-29 06:37:29 +03:00
2008-01-29 06:35:05 +03:00
x - > props . header_len = sizeof ( struct ip_esp_hdr ) +
crypto_aead_ivsize ( aead ) ;
2007-10-18 08:35:15 +04:00
switch ( x - > props . mode ) {
case XFRM_MODE_BEET :
2008-08-06 13:40:25 +04:00
if ( x - > sel . family ! = AF_INET6 )
x - > props . header_len + = IPV4_BEET_PHMAXLEN +
2014-08-25 00:53:10 +04:00
( sizeof ( struct ipv6hdr ) - sizeof ( struct iphdr ) ) ;
2008-08-06 13:40:25 +04:00
break ;
2007-10-18 08:35:15 +04:00
case XFRM_MODE_TRANSPORT :
break ;
case XFRM_MODE_TUNNEL :
2005-04-17 02:20:36 +04:00
x - > props . header_len + = sizeof ( struct ipv6hdr ) ;
2007-10-22 13:30:15 +04:00
break ;
2007-10-18 08:35:15 +04:00
default :
goto error ;
}
2008-01-29 06:35:05 +03:00
align = ALIGN ( crypto_aead_blocksize ( aead ) , 4 ) ;
2013-10-18 14:09:05 +04:00
x - > props . trailer_len = align + 1 + crypto_aead_authsize ( aead ) ;
2005-04-17 02:20:36 +04:00
error :
2008-01-29 06:35:05 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
2014-03-14 10:28:07 +04:00
static int esp6_rcv_cb ( struct sk_buff * skb , int err )
{
return 0 ;
}
2014-08-25 00:53:11 +04:00
static const struct xfrm_type esp6_type = {
2005-04-17 02:20:36 +04:00
. description = " ESP6 " ,
2014-08-25 00:53:11 +04:00
. owner = THIS_MODULE ,
. proto = IPPROTO_ESP ,
2007-10-09 04:25:53 +04:00
. flags = XFRM_TYPE_REPLAY_PROT ,
2005-04-17 02:20:36 +04:00
. init_state = esp6_init_state ,
. destructor = esp6_destroy ,
2007-04-09 22:47:18 +04:00
. get_mtu = esp6_get_mtu ,
2005-04-17 02:20:36 +04:00
. input = esp6_input ,
2006-08-24 04:57:28 +04:00
. output = esp6_output ,
. hdr_offset = xfrm6_find_1stfragopt ,
2005-04-17 02:20:36 +04:00
} ;
2014-03-14 10:28:07 +04:00
static struct xfrm6_protocol esp6_protocol = {
. handler = xfrm6_rcv ,
. cb_handler = esp6_rcv_cb ,
2005-04-17 02:20:36 +04:00
. err_handler = esp6_err ,
2014-03-14 10:28:07 +04:00
. priority = 0 ,
2005-04-17 02:20:36 +04:00
} ;
static int __init esp6_init ( void )
{
if ( xfrm_register_type ( & esp6_type , AF_INET6 ) < 0 ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't add xfrm type \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
}
2014-03-14 10:28:07 +04:00
if ( xfrm6_protocol_register ( & esp6_protocol , IPPROTO_ESP ) < 0 ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't add protocol \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
xfrm_unregister_type ( & esp6_type , AF_INET6 ) ;
return - EAGAIN ;
}
return 0 ;
}
static void __exit esp6_fini ( void )
{
2014-03-14 10:28:07 +04:00
if ( xfrm6_protocol_deregister ( & esp6_protocol , IPPROTO_ESP ) < 0 )
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't remove protocol \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( xfrm_unregister_type ( & esp6_type , AF_INET6 ) < 0 )
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't remove xfrm type \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( esp6_init ) ;
module_exit ( esp6_fini ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-06-27 10:57:49 +04:00
MODULE_ALIAS_XFRM_TYPE ( AF_INET6 , XFRM_PROTO_ESP ) ;