11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (C)2002 USAGI/WIDE Project 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 51da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 61da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 71da177e4SLinus Torvalds * (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 101da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 111da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121da177e4SLinus Torvalds * GNU General Public License for more details. 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 151da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 161da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Authors 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds * Mitsuru KANDA @USAGI : IPv6 Support 211da177e4SLinus Torvalds * Kazunori MIYAZAWA @USAGI : 221da177e4SLinus Torvalds * Kunihiro Ishiguro <kunihiro@ipinfusion.com> 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds * This file is derived from net/ipv4/esp.c 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds 27f3213831SJoe Perches #define pr_fmt(fmt) "IPv6: " fmt 28f3213831SJoe Perches 2938320c70SHerbert Xu #include <crypto/aead.h> 3038320c70SHerbert Xu #include <crypto/authenc.h> 316b7326c8SHerbert Xu #include <linux/err.h> 321da177e4SLinus Torvalds #include <linux/module.h> 331da177e4SLinus Torvalds #include <net/ip.h> 341da177e4SLinus Torvalds #include <net/xfrm.h> 351da177e4SLinus Torvalds #include <net/esp.h> 3672998d8cSAdrian Bunk #include <linux/scatterlist.h> 37a02a6422SHerbert Xu #include <linux/kernel.h> 381da177e4SLinus Torvalds #include <linux/pfkeyv2.h> 391da177e4SLinus Torvalds #include <linux/random.h> 4038320c70SHerbert Xu #include <linux/slab.h> 41b7c6538cSHerbert Xu #include <linux/spinlock.h> 4281aded24SDavid S. Miller #include <net/ip6_route.h> 431da177e4SLinus Torvalds #include <net/icmp.h> 441da177e4SLinus Torvalds #include <net/ipv6.h> 4514c85021SArnaldo Carvalho de Melo #include <net/protocol.h> 461da177e4SLinus Torvalds #include <linux/icmpv6.h> 471da177e4SLinus Torvalds 4838320c70SHerbert Xu struct esp_skb_cb { 4938320c70SHerbert Xu struct xfrm_skb_cb xfrm; 5038320c70SHerbert Xu void *tmp; 5138320c70SHerbert Xu }; 5238320c70SHerbert Xu 5338320c70SHerbert Xu #define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0])) 5438320c70SHerbert Xu 55040253c9SMartin Willi static u32 esp6_get_mtu(struct xfrm_state *x, int mtu); 56040253c9SMartin Willi 5738320c70SHerbert Xu /* 5838320c70SHerbert Xu * Allocate an AEAD request structure with extra space for SG and IV. 5938320c70SHerbert Xu * 60d212a4c2SSteffen Klassert * For alignment considerations the upper 32 bits of the sequence number are 61d212a4c2SSteffen Klassert * placed at the front, if present. Followed by the IV, the request and finally 62d212a4c2SSteffen Klassert * the SG list. 6338320c70SHerbert Xu * 6438320c70SHerbert Xu * TODO: Use spare space in skb for this where possible. 6538320c70SHerbert Xu */ 66d212a4c2SSteffen Klassert static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqihlen) 6738320c70SHerbert Xu { 6838320c70SHerbert Xu unsigned int len; 6938320c70SHerbert Xu 70d212a4c2SSteffen Klassert len = seqihlen; 71d212a4c2SSteffen Klassert 72d212a4c2SSteffen Klassert len += crypto_aead_ivsize(aead); 73d212a4c2SSteffen Klassert 7438320c70SHerbert Xu if (len) { 7538320c70SHerbert Xu len += crypto_aead_alignmask(aead) & 7638320c70SHerbert Xu ~(crypto_tfm_ctx_alignment() - 1); 7738320c70SHerbert Xu len = ALIGN(len, crypto_tfm_ctx_alignment()); 7838320c70SHerbert Xu } 7938320c70SHerbert Xu 8038320c70SHerbert Xu len += sizeof(struct aead_givcrypt_request) + crypto_aead_reqsize(aead); 8138320c70SHerbert Xu len = ALIGN(len, __alignof__(struct scatterlist)); 8238320c70SHerbert Xu 8338320c70SHerbert Xu len += sizeof(struct scatterlist) * nfrags; 8438320c70SHerbert Xu 8538320c70SHerbert Xu return kmalloc(len, GFP_ATOMIC); 8638320c70SHerbert Xu } 8738320c70SHerbert Xu 88d212a4c2SSteffen Klassert static inline __be32 *esp_tmp_seqhi(void *tmp) 89d212a4c2SSteffen Klassert { 90d212a4c2SSteffen Klassert return PTR_ALIGN((__be32 *)tmp, __alignof__(__be32)); 91d212a4c2SSteffen Klassert } 92d212a4c2SSteffen Klassert 93d212a4c2SSteffen Klassert static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int seqhilen) 9438320c70SHerbert Xu { 9538320c70SHerbert Xu return crypto_aead_ivsize(aead) ? 96d212a4c2SSteffen Klassert PTR_ALIGN((u8 *)tmp + seqhilen, 97d212a4c2SSteffen Klassert crypto_aead_alignmask(aead) + 1) : tmp + seqhilen; 9838320c70SHerbert Xu } 9938320c70SHerbert Xu 10038320c70SHerbert Xu static inline struct aead_givcrypt_request *esp_tmp_givreq( 10138320c70SHerbert Xu struct crypto_aead *aead, u8 *iv) 10238320c70SHerbert Xu { 10338320c70SHerbert Xu struct aead_givcrypt_request *req; 10438320c70SHerbert Xu 10538320c70SHerbert Xu req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead), 10638320c70SHerbert Xu crypto_tfm_ctx_alignment()); 10738320c70SHerbert Xu aead_givcrypt_set_tfm(req, aead); 10838320c70SHerbert Xu return req; 10938320c70SHerbert Xu } 11038320c70SHerbert Xu 11138320c70SHerbert Xu static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv) 11238320c70SHerbert Xu { 11338320c70SHerbert Xu struct aead_request *req; 11438320c70SHerbert Xu 11538320c70SHerbert Xu req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead), 11638320c70SHerbert Xu crypto_tfm_ctx_alignment()); 11738320c70SHerbert Xu aead_request_set_tfm(req, aead); 11838320c70SHerbert Xu return req; 11938320c70SHerbert Xu } 12038320c70SHerbert Xu 12138320c70SHerbert Xu static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, 12238320c70SHerbert Xu struct aead_request *req) 12338320c70SHerbert Xu { 12438320c70SHerbert Xu return (void *)ALIGN((unsigned long)(req + 1) + 12538320c70SHerbert Xu crypto_aead_reqsize(aead), 12638320c70SHerbert Xu __alignof__(struct scatterlist)); 12738320c70SHerbert Xu } 12838320c70SHerbert Xu 12938320c70SHerbert Xu static inline struct scatterlist *esp_givreq_sg( 13038320c70SHerbert Xu struct crypto_aead *aead, struct aead_givcrypt_request *req) 13138320c70SHerbert Xu { 13238320c70SHerbert Xu return (void *)ALIGN((unsigned long)(req + 1) + 13338320c70SHerbert Xu crypto_aead_reqsize(aead), 13438320c70SHerbert Xu __alignof__(struct scatterlist)); 13538320c70SHerbert Xu } 13638320c70SHerbert Xu 13738320c70SHerbert Xu static void esp_output_done(struct crypto_async_request *base, int err) 13838320c70SHerbert Xu { 13938320c70SHerbert Xu struct sk_buff *skb = base->data; 14038320c70SHerbert Xu 14138320c70SHerbert Xu kfree(ESP_SKB_CB(skb)->tmp); 14238320c70SHerbert Xu xfrm_output_resume(skb, err); 14338320c70SHerbert Xu } 14438320c70SHerbert Xu 1451da177e4SLinus Torvalds static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds int err; 14887bdc48dSHerbert Xu struct ip_esp_hdr *esph; 14938320c70SHerbert Xu struct crypto_aead *aead; 15038320c70SHerbert Xu struct aead_givcrypt_request *req; 15138320c70SHerbert Xu struct scatterlist *sg; 15238320c70SHerbert Xu struct scatterlist *asg; 1531da177e4SLinus Torvalds struct sk_buff *trailer; 15438320c70SHerbert Xu void *tmp; 1551da177e4SLinus Torvalds int blksize; 1561da177e4SLinus Torvalds int clen; 1571da177e4SLinus Torvalds int alen; 158040253c9SMartin Willi int plen; 159040253c9SMartin Willi int tfclen; 1601da177e4SLinus Torvalds int nfrags; 161d212a4c2SSteffen Klassert int assoclen; 162d212a4c2SSteffen Klassert int sglists; 163d212a4c2SSteffen Klassert int seqhilen; 16438320c70SHerbert Xu u8 *iv; 16527a884dcSArnaldo Carvalho de Melo u8 *tail; 166d212a4c2SSteffen Klassert __be32 *seqhi; 167ea2ae17dSArnaldo Carvalho de Melo struct esp_data *esp = x->data; 1681da177e4SLinus Torvalds 1697b277b1aSHerbert Xu /* skb is pure payload to encrypt */ 17038320c70SHerbert Xu aead = esp->aead; 17138320c70SHerbert Xu alen = crypto_aead_authsize(aead); 1721da177e4SLinus Torvalds 173040253c9SMartin Willi tfclen = 0; 174040253c9SMartin Willi if (x->tfcpad) { 175040253c9SMartin Willi struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); 176040253c9SMartin Willi u32 padto; 177040253c9SMartin Willi 178040253c9SMartin Willi padto = min(x->tfcpad, esp6_get_mtu(x, dst->child_mtu_cached)); 179040253c9SMartin Willi if (skb->len < padto) 180040253c9SMartin Willi tfclen = padto - skb->len; 181040253c9SMartin Willi } 18238320c70SHerbert Xu blksize = ALIGN(crypto_aead_blocksize(aead), 4); 183040253c9SMartin Willi clen = ALIGN(skb->len + 2 + tfclen, blksize); 18438320c70SHerbert Xu if (esp->padlen) 18538320c70SHerbert Xu clen = ALIGN(clen, esp->padlen); 186040253c9SMartin Willi plen = clen - skb->len - tfclen; 18738320c70SHerbert Xu 188040253c9SMartin Willi err = skb_cow_data(skb, tfclen + plen + alen, &trailer); 189040253c9SMartin Willi if (err < 0) 1901da177e4SLinus Torvalds goto error; 19138320c70SHerbert Xu nfrags = err; 19238320c70SHerbert Xu 193d212a4c2SSteffen Klassert assoclen = sizeof(*esph); 194d212a4c2SSteffen Klassert sglists = 1; 195d212a4c2SSteffen Klassert seqhilen = 0; 196d212a4c2SSteffen Klassert 197d212a4c2SSteffen Klassert if (x->props.flags & XFRM_STATE_ESN) { 198d212a4c2SSteffen Klassert sglists += 2; 199d212a4c2SSteffen Klassert seqhilen += sizeof(__be32); 200d212a4c2SSteffen Klassert assoclen += seqhilen; 201d212a4c2SSteffen Klassert } 202d212a4c2SSteffen Klassert 203d212a4c2SSteffen Klassert tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen); 20448f125ceSJulia Lawall if (!tmp) { 20548f125ceSJulia Lawall err = -ENOMEM; 20638320c70SHerbert Xu goto error; 20748f125ceSJulia Lawall } 20838320c70SHerbert Xu 209d212a4c2SSteffen Klassert seqhi = esp_tmp_seqhi(tmp); 210d212a4c2SSteffen Klassert iv = esp_tmp_iv(aead, tmp, seqhilen); 21138320c70SHerbert Xu req = esp_tmp_givreq(aead, iv); 21238320c70SHerbert Xu asg = esp_givreq_sg(aead, req); 213d212a4c2SSteffen Klassert sg = asg + sglists; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds /* Fill padding... */ 21627a884dcSArnaldo Carvalho de Melo tail = skb_tail_pointer(trailer); 217040253c9SMartin Willi if (tfclen) { 218040253c9SMartin Willi memset(tail, 0, tfclen); 219040253c9SMartin Willi tail += tfclen; 220040253c9SMartin Willi } 2211da177e4SLinus Torvalds do { 2221da177e4SLinus Torvalds int i; 223040253c9SMartin Willi for (i = 0; i < plen - 2; i++) 22427a884dcSArnaldo Carvalho de Melo tail[i] = i + 1; 2251da177e4SLinus Torvalds } while (0); 226040253c9SMartin Willi tail[plen - 2] = plen - 2; 227040253c9SMartin Willi tail[plen - 1] = *skb_mac_header(skb); 22838320c70SHerbert Xu pskb_put(skb, trailer, clen - skb->len + alen); 2291da177e4SLinus Torvalds 2307b277b1aSHerbert Xu skb_push(skb, -skb_network_offset(skb)); 23187bdc48dSHerbert Xu esph = ip_esp_hdr(skb); 232007f0211SHerbert Xu *skb_mac_header(skb) = IPPROTO_ESP; 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds esph->spi = x->id.spi; 2351ce3644aSSteffen Klassert esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); 2361da177e4SLinus Torvalds 237ed0e7e0cSDavid S. Miller sg_init_table(sg, nfrags); 23851c739d1SDavid S. Miller skb_to_sgvec(skb, sg, 23938320c70SHerbert Xu esph->enc_data + crypto_aead_ivsize(aead) - skb->data, 24038320c70SHerbert Xu clen + alen); 241d212a4c2SSteffen Klassert 242d212a4c2SSteffen Klassert if ((x->props.flags & XFRM_STATE_ESN)) { 243d212a4c2SSteffen Klassert sg_init_table(asg, 3); 244d212a4c2SSteffen Klassert sg_set_buf(asg, &esph->spi, sizeof(__be32)); 245d212a4c2SSteffen Klassert *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi); 246d212a4c2SSteffen Klassert sg_set_buf(asg + 1, seqhi, seqhilen); 247d212a4c2SSteffen Klassert sg_set_buf(asg + 2, &esph->seq_no, sizeof(__be32)); 248d212a4c2SSteffen Klassert } else 24938320c70SHerbert Xu sg_init_one(asg, esph, sizeof(*esph)); 2501da177e4SLinus Torvalds 25138320c70SHerbert Xu aead_givcrypt_set_callback(req, 0, esp_output_done, skb); 25238320c70SHerbert Xu aead_givcrypt_set_crypt(req, sg, sg, clen, iv); 253d212a4c2SSteffen Klassert aead_givcrypt_set_assoc(req, asg, assoclen); 254b318e0e4SHerbert Xu aead_givcrypt_set_giv(req, esph->enc_data, 2551ce3644aSSteffen Klassert XFRM_SKB_CB(skb)->seq.output.low); 2566b7326c8SHerbert Xu 25738320c70SHerbert Xu ESP_SKB_CB(skb)->tmp = tmp; 25838320c70SHerbert Xu err = crypto_aead_givencrypt(req); 25938320c70SHerbert Xu if (err == -EINPROGRESS) 26038320c70SHerbert Xu goto error; 2611da177e4SLinus Torvalds 26238320c70SHerbert Xu if (err == -EBUSY) 26338320c70SHerbert Xu err = NET_XMIT_DROP; 2641da177e4SLinus Torvalds 26538320c70SHerbert Xu kfree(tmp); 266b7c6538cSHerbert Xu 2671da177e4SLinus Torvalds error: 2681da177e4SLinus Torvalds return err; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 27138320c70SHerbert Xu static int esp_input_done2(struct sk_buff *skb, int err) 27238320c70SHerbert Xu { 27338320c70SHerbert Xu struct xfrm_state *x = xfrm_input_state(skb); 27438320c70SHerbert Xu struct esp_data *esp = x->data; 27538320c70SHerbert Xu struct crypto_aead *aead = esp->aead; 27638320c70SHerbert Xu int alen = crypto_aead_authsize(aead); 27738320c70SHerbert Xu int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); 27838320c70SHerbert Xu int elen = skb->len - hlen; 27938320c70SHerbert Xu int hdr_len = skb_network_header_len(skb); 28038320c70SHerbert Xu int padlen; 28138320c70SHerbert Xu u8 nexthdr[2]; 28238320c70SHerbert Xu 28338320c70SHerbert Xu kfree(ESP_SKB_CB(skb)->tmp); 28438320c70SHerbert Xu 28538320c70SHerbert Xu if (unlikely(err)) 28638320c70SHerbert Xu goto out; 28738320c70SHerbert Xu 28838320c70SHerbert Xu if (skb_copy_bits(skb, skb->len - alen - 2, nexthdr, 2)) 28938320c70SHerbert Xu BUG(); 29038320c70SHerbert Xu 29138320c70SHerbert Xu err = -EINVAL; 29238320c70SHerbert Xu padlen = nexthdr[0]; 29338320c70SHerbert Xu if (padlen + 2 + alen >= elen) { 29438320c70SHerbert Xu LIMIT_NETDEBUG(KERN_WARNING "ipsec esp packet is garbage " 29538320c70SHerbert Xu "padlen=%d, elen=%d\n", padlen + 2, elen - alen); 29638320c70SHerbert Xu goto out; 29738320c70SHerbert Xu } 29838320c70SHerbert Xu 29938320c70SHerbert Xu /* ... check padding bits here. Silly. :-) */ 30038320c70SHerbert Xu 30138320c70SHerbert Xu pskb_trim(skb, skb->len - alen - padlen - 2); 30238320c70SHerbert Xu __skb_pull(skb, hlen); 303a9403f8aSLi RongQing if (x->props.mode == XFRM_MODE_TUNNEL) 304a9403f8aSLi RongQing skb_reset_transport_header(skb); 305a9403f8aSLi RongQing else 30638320c70SHerbert Xu skb_set_transport_header(skb, -hdr_len); 30738320c70SHerbert Xu 30838320c70SHerbert Xu err = nexthdr[1]; 30938320c70SHerbert Xu 31038320c70SHerbert Xu /* RFC4303: Drop dummy packets without any error */ 31138320c70SHerbert Xu if (err == IPPROTO_NONE) 31238320c70SHerbert Xu err = -EINVAL; 31338320c70SHerbert Xu 31438320c70SHerbert Xu out: 31538320c70SHerbert Xu return err; 31638320c70SHerbert Xu } 31738320c70SHerbert Xu 31838320c70SHerbert Xu static void esp_input_done(struct crypto_async_request *base, int err) 31938320c70SHerbert Xu { 32038320c70SHerbert Xu struct sk_buff *skb = base->data; 32138320c70SHerbert Xu 32238320c70SHerbert Xu xfrm_input_resume(skb, esp_input_done2(skb, err)); 32338320c70SHerbert Xu } 32438320c70SHerbert Xu 325e695633eSHerbert Xu static int esp6_input(struct xfrm_state *x, struct sk_buff *skb) 3261da177e4SLinus Torvalds { 32787bdc48dSHerbert Xu struct ip_esp_hdr *esph; 3281da177e4SLinus Torvalds struct esp_data *esp = x->data; 32938320c70SHerbert Xu struct crypto_aead *aead = esp->aead; 33038320c70SHerbert Xu struct aead_request *req; 3311da177e4SLinus Torvalds struct sk_buff *trailer; 33238320c70SHerbert Xu int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead); 3331da177e4SLinus Torvalds int nfrags; 334d212a4c2SSteffen Klassert int assoclen; 335d212a4c2SSteffen Klassert int sglists; 336d212a4c2SSteffen Klassert int seqhilen; 3371da177e4SLinus Torvalds int ret = 0; 33838320c70SHerbert Xu void *tmp; 339d212a4c2SSteffen Klassert __be32 *seqhi; 34038320c70SHerbert Xu u8 *iv; 34138320c70SHerbert Xu struct scatterlist *sg; 34238320c70SHerbert Xu struct scatterlist *asg; 3431da177e4SLinus Torvalds 344920fc941SThomas Graf if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead))) { 3451da177e4SLinus Torvalds ret = -EINVAL; 34631a4ab93SHerbert Xu goto out; 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds 34938320c70SHerbert Xu if (elen <= 0) { 3501da177e4SLinus Torvalds ret = -EINVAL; 35131a4ab93SHerbert Xu goto out; 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0) { 3551da177e4SLinus Torvalds ret = -EINVAL; 3561da177e4SLinus Torvalds goto out; 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds 35938320c70SHerbert Xu ret = -ENOMEM; 360d212a4c2SSteffen Klassert 361d212a4c2SSteffen Klassert assoclen = sizeof(*esph); 362d212a4c2SSteffen Klassert sglists = 1; 363d212a4c2SSteffen Klassert seqhilen = 0; 364d212a4c2SSteffen Klassert 365d212a4c2SSteffen Klassert if (x->props.flags & XFRM_STATE_ESN) { 366d212a4c2SSteffen Klassert sglists += 2; 367d212a4c2SSteffen Klassert seqhilen += sizeof(__be32); 368d212a4c2SSteffen Klassert assoclen += seqhilen; 369d212a4c2SSteffen Klassert } 370d212a4c2SSteffen Klassert 371d212a4c2SSteffen Klassert tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen); 37238320c70SHerbert Xu if (!tmp) 37338320c70SHerbert Xu goto out; 37438320c70SHerbert Xu 37538320c70SHerbert Xu ESP_SKB_CB(skb)->tmp = tmp; 376d212a4c2SSteffen Klassert seqhi = esp_tmp_seqhi(tmp); 377d212a4c2SSteffen Klassert iv = esp_tmp_iv(aead, tmp, seqhilen); 37838320c70SHerbert Xu req = esp_tmp_req(aead, iv); 37938320c70SHerbert Xu asg = esp_req_sg(aead, req); 380c0a56e64SSteffen Klassert sg = asg + sglists; 38138320c70SHerbert Xu 3821da177e4SLinus Torvalds skb->ip_summed = CHECKSUM_NONE; 3831da177e4SLinus Torvalds 38487bdc48dSHerbert Xu esph = (struct ip_esp_hdr *)skb->data; 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds /* Get ivec. This can be wrong, check against another impls. */ 38738320c70SHerbert Xu iv = esph->enc_data; 3881da177e4SLinus Torvalds 389ed0e7e0cSDavid S. Miller sg_init_table(sg, nfrags); 39038320c70SHerbert Xu skb_to_sgvec(skb, sg, sizeof(*esph) + crypto_aead_ivsize(aead), elen); 391d212a4c2SSteffen Klassert 392d212a4c2SSteffen Klassert if ((x->props.flags & XFRM_STATE_ESN)) { 393d212a4c2SSteffen Klassert sg_init_table(asg, 3); 394d212a4c2SSteffen Klassert sg_set_buf(asg, &esph->spi, sizeof(__be32)); 395d212a4c2SSteffen Klassert *seqhi = XFRM_SKB_CB(skb)->seq.input.hi; 396d212a4c2SSteffen Klassert sg_set_buf(asg + 1, seqhi, seqhilen); 397d212a4c2SSteffen Klassert sg_set_buf(asg + 2, &esph->seq_no, sizeof(__be32)); 398d212a4c2SSteffen Klassert } else 39938320c70SHerbert Xu sg_init_one(asg, esph, sizeof(*esph)); 4000ebea8efSHerbert Xu 40138320c70SHerbert Xu aead_request_set_callback(req, 0, esp_input_done, skb); 40238320c70SHerbert Xu aead_request_set_crypt(req, sg, sg, elen, iv); 403d212a4c2SSteffen Klassert aead_request_set_assoc(req, asg, assoclen); 4040ebea8efSHerbert Xu 40538320c70SHerbert Xu ret = crypto_aead_decrypt(req); 40638320c70SHerbert Xu if (ret == -EINPROGRESS) 4076b7326c8SHerbert Xu goto out; 4081da177e4SLinus Torvalds 40938320c70SHerbert Xu ret = esp_input_done2(skb, ret); 4100ebea8efSHerbert Xu 4111da177e4SLinus Torvalds out: 4121da177e4SLinus Torvalds return ret; 4131da177e4SLinus Torvalds } 4141da177e4SLinus Torvalds 415c5c25238SPatrick McHardy static u32 esp6_get_mtu(struct xfrm_state *x, int mtu) 4161da177e4SLinus Torvalds { 4171da177e4SLinus Torvalds struct esp_data *esp = x->data; 41838320c70SHerbert Xu u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4); 41938320c70SHerbert Xu u32 align = max_t(u32, blksize, esp->padlen); 42091657eafSBenjamin Poirier unsigned int net_adj; 4211da177e4SLinus Torvalds 42291657eafSBenjamin Poirier if (x->props.mode != XFRM_MODE_TUNNEL) 42391657eafSBenjamin Poirier net_adj = sizeof(struct ipv6hdr); 42491657eafSBenjamin Poirier else 42591657eafSBenjamin Poirier net_adj = 0; 426c5c25238SPatrick McHardy 42791657eafSBenjamin Poirier return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) - 42891657eafSBenjamin Poirier net_adj) & ~(align - 1)) + (net_adj - 2); 4291da177e4SLinus Torvalds } 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, 432d5fdd6baSBrian Haley u8 type, u8 code, int offset, __be32 info) 4331da177e4SLinus Torvalds { 4344fb236baSAlexey Dobriyan struct net *net = dev_net(skb->dev); 435b71d1d42SEric Dumazet const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data; 43687bdc48dSHerbert Xu struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data + offset); 4371da177e4SLinus Torvalds struct xfrm_state *x; 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds if (type != ICMPV6_DEST_UNREACH && 440ec18d9a2SDavid S. Miller type != ICMPV6_PKT_TOOBIG && 441ec18d9a2SDavid S. Miller type != NDISC_REDIRECT) 4421da177e4SLinus Torvalds return; 4431da177e4SLinus Torvalds 444b71d1d42SEric Dumazet x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr, 445b71d1d42SEric Dumazet esph->spi, IPPROTO_ESP, AF_INET6); 4461da177e4SLinus Torvalds if (!x) 4471da177e4SLinus Torvalds return; 448ec18d9a2SDavid S. Miller 449ec18d9a2SDavid S. Miller if (type == NDISC_REDIRECT) 450ec18d9a2SDavid S. Miller ip6_redirect(skb, net, 0, 0); 451ec18d9a2SDavid S. Miller else 45281aded24SDavid S. Miller ip6_update_pmtu(skb, net, info, 0, 0); 4531da177e4SLinus Torvalds xfrm_state_put(x); 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds static void esp6_destroy(struct xfrm_state *x) 4571da177e4SLinus Torvalds { 4581da177e4SLinus Torvalds struct esp_data *esp = x->data; 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds if (!esp) 4611da177e4SLinus Torvalds return; 4621da177e4SLinus Torvalds 46338320c70SHerbert Xu crypto_free_aead(esp->aead); 4641da177e4SLinus Torvalds kfree(esp); 4651da177e4SLinus Torvalds } 4661da177e4SLinus Torvalds 4671a6509d9SHerbert Xu static int esp_init_aead(struct xfrm_state *x) 4681da177e4SLinus Torvalds { 4691a6509d9SHerbert Xu struct esp_data *esp = x->data; 4701a6509d9SHerbert Xu struct crypto_aead *aead; 4711a6509d9SHerbert Xu int err; 4721a6509d9SHerbert Xu 4731a6509d9SHerbert Xu aead = crypto_alloc_aead(x->aead->alg_name, 0, 0); 4741a6509d9SHerbert Xu err = PTR_ERR(aead); 4751a6509d9SHerbert Xu if (IS_ERR(aead)) 4761a6509d9SHerbert Xu goto error; 4771a6509d9SHerbert Xu 4781a6509d9SHerbert Xu esp->aead = aead; 4791a6509d9SHerbert Xu 4801a6509d9SHerbert Xu err = crypto_aead_setkey(aead, x->aead->alg_key, 4811a6509d9SHerbert Xu (x->aead->alg_key_len + 7) / 8); 4821a6509d9SHerbert Xu if (err) 4831a6509d9SHerbert Xu goto error; 4841a6509d9SHerbert Xu 4851a6509d9SHerbert Xu err = crypto_aead_setauthsize(aead, x->aead->alg_icv_len / 8); 4861a6509d9SHerbert Xu if (err) 4871a6509d9SHerbert Xu goto error; 4881a6509d9SHerbert Xu 4891a6509d9SHerbert Xu error: 4901a6509d9SHerbert Xu return err; 4911a6509d9SHerbert Xu } 4921a6509d9SHerbert Xu 4931a6509d9SHerbert Xu static int esp_init_authenc(struct xfrm_state *x) 4941a6509d9SHerbert Xu { 4951a6509d9SHerbert Xu struct esp_data *esp = x->data; 49638320c70SHerbert Xu struct crypto_aead *aead; 49738320c70SHerbert Xu struct crypto_authenc_key_param *param; 49838320c70SHerbert Xu struct rtattr *rta; 49938320c70SHerbert Xu char *key; 50038320c70SHerbert Xu char *p; 50138320c70SHerbert Xu char authenc_name[CRYPTO_MAX_ALG_NAME]; 50238320c70SHerbert Xu unsigned int keylen; 50338320c70SHerbert Xu int err; 5041da177e4SLinus Torvalds 5051a6509d9SHerbert Xu err = -EINVAL; 5061da177e4SLinus Torvalds if (x->ealg == NULL) 5071a6509d9SHerbert Xu goto error; 5081da177e4SLinus Torvalds 5091a6509d9SHerbert Xu err = -ENAMETOOLONG; 510d212a4c2SSteffen Klassert 511d212a4c2SSteffen Klassert if ((x->props.flags & XFRM_STATE_ESN)) { 512d212a4c2SSteffen Klassert if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME, 513d212a4c2SSteffen Klassert "authencesn(%s,%s)", 51438320c70SHerbert Xu x->aalg ? x->aalg->alg_name : "digest_null", 51538320c70SHerbert Xu x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME) 5161a6509d9SHerbert Xu goto error; 517d212a4c2SSteffen Klassert } else { 518d212a4c2SSteffen Klassert if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME, 519d212a4c2SSteffen Klassert "authenc(%s,%s)", 520d212a4c2SSteffen Klassert x->aalg ? x->aalg->alg_name : "digest_null", 521d212a4c2SSteffen Klassert x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME) 522d212a4c2SSteffen Klassert goto error; 523d212a4c2SSteffen Klassert } 52438320c70SHerbert Xu 52538320c70SHerbert Xu aead = crypto_alloc_aead(authenc_name, 0, 0); 52638320c70SHerbert Xu err = PTR_ERR(aead); 52738320c70SHerbert Xu if (IS_ERR(aead)) 52838320c70SHerbert Xu goto error; 52938320c70SHerbert Xu 53038320c70SHerbert Xu esp->aead = aead; 53138320c70SHerbert Xu 53238320c70SHerbert Xu keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) + 53338320c70SHerbert Xu (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param)); 53438320c70SHerbert Xu err = -ENOMEM; 53538320c70SHerbert Xu key = kmalloc(keylen, GFP_KERNEL); 53638320c70SHerbert Xu if (!key) 53738320c70SHerbert Xu goto error; 53838320c70SHerbert Xu 53938320c70SHerbert Xu p = key; 54038320c70SHerbert Xu rta = (void *)p; 54138320c70SHerbert Xu rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM; 54238320c70SHerbert Xu rta->rta_len = RTA_LENGTH(sizeof(*param)); 54338320c70SHerbert Xu param = RTA_DATA(rta); 54438320c70SHerbert Xu p += RTA_SPACE(sizeof(*param)); 54538320c70SHerbert Xu 5461da177e4SLinus Torvalds if (x->aalg) { 5471da177e4SLinus Torvalds struct xfrm_algo_desc *aalg_desc; 5481da177e4SLinus Torvalds 54938320c70SHerbert Xu memcpy(p, x->aalg->alg_key, (x->aalg->alg_key_len + 7) / 8); 55038320c70SHerbert Xu p += (x->aalg->alg_key_len + 7) / 8; 5511da177e4SLinus Torvalds 5521da177e4SLinus Torvalds aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0); 5531da177e4SLinus Torvalds BUG_ON(!aalg_desc); 5541da177e4SLinus Torvalds 55538320c70SHerbert Xu err = -EINVAL; 5561da177e4SLinus Torvalds if (aalg_desc->uinfo.auth.icv_fullbits/8 != 55738320c70SHerbert Xu crypto_aead_authsize(aead)) { 55807d4ee58SHerbert Xu NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n", 5591da177e4SLinus Torvalds x->aalg->alg_name, 56038320c70SHerbert Xu crypto_aead_authsize(aead), 5611da177e4SLinus Torvalds aalg_desc->uinfo.auth.icv_fullbits/8); 56238320c70SHerbert Xu goto free_key; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds 56538320c70SHerbert Xu err = crypto_aead_setauthsize( 5668f8a088cSMartin Willi aead, x->aalg->alg_trunc_len / 8); 56738320c70SHerbert Xu if (err) 56838320c70SHerbert Xu goto free_key; 56938320c70SHerbert Xu } 5701da177e4SLinus Torvalds 57138320c70SHerbert Xu param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8); 57238320c70SHerbert Xu memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8); 57338320c70SHerbert Xu 57438320c70SHerbert Xu err = crypto_aead_setkey(aead, key, keylen); 57538320c70SHerbert Xu 57638320c70SHerbert Xu free_key: 57738320c70SHerbert Xu kfree(key); 57838320c70SHerbert Xu 5791a6509d9SHerbert Xu error: 5801a6509d9SHerbert Xu return err; 5811a6509d9SHerbert Xu } 5821a6509d9SHerbert Xu 5831a6509d9SHerbert Xu static int esp6_init_state(struct xfrm_state *x) 5841a6509d9SHerbert Xu { 5851a6509d9SHerbert Xu struct esp_data *esp; 5861a6509d9SHerbert Xu struct crypto_aead *aead; 5871a6509d9SHerbert Xu u32 align; 5881a6509d9SHerbert Xu int err; 5891a6509d9SHerbert Xu 5901a6509d9SHerbert Xu if (x->encap) 5911a6509d9SHerbert Xu return -EINVAL; 5921a6509d9SHerbert Xu 5931a6509d9SHerbert Xu esp = kzalloc(sizeof(*esp), GFP_KERNEL); 5941a6509d9SHerbert Xu if (esp == NULL) 5951a6509d9SHerbert Xu return -ENOMEM; 5961a6509d9SHerbert Xu 5971a6509d9SHerbert Xu x->data = esp; 5981a6509d9SHerbert Xu 5991a6509d9SHerbert Xu if (x->aead) 6001a6509d9SHerbert Xu err = esp_init_aead(x); 6011a6509d9SHerbert Xu else 6021a6509d9SHerbert Xu err = esp_init_authenc(x); 6031a6509d9SHerbert Xu 60438320c70SHerbert Xu if (err) 6051da177e4SLinus Torvalds goto error; 60638320c70SHerbert Xu 6071a6509d9SHerbert Xu aead = esp->aead; 6081a6509d9SHerbert Xu 6091a6509d9SHerbert Xu esp->padlen = 0; 6101a6509d9SHerbert Xu 61138320c70SHerbert Xu x->props.header_len = sizeof(struct ip_esp_hdr) + 61238320c70SHerbert Xu crypto_aead_ivsize(aead); 613ca68145fSHerbert Xu switch (x->props.mode) { 614ca68145fSHerbert Xu case XFRM_MODE_BEET: 615abf5cdb8SJoakim Koskela if (x->sel.family != AF_INET6) 616abf5cdb8SJoakim Koskela x->props.header_len += IPV4_BEET_PHMAXLEN + 617abf5cdb8SJoakim Koskela (sizeof(struct ipv6hdr) - sizeof(struct iphdr)); 618abf5cdb8SJoakim Koskela break; 619ca68145fSHerbert Xu case XFRM_MODE_TRANSPORT: 620ca68145fSHerbert Xu break; 621ca68145fSHerbert Xu case XFRM_MODE_TUNNEL: 6221da177e4SLinus Torvalds x->props.header_len += sizeof(struct ipv6hdr); 623ea2c47b4SMasahide NAKAMURA break; 624ca68145fSHerbert Xu default: 625ca68145fSHerbert Xu goto error; 626ca68145fSHerbert Xu } 62738320c70SHerbert Xu 62838320c70SHerbert Xu align = ALIGN(crypto_aead_blocksize(aead), 4); 62938320c70SHerbert Xu if (esp->padlen) 63038320c70SHerbert Xu align = max_t(u32, align, esp->padlen); 63138320c70SHerbert Xu x->props.trailer_len = align + 1 + crypto_aead_authsize(esp->aead); 6321da177e4SLinus Torvalds 6331da177e4SLinus Torvalds error: 63438320c70SHerbert Xu return err; 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds 637533cb5b0SEric Dumazet static const struct xfrm_type esp6_type = 6381da177e4SLinus Torvalds { 6391da177e4SLinus Torvalds .description = "ESP6", 6401da177e4SLinus Torvalds .owner = THIS_MODULE, 6411da177e4SLinus Torvalds .proto = IPPROTO_ESP, 642436a0a40SHerbert Xu .flags = XFRM_TYPE_REPLAY_PROT, 6431da177e4SLinus Torvalds .init_state = esp6_init_state, 6441da177e4SLinus Torvalds .destructor = esp6_destroy, 645c5c25238SPatrick McHardy .get_mtu = esp6_get_mtu, 6461da177e4SLinus Torvalds .input = esp6_input, 647aee5adb4SMasahide NAKAMURA .output = esp6_output, 648aee5adb4SMasahide NAKAMURA .hdr_offset = xfrm6_find_1stfragopt, 6491da177e4SLinus Torvalds }; 6501da177e4SLinus Torvalds 65141135cc8SAlexey Dobriyan static const struct inet6_protocol esp6_protocol = { 6521da177e4SLinus Torvalds .handler = xfrm6_rcv, 6531da177e4SLinus Torvalds .err_handler = esp6_err, 6541da177e4SLinus Torvalds .flags = INET6_PROTO_NOPOLICY, 6551da177e4SLinus Torvalds }; 6561da177e4SLinus Torvalds 6571da177e4SLinus Torvalds static int __init esp6_init(void) 6581da177e4SLinus Torvalds { 6591da177e4SLinus Torvalds if (xfrm_register_type(&esp6_type, AF_INET6) < 0) { 660f3213831SJoe Perches pr_info("%s: can't add xfrm type\n", __func__); 6611da177e4SLinus Torvalds return -EAGAIN; 6621da177e4SLinus Torvalds } 6631da177e4SLinus Torvalds if (inet6_add_protocol(&esp6_protocol, IPPROTO_ESP) < 0) { 664f3213831SJoe Perches pr_info("%s: can't add protocol\n", __func__); 6651da177e4SLinus Torvalds xfrm_unregister_type(&esp6_type, AF_INET6); 6661da177e4SLinus Torvalds return -EAGAIN; 6671da177e4SLinus Torvalds } 6681da177e4SLinus Torvalds 6691da177e4SLinus Torvalds return 0; 6701da177e4SLinus Torvalds } 6711da177e4SLinus Torvalds 6721da177e4SLinus Torvalds static void __exit esp6_fini(void) 6731da177e4SLinus Torvalds { 6741da177e4SLinus Torvalds if (inet6_del_protocol(&esp6_protocol, IPPROTO_ESP) < 0) 675f3213831SJoe Perches pr_info("%s: can't remove protocol\n", __func__); 6761da177e4SLinus Torvalds if (xfrm_unregister_type(&esp6_type, AF_INET6) < 0) 677f3213831SJoe Perches pr_info("%s: can't remove xfrm type\n", __func__); 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds module_init(esp6_init); 6811da177e4SLinus Torvalds module_exit(esp6_fini); 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 684d3d6dd3aSMasahide NAKAMURA MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ESP); 685