xref: /openbmc/linux/net/ipv6/esp6.c (revision a9403f8a)
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