xref: /linux/net/ipv4/esp4.c (revision 32613090a96dba2ca2cc524c8d4749d3126fdde5)
138320c70SHerbert Xu #include <crypto/aead.h>
238320c70SHerbert Xu #include <crypto/authenc.h>
36b7326c8SHerbert Xu #include <linux/err.h>
41da177e4SLinus Torvalds #include <linux/module.h>
51da177e4SLinus Torvalds #include <net/ip.h>
61da177e4SLinus Torvalds #include <net/xfrm.h>
71da177e4SLinus Torvalds #include <net/esp.h>
872998d8cSAdrian Bunk #include <linux/scatterlist.h>
9a02a6422SHerbert Xu #include <linux/kernel.h>
101da177e4SLinus Torvalds #include <linux/pfkeyv2.h>
1138320c70SHerbert Xu #include <linux/rtnetlink.h>
1238320c70SHerbert Xu #include <linux/slab.h>
13b7c6538cSHerbert Xu #include <linux/spinlock.h>
142017a72cSThomas Graf #include <linux/in6.h>
151da177e4SLinus Torvalds #include <net/icmp.h>
1614c85021SArnaldo Carvalho de Melo #include <net/protocol.h>
171da177e4SLinus Torvalds #include <net/udp.h>
181da177e4SLinus Torvalds 
1938320c70SHerbert Xu struct esp_skb_cb {
2038320c70SHerbert Xu 	struct xfrm_skb_cb xfrm;
2138320c70SHerbert Xu 	void *tmp;
2238320c70SHerbert Xu };
2338320c70SHerbert Xu 
2438320c70SHerbert Xu #define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0]))
2538320c70SHerbert Xu 
2638320c70SHerbert Xu /*
2738320c70SHerbert Xu  * Allocate an AEAD request structure with extra space for SG and IV.
2838320c70SHerbert Xu  *
2938320c70SHerbert Xu  * For alignment considerations the IV is placed at the front, followed
3038320c70SHerbert Xu  * by the request and finally the SG list.
3138320c70SHerbert Xu  *
3238320c70SHerbert Xu  * TODO: Use spare space in skb for this where possible.
3338320c70SHerbert Xu  */
3438320c70SHerbert Xu static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags)
3538320c70SHerbert Xu {
3638320c70SHerbert Xu 	unsigned int len;
3738320c70SHerbert Xu 
3838320c70SHerbert Xu 	len = crypto_aead_ivsize(aead);
3938320c70SHerbert Xu 	if (len) {
4038320c70SHerbert Xu 		len += crypto_aead_alignmask(aead) &
4138320c70SHerbert Xu 		       ~(crypto_tfm_ctx_alignment() - 1);
4238320c70SHerbert Xu 		len = ALIGN(len, crypto_tfm_ctx_alignment());
4338320c70SHerbert Xu 	}
4438320c70SHerbert Xu 
4538320c70SHerbert Xu 	len += sizeof(struct aead_givcrypt_request) + crypto_aead_reqsize(aead);
4638320c70SHerbert Xu 	len = ALIGN(len, __alignof__(struct scatterlist));
4738320c70SHerbert Xu 
4838320c70SHerbert Xu 	len += sizeof(struct scatterlist) * nfrags;
4938320c70SHerbert Xu 
5038320c70SHerbert Xu 	return kmalloc(len, GFP_ATOMIC);
5138320c70SHerbert Xu }
5238320c70SHerbert Xu 
5338320c70SHerbert Xu static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp)
5438320c70SHerbert Xu {
5538320c70SHerbert Xu 	return crypto_aead_ivsize(aead) ?
5638320c70SHerbert Xu 	       PTR_ALIGN((u8 *)tmp, crypto_aead_alignmask(aead) + 1) : tmp;
5738320c70SHerbert Xu }
5838320c70SHerbert Xu 
5938320c70SHerbert Xu static inline struct aead_givcrypt_request *esp_tmp_givreq(
6038320c70SHerbert Xu 	struct crypto_aead *aead, u8 *iv)
6138320c70SHerbert Xu {
6238320c70SHerbert Xu 	struct aead_givcrypt_request *req;
6338320c70SHerbert Xu 
6438320c70SHerbert Xu 	req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),
6538320c70SHerbert Xu 				crypto_tfm_ctx_alignment());
6638320c70SHerbert Xu 	aead_givcrypt_set_tfm(req, aead);
6738320c70SHerbert Xu 	return req;
6838320c70SHerbert Xu }
6938320c70SHerbert Xu 
7038320c70SHerbert Xu static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv)
7138320c70SHerbert Xu {
7238320c70SHerbert Xu 	struct aead_request *req;
7338320c70SHerbert Xu 
7438320c70SHerbert Xu 	req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),
7538320c70SHerbert Xu 				crypto_tfm_ctx_alignment());
7638320c70SHerbert Xu 	aead_request_set_tfm(req, aead);
7738320c70SHerbert Xu 	return req;
7838320c70SHerbert Xu }
7938320c70SHerbert Xu 
8038320c70SHerbert Xu static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,
8138320c70SHerbert Xu 					     struct aead_request *req)
8238320c70SHerbert Xu {
8338320c70SHerbert Xu 	return (void *)ALIGN((unsigned long)(req + 1) +
8438320c70SHerbert Xu 			     crypto_aead_reqsize(aead),
8538320c70SHerbert Xu 			     __alignof__(struct scatterlist));
8638320c70SHerbert Xu }
8738320c70SHerbert Xu 
8838320c70SHerbert Xu static inline struct scatterlist *esp_givreq_sg(
8938320c70SHerbert Xu 	struct crypto_aead *aead, struct aead_givcrypt_request *req)
9038320c70SHerbert Xu {
9138320c70SHerbert Xu 	return (void *)ALIGN((unsigned long)(req + 1) +
9238320c70SHerbert Xu 			     crypto_aead_reqsize(aead),
9338320c70SHerbert Xu 			     __alignof__(struct scatterlist));
9438320c70SHerbert Xu }
9538320c70SHerbert Xu 
9638320c70SHerbert Xu static void esp_output_done(struct crypto_async_request *base, int err)
9738320c70SHerbert Xu {
9838320c70SHerbert Xu 	struct sk_buff *skb = base->data;
9938320c70SHerbert Xu 
10038320c70SHerbert Xu 	kfree(ESP_SKB_CB(skb)->tmp);
10138320c70SHerbert Xu 	xfrm_output_resume(skb, err);
10238320c70SHerbert Xu }
10338320c70SHerbert Xu 
1041da177e4SLinus Torvalds static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
1051da177e4SLinus Torvalds {
1061da177e4SLinus Torvalds 	int err;
1071da177e4SLinus Torvalds 	struct ip_esp_hdr *esph;
10838320c70SHerbert Xu 	struct crypto_aead *aead;
10938320c70SHerbert Xu 	struct aead_givcrypt_request *req;
11038320c70SHerbert Xu 	struct scatterlist *sg;
11138320c70SHerbert Xu 	struct scatterlist *asg;
1121da177e4SLinus Torvalds 	struct esp_data *esp;
1131da177e4SLinus Torvalds 	struct sk_buff *trailer;
11438320c70SHerbert Xu 	void *tmp;
11538320c70SHerbert Xu 	u8 *iv;
11627a884dcSArnaldo Carvalho de Melo 	u8 *tail;
1171da177e4SLinus Torvalds 	int blksize;
1181da177e4SLinus Torvalds 	int clen;
1191da177e4SLinus Torvalds 	int alen;
1201da177e4SLinus Torvalds 	int nfrags;
1211da177e4SLinus Torvalds 
1227b277b1aSHerbert Xu 	/* skb is pure payload to encrypt */
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	err = -ENOMEM;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	/* Round to block size */
1271da177e4SLinus Torvalds 	clen = skb->len;
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	esp = x->data;
13038320c70SHerbert Xu 	aead = esp->aead;
13138320c70SHerbert Xu 	alen = crypto_aead_authsize(aead);
1321da177e4SLinus Torvalds 
13338320c70SHerbert Xu 	blksize = ALIGN(crypto_aead_blocksize(aead), 4);
13438320c70SHerbert Xu 	clen = ALIGN(clen + 2, blksize);
13538320c70SHerbert Xu 	if (esp->padlen)
13638320c70SHerbert Xu 		clen = ALIGN(clen, esp->padlen);
13738320c70SHerbert Xu 
13838320c70SHerbert Xu 	if ((err = skb_cow_data(skb, clen - skb->len + alen, &trailer)) < 0)
1391da177e4SLinus Torvalds 		goto error;
14038320c70SHerbert Xu 	nfrags = err;
14138320c70SHerbert Xu 
14238320c70SHerbert Xu 	tmp = esp_alloc_tmp(aead, nfrags + 1);
14338320c70SHerbert Xu 	if (!tmp)
14438320c70SHerbert Xu 		goto error;
14538320c70SHerbert Xu 
14638320c70SHerbert Xu 	iv = esp_tmp_iv(aead, tmp);
14738320c70SHerbert Xu 	req = esp_tmp_givreq(aead, iv);
14838320c70SHerbert Xu 	asg = esp_givreq_sg(aead, req);
14938320c70SHerbert Xu 	sg = asg + 1;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	/* Fill padding... */
15227a884dcSArnaldo Carvalho de Melo 	tail = skb_tail_pointer(trailer);
1531da177e4SLinus Torvalds 	do {
1541da177e4SLinus Torvalds 		int i;
1551da177e4SLinus Torvalds 		for (i=0; i<clen-skb->len - 2; i++)
15627a884dcSArnaldo Carvalho de Melo 			tail[i] = i + 1;
1571da177e4SLinus Torvalds 	} while (0);
15827a884dcSArnaldo Carvalho de Melo 	tail[clen - skb->len - 2] = (clen - skb->len) - 2;
15938320c70SHerbert Xu 	tail[clen - skb->len - 1] = *skb_mac_header(skb);
16038320c70SHerbert Xu 	pskb_put(skb, trailer, clen - skb->len + alen);
1611da177e4SLinus Torvalds 
1627b277b1aSHerbert Xu 	skb_push(skb, -skb_network_offset(skb));
16387bdc48dSHerbert Xu 	esph = ip_esp_hdr(skb);
16437fedd3aSHerbert Xu 	*skb_mac_header(skb) = IPPROTO_ESP;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	/* this is non-NULL only with UDP Encapsulation */
1671da177e4SLinus Torvalds 	if (x->encap) {
1681da177e4SLinus Torvalds 		struct xfrm_encap_tmpl *encap = x->encap;
1691da177e4SLinus Torvalds 		struct udphdr *uh;
170d5a0a1e3SAl Viro 		__be32 *udpdata32;
1715e226e4dSAl Viro 		__be16 sport, dport;
17238320c70SHerbert Xu 		int encap_type;
17338320c70SHerbert Xu 
17438320c70SHerbert Xu 		spin_lock_bh(&x->lock);
17538320c70SHerbert Xu 		sport = encap->encap_sport;
17638320c70SHerbert Xu 		dport = encap->encap_dport;
17738320c70SHerbert Xu 		encap_type = encap->encap_type;
17838320c70SHerbert Xu 		spin_unlock_bh(&x->lock);
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 		uh = (struct udphdr *)esph;
18138320c70SHerbert Xu 		uh->source = sport;
18238320c70SHerbert Xu 		uh->dest = dport;
18338320c70SHerbert Xu 		uh->len = htons(skb->len - skb_transport_offset(skb));
1841da177e4SLinus Torvalds 		uh->check = 0;
1851da177e4SLinus Torvalds 
18638320c70SHerbert Xu 		switch (encap_type) {
1871da177e4SLinus Torvalds 		default:
1881da177e4SLinus Torvalds 		case UDP_ENCAP_ESPINUDP:
1891da177e4SLinus Torvalds 			esph = (struct ip_esp_hdr *)(uh + 1);
1901da177e4SLinus Torvalds 			break;
1911da177e4SLinus Torvalds 		case UDP_ENCAP_ESPINUDP_NON_IKE:
192d5a0a1e3SAl Viro 			udpdata32 = (__be32 *)(uh + 1);
1931da177e4SLinus Torvalds 			udpdata32[0] = udpdata32[1] = 0;
1941da177e4SLinus Torvalds 			esph = (struct ip_esp_hdr *)(udpdata32 + 2);
1951da177e4SLinus Torvalds 			break;
1961da177e4SLinus Torvalds 		}
1971da177e4SLinus Torvalds 
19837fedd3aSHerbert Xu 		*skb_mac_header(skb) = IPPROTO_UDP;
19937fedd3aSHerbert Xu 	}
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds 	esph->spi = x->id.spi;
202b318e0e4SHerbert Xu 	esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output);
2031da177e4SLinus Torvalds 
204ed0e7e0cSDavid S. Miller 	sg_init_table(sg, nfrags);
20551c739d1SDavid S. Miller 	skb_to_sgvec(skb, sg,
20638320c70SHerbert Xu 		     esph->enc_data + crypto_aead_ivsize(aead) - skb->data,
20738320c70SHerbert Xu 		     clen + alen);
20838320c70SHerbert Xu 	sg_init_one(asg, esph, sizeof(*esph));
2091da177e4SLinus Torvalds 
21038320c70SHerbert Xu 	aead_givcrypt_set_callback(req, 0, esp_output_done, skb);
21138320c70SHerbert Xu 	aead_givcrypt_set_crypt(req, sg, sg, clen, iv);
21238320c70SHerbert Xu 	aead_givcrypt_set_assoc(req, asg, sizeof(*esph));
213b318e0e4SHerbert Xu 	aead_givcrypt_set_giv(req, esph->enc_data,
214b318e0e4SHerbert Xu 			      XFRM_SKB_CB(skb)->seq.output);
2156b7326c8SHerbert Xu 
21638320c70SHerbert Xu 	ESP_SKB_CB(skb)->tmp = tmp;
21738320c70SHerbert Xu 	err = crypto_aead_givencrypt(req);
21838320c70SHerbert Xu 	if (err == -EINPROGRESS)
21938320c70SHerbert Xu 		goto error;
2201da177e4SLinus Torvalds 
22138320c70SHerbert Xu 	if (err == -EBUSY)
22238320c70SHerbert Xu 		err = NET_XMIT_DROP;
2231da177e4SLinus Torvalds 
22438320c70SHerbert Xu 	kfree(tmp);
225b7c6538cSHerbert Xu 
2261da177e4SLinus Torvalds error:
2271da177e4SLinus Torvalds 	return err;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
23038320c70SHerbert Xu static int esp_input_done2(struct sk_buff *skb, int err)
2311da177e4SLinus Torvalds {
2321da177e4SLinus Torvalds 	struct iphdr *iph;
23338320c70SHerbert Xu 	struct xfrm_state *x = xfrm_input_state(skb);
2341da177e4SLinus Torvalds 	struct esp_data *esp = x->data;
23538320c70SHerbert Xu 	struct crypto_aead *aead = esp->aead;
23638320c70SHerbert Xu 	int alen = crypto_aead_authsize(aead);
23738320c70SHerbert Xu 	int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead);
23838320c70SHerbert Xu 	int elen = skb->len - hlen;
23931a4ab93SHerbert Xu 	int ihl;
2404bf05eceSHerbert Xu 	u8 nexthdr[2];
2414bf05eceSHerbert Xu 	int padlen;
2421da177e4SLinus Torvalds 
24338320c70SHerbert Xu 	kfree(ESP_SKB_CB(skb)->tmp);
2440ebea8efSHerbert Xu 
2456b7326c8SHerbert Xu 	if (unlikely(err))
246668dc8afSHerbert Xu 		goto out;
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
2491da177e4SLinus Torvalds 		BUG();
2501da177e4SLinus Torvalds 
251668dc8afSHerbert Xu 	err = -EINVAL;
2521da177e4SLinus Torvalds 	padlen = nexthdr[0];
25338320c70SHerbert Xu 	if (padlen + 2 + alen >= elen)
2541da177e4SLinus Torvalds 		goto out;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	/* ... check padding bits here. Silly. :-) */
2571da177e4SLinus Torvalds 
258eddc9ec5SArnaldo Carvalho de Melo 	iph = ip_hdr(skb);
25931a4ab93SHerbert Xu 	ihl = iph->ihl * 4;
26031a4ab93SHerbert Xu 
2611da177e4SLinus Torvalds 	if (x->encap) {
262752c1f4cSHerbert Xu 		struct xfrm_encap_tmpl *encap = x->encap;
263d56f90a7SArnaldo Carvalho de Melo 		struct udphdr *uh = (void *)(skb_network_header(skb) + ihl);
264752c1f4cSHerbert Xu 
2651da177e4SLinus Torvalds 		/*
2661da177e4SLinus Torvalds 		 * 1) if the NAT-T peer's IP or port changed then
2671da177e4SLinus Torvalds 		 *    advertize the change to the keying daemon.
2681da177e4SLinus Torvalds 		 *    This is an inbound SA, so just compare
2691da177e4SLinus Torvalds 		 *    SRC ports.
2701da177e4SLinus Torvalds 		 */
271752c1f4cSHerbert Xu 		if (iph->saddr != x->props.saddr.a4 ||
272752c1f4cSHerbert Xu 		    uh->source != encap->encap_sport) {
2731da177e4SLinus Torvalds 			xfrm_address_t ipaddr;
2741da177e4SLinus Torvalds 
275752c1f4cSHerbert Xu 			ipaddr.a4 = iph->saddr;
276752c1f4cSHerbert Xu 			km_new_mapping(x, &ipaddr, uh->source);
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 			/* XXX: perhaps add an extra
2791da177e4SLinus Torvalds 			 * policy check here, to see
2801da177e4SLinus Torvalds 			 * if we should allow or
2811da177e4SLinus Torvalds 			 * reject a packet from a
2821da177e4SLinus Torvalds 			 * different source
2831da177e4SLinus Torvalds 			 * address/port.
2841da177e4SLinus Torvalds 			 */
2851da177e4SLinus Torvalds 		}
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 		/*
2881da177e4SLinus Torvalds 		 * 2) ignore UDP/TCP checksums in case
2891da177e4SLinus Torvalds 		 *    of NAT-T in Transport Mode, or
2901da177e4SLinus Torvalds 		 *    perform other post-processing fixes
291752c1f4cSHerbert Xu 		 *    as per draft-ietf-ipsec-udp-encaps-06,
2921da177e4SLinus Torvalds 		 *    section 3.1.2
2931da177e4SLinus Torvalds 		 */
2948bd17075SHerbert Xu 		if (x->props.mode == XFRM_MODE_TRANSPORT)
2951da177e4SLinus Torvalds 			skb->ip_summed = CHECKSUM_UNNECESSARY;
296752c1f4cSHerbert Xu 	}
2971da177e4SLinus Torvalds 
298752c1f4cSHerbert Xu 	pskb_trim(skb, skb->len - alen - padlen - 2);
29938320c70SHerbert Xu 	__skb_pull(skb, hlen);
300967b05f6SArnaldo Carvalho de Melo 	skb_set_transport_header(skb, -ihl);
301752c1f4cSHerbert Xu 
30238320c70SHerbert Xu 	err = nexthdr[1];
30338320c70SHerbert Xu 
30438320c70SHerbert Xu 	/* RFC4303: Drop dummy packets without any error */
30538320c70SHerbert Xu 	if (err == IPPROTO_NONE)
30638320c70SHerbert Xu 		err = -EINVAL;
30738320c70SHerbert Xu 
30838320c70SHerbert Xu out:
30938320c70SHerbert Xu 	return err;
31038320c70SHerbert Xu }
31138320c70SHerbert Xu 
31238320c70SHerbert Xu static void esp_input_done(struct crypto_async_request *base, int err)
31338320c70SHerbert Xu {
31438320c70SHerbert Xu 	struct sk_buff *skb = base->data;
31538320c70SHerbert Xu 
31638320c70SHerbert Xu 	xfrm_input_resume(skb, esp_input_done2(skb, err));
31738320c70SHerbert Xu }
31838320c70SHerbert Xu 
31938320c70SHerbert Xu /*
32038320c70SHerbert Xu  * Note: detecting truncated vs. non-truncated authentication data is very
32138320c70SHerbert Xu  * expensive, so we only support truncated data, which is the recommended
32238320c70SHerbert Xu  * and common case.
32338320c70SHerbert Xu  */
32438320c70SHerbert Xu static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
32538320c70SHerbert Xu {
32638320c70SHerbert Xu 	struct ip_esp_hdr *esph;
32738320c70SHerbert Xu 	struct esp_data *esp = x->data;
32838320c70SHerbert Xu 	struct crypto_aead *aead = esp->aead;
32938320c70SHerbert Xu 	struct aead_request *req;
33038320c70SHerbert Xu 	struct sk_buff *trailer;
33138320c70SHerbert Xu 	int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead);
33238320c70SHerbert Xu 	int nfrags;
33338320c70SHerbert Xu 	void *tmp;
33438320c70SHerbert Xu 	u8 *iv;
33538320c70SHerbert Xu 	struct scatterlist *sg;
33638320c70SHerbert Xu 	struct scatterlist *asg;
33738320c70SHerbert Xu 	int err = -EINVAL;
33838320c70SHerbert Xu 
339920fc941SThomas Graf 	if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)))
34038320c70SHerbert Xu 		goto out;
34138320c70SHerbert Xu 
34238320c70SHerbert Xu 	if (elen <= 0)
34338320c70SHerbert Xu 		goto out;
34438320c70SHerbert Xu 
34538320c70SHerbert Xu 	if ((err = skb_cow_data(skb, 0, &trailer)) < 0)
34638320c70SHerbert Xu 		goto out;
34738320c70SHerbert Xu 	nfrags = err;
34838320c70SHerbert Xu 
34938320c70SHerbert Xu 	err = -ENOMEM;
35038320c70SHerbert Xu 	tmp = esp_alloc_tmp(aead, nfrags + 1);
35138320c70SHerbert Xu 	if (!tmp)
35238320c70SHerbert Xu 		goto out;
35338320c70SHerbert Xu 
35438320c70SHerbert Xu 	ESP_SKB_CB(skb)->tmp = tmp;
35538320c70SHerbert Xu 	iv = esp_tmp_iv(aead, tmp);
35638320c70SHerbert Xu 	req = esp_tmp_req(aead, iv);
35738320c70SHerbert Xu 	asg = esp_req_sg(aead, req);
35838320c70SHerbert Xu 	sg = asg + 1;
35938320c70SHerbert Xu 
36038320c70SHerbert Xu 	skb->ip_summed = CHECKSUM_NONE;
36138320c70SHerbert Xu 
36238320c70SHerbert Xu 	esph = (struct ip_esp_hdr *)skb->data;
36338320c70SHerbert Xu 
36438320c70SHerbert Xu 	/* Get ivec. This can be wrong, check against another impls. */
36538320c70SHerbert Xu 	iv = esph->enc_data;
36638320c70SHerbert Xu 
36738320c70SHerbert Xu 	sg_init_table(sg, nfrags);
36838320c70SHerbert Xu 	skb_to_sgvec(skb, sg, sizeof(*esph) + crypto_aead_ivsize(aead), elen);
36938320c70SHerbert Xu 	sg_init_one(asg, esph, sizeof(*esph));
37038320c70SHerbert Xu 
37138320c70SHerbert Xu 	aead_request_set_callback(req, 0, esp_input_done, skb);
37238320c70SHerbert Xu 	aead_request_set_crypt(req, sg, sg, elen, iv);
37338320c70SHerbert Xu 	aead_request_set_assoc(req, asg, sizeof(*esph));
37438320c70SHerbert Xu 
37538320c70SHerbert Xu 	err = crypto_aead_decrypt(req);
37638320c70SHerbert Xu 	if (err == -EINPROGRESS)
37738320c70SHerbert Xu 		goto out;
37838320c70SHerbert Xu 
37938320c70SHerbert Xu 	err = esp_input_done2(skb, err);
380752c1f4cSHerbert Xu 
381752c1f4cSHerbert Xu out:
382668dc8afSHerbert Xu 	return err;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds 
385c5c25238SPatrick McHardy static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
3861da177e4SLinus Torvalds {
3871da177e4SLinus Torvalds 	struct esp_data *esp = x->data;
38838320c70SHerbert Xu 	u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
38938320c70SHerbert Xu 	u32 align = max_t(u32, blksize, esp->padlen);
390c5c25238SPatrick McHardy 	u32 rem;
391c5c25238SPatrick McHardy 
39238320c70SHerbert Xu 	mtu -= x->props.header_len + crypto_aead_authsize(esp->aead);
393c5c25238SPatrick McHardy 	rem = mtu & (align - 1);
394c5c25238SPatrick McHardy 	mtu &= ~(align - 1);
3951da177e4SLinus Torvalds 
3960a69452cSDiego Beltrami 	switch (x->props.mode) {
3970a69452cSDiego Beltrami 	case XFRM_MODE_TUNNEL:
3980a69452cSDiego Beltrami 		break;
3990a69452cSDiego Beltrami 	default:
4000a69452cSDiego Beltrami 	case XFRM_MODE_TRANSPORT:
4010a69452cSDiego Beltrami 		/* The worst case */
402c5c25238SPatrick McHardy 		mtu -= blksize - 4;
403c5c25238SPatrick McHardy 		mtu += min_t(u32, blksize - 4, rem);
4040a69452cSDiego Beltrami 		break;
4050a69452cSDiego Beltrami 	case XFRM_MODE_BEET:
4060a69452cSDiego Beltrami 		/* The worst case. */
407c5c25238SPatrick McHardy 		mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem);
4080a69452cSDiego Beltrami 		break;
4091da177e4SLinus Torvalds 	}
4100a69452cSDiego Beltrami 
411c5c25238SPatrick McHardy 	return mtu - 2;
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds static void esp4_err(struct sk_buff *skb, u32 info)
4151da177e4SLinus Torvalds {
4164fb236baSAlexey Dobriyan 	struct net *net = dev_net(skb->dev);
4171da177e4SLinus Torvalds 	struct iphdr *iph = (struct iphdr *)skb->data;
4181da177e4SLinus Torvalds 	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2));
4191da177e4SLinus Torvalds 	struct xfrm_state *x;
4201da177e4SLinus Torvalds 
42188c7664fSArnaldo Carvalho de Melo 	if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
42288c7664fSArnaldo Carvalho de Melo 	    icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
4231da177e4SLinus Torvalds 		return;
4241da177e4SLinus Torvalds 
4254fb236baSAlexey Dobriyan 	x = xfrm_state_lookup(net, (xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
4261da177e4SLinus Torvalds 	if (!x)
4271da177e4SLinus Torvalds 		return;
42864ce2073SPatrick McHardy 	NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n",
42964ce2073SPatrick McHardy 		 ntohl(esph->spi), ntohl(iph->daddr));
4301da177e4SLinus Torvalds 	xfrm_state_put(x);
4311da177e4SLinus Torvalds }
4321da177e4SLinus Torvalds 
4331da177e4SLinus Torvalds static void esp_destroy(struct xfrm_state *x)
4341da177e4SLinus Torvalds {
4351da177e4SLinus Torvalds 	struct esp_data *esp = x->data;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	if (!esp)
4381da177e4SLinus Torvalds 		return;
4391da177e4SLinus Torvalds 
44038320c70SHerbert Xu 	crypto_free_aead(esp->aead);
4411da177e4SLinus Torvalds 	kfree(esp);
4421da177e4SLinus Torvalds }
4431da177e4SLinus Torvalds 
4441a6509d9SHerbert Xu static int esp_init_aead(struct xfrm_state *x)
4451da177e4SLinus Torvalds {
4461a6509d9SHerbert Xu 	struct esp_data *esp = x->data;
4471a6509d9SHerbert Xu 	struct crypto_aead *aead;
4481a6509d9SHerbert Xu 	int err;
4491a6509d9SHerbert Xu 
4501a6509d9SHerbert Xu 	aead = crypto_alloc_aead(x->aead->alg_name, 0, 0);
4511a6509d9SHerbert Xu 	err = PTR_ERR(aead);
4521a6509d9SHerbert Xu 	if (IS_ERR(aead))
4531a6509d9SHerbert Xu 		goto error;
4541a6509d9SHerbert Xu 
4551a6509d9SHerbert Xu 	esp->aead = aead;
4561a6509d9SHerbert Xu 
4571a6509d9SHerbert Xu 	err = crypto_aead_setkey(aead, x->aead->alg_key,
4581a6509d9SHerbert Xu 				 (x->aead->alg_key_len + 7) / 8);
4591a6509d9SHerbert Xu 	if (err)
4601a6509d9SHerbert Xu 		goto error;
4611a6509d9SHerbert Xu 
4621a6509d9SHerbert Xu 	err = crypto_aead_setauthsize(aead, x->aead->alg_icv_len / 8);
4631a6509d9SHerbert Xu 	if (err)
4641a6509d9SHerbert Xu 		goto error;
4651a6509d9SHerbert Xu 
4661a6509d9SHerbert Xu error:
4671a6509d9SHerbert Xu 	return err;
4681a6509d9SHerbert Xu }
4691a6509d9SHerbert Xu 
4701a6509d9SHerbert Xu static int esp_init_authenc(struct xfrm_state *x)
4711a6509d9SHerbert Xu {
4721a6509d9SHerbert Xu 	struct esp_data *esp = x->data;
47338320c70SHerbert Xu 	struct crypto_aead *aead;
47438320c70SHerbert Xu 	struct crypto_authenc_key_param *param;
47538320c70SHerbert Xu 	struct rtattr *rta;
47638320c70SHerbert Xu 	char *key;
47738320c70SHerbert Xu 	char *p;
47838320c70SHerbert Xu 	char authenc_name[CRYPTO_MAX_ALG_NAME];
47938320c70SHerbert Xu 	unsigned int keylen;
48038320c70SHerbert Xu 	int err;
4811da177e4SLinus Torvalds 
4821a6509d9SHerbert Xu 	err = -EINVAL;
4831da177e4SLinus Torvalds 	if (x->ealg == NULL)
4841a6509d9SHerbert Xu 		goto error;
48538320c70SHerbert Xu 
4861a6509d9SHerbert Xu 	err = -ENAMETOOLONG;
48738320c70SHerbert Xu 	if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME, "authenc(%s,%s)",
48838320c70SHerbert Xu 		     x->aalg ? x->aalg->alg_name : "digest_null",
48938320c70SHerbert Xu 		     x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME)
4901a6509d9SHerbert Xu 		goto error;
49138320c70SHerbert Xu 
49238320c70SHerbert Xu 	aead = crypto_alloc_aead(authenc_name, 0, 0);
49338320c70SHerbert Xu 	err = PTR_ERR(aead);
49438320c70SHerbert Xu 	if (IS_ERR(aead))
49538320c70SHerbert Xu 		goto error;
49638320c70SHerbert Xu 
49738320c70SHerbert Xu 	esp->aead = aead;
49838320c70SHerbert Xu 
49938320c70SHerbert Xu 	keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) +
50038320c70SHerbert Xu 		 (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param));
50138320c70SHerbert Xu 	err = -ENOMEM;
50238320c70SHerbert Xu 	key = kmalloc(keylen, GFP_KERNEL);
50338320c70SHerbert Xu 	if (!key)
50438320c70SHerbert Xu 		goto error;
50538320c70SHerbert Xu 
50638320c70SHerbert Xu 	p = key;
50738320c70SHerbert Xu 	rta = (void *)p;
50838320c70SHerbert Xu 	rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM;
50938320c70SHerbert Xu 	rta->rta_len = RTA_LENGTH(sizeof(*param));
51038320c70SHerbert Xu 	param = RTA_DATA(rta);
51138320c70SHerbert Xu 	p += RTA_SPACE(sizeof(*param));
51238320c70SHerbert Xu 
5131da177e4SLinus Torvalds 	if (x->aalg) {
5141da177e4SLinus Torvalds 		struct xfrm_algo_desc *aalg_desc;
5151da177e4SLinus Torvalds 
51638320c70SHerbert Xu 		memcpy(p, x->aalg->alg_key, (x->aalg->alg_key_len + 7) / 8);
51738320c70SHerbert Xu 		p += (x->aalg->alg_key_len + 7) / 8;
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds 		aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
5201da177e4SLinus Torvalds 		BUG_ON(!aalg_desc);
5211da177e4SLinus Torvalds 
52238320c70SHerbert Xu 		err = -EINVAL;
5231da177e4SLinus Torvalds 		if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
52438320c70SHerbert Xu 		    crypto_aead_authsize(aead)) {
52564ce2073SPatrick McHardy 			NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
5261da177e4SLinus Torvalds 				 x->aalg->alg_name,
52738320c70SHerbert Xu 				 crypto_aead_authsize(aead),
52864ce2073SPatrick McHardy 				 aalg_desc->uinfo.auth.icv_fullbits/8);
52938320c70SHerbert Xu 			goto free_key;
5301da177e4SLinus Torvalds 		}
5311da177e4SLinus Torvalds 
53238320c70SHerbert Xu 		err = crypto_aead_setauthsize(
53338320c70SHerbert Xu 			aead, aalg_desc->uinfo.auth.icv_truncbits / 8);
53438320c70SHerbert Xu 		if (err)
53538320c70SHerbert Xu 			goto free_key;
5361da177e4SLinus Torvalds 	}
5374b7137ffSHerbert Xu 
53838320c70SHerbert Xu 	param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8);
53938320c70SHerbert Xu 	memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8);
54038320c70SHerbert Xu 
54138320c70SHerbert Xu 	err = crypto_aead_setkey(aead, key, keylen);
54238320c70SHerbert Xu 
54338320c70SHerbert Xu free_key:
54438320c70SHerbert Xu 	kfree(key);
54538320c70SHerbert Xu 
5461a6509d9SHerbert Xu error:
5471a6509d9SHerbert Xu 	return err;
5481a6509d9SHerbert Xu }
5491a6509d9SHerbert Xu 
5501a6509d9SHerbert Xu static int esp_init_state(struct xfrm_state *x)
5511a6509d9SHerbert Xu {
5521a6509d9SHerbert Xu 	struct esp_data *esp;
5531a6509d9SHerbert Xu 	struct crypto_aead *aead;
5541a6509d9SHerbert Xu 	u32 align;
5551a6509d9SHerbert Xu 	int err;
5561a6509d9SHerbert Xu 
5571a6509d9SHerbert Xu 	esp = kzalloc(sizeof(*esp), GFP_KERNEL);
5581a6509d9SHerbert Xu 	if (esp == NULL)
5591a6509d9SHerbert Xu 		return -ENOMEM;
5601a6509d9SHerbert Xu 
5611a6509d9SHerbert Xu 	x->data = esp;
5621a6509d9SHerbert Xu 
5631a6509d9SHerbert Xu 	if (x->aead)
5641a6509d9SHerbert Xu 		err = esp_init_aead(x);
5651a6509d9SHerbert Xu 	else
5661a6509d9SHerbert Xu 		err = esp_init_authenc(x);
5671a6509d9SHerbert Xu 
56838320c70SHerbert Xu 	if (err)
5691da177e4SLinus Torvalds 		goto error;
57038320c70SHerbert Xu 
5711a6509d9SHerbert Xu 	aead = esp->aead;
5721a6509d9SHerbert Xu 
5731a6509d9SHerbert Xu 	esp->padlen = 0;
5741a6509d9SHerbert Xu 
57538320c70SHerbert Xu 	x->props.header_len = sizeof(struct ip_esp_hdr) +
57638320c70SHerbert Xu 			      crypto_aead_ivsize(aead);
5777e49e6deSMasahide NAKAMURA 	if (x->props.mode == XFRM_MODE_TUNNEL)
5781da177e4SLinus Torvalds 		x->props.header_len += sizeof(struct iphdr);
579eb49e630SJoakim Koskela 	else if (x->props.mode == XFRM_MODE_BEET && x->sel.family != AF_INET6)
580ac758e3cSPatrick McHardy 		x->props.header_len += IPV4_BEET_PHMAXLEN;
5811da177e4SLinus Torvalds 	if (x->encap) {
5821da177e4SLinus Torvalds 		struct xfrm_encap_tmpl *encap = x->encap;
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 		switch (encap->encap_type) {
5851da177e4SLinus Torvalds 		default:
5861da177e4SLinus Torvalds 			goto error;
5871da177e4SLinus Torvalds 		case UDP_ENCAP_ESPINUDP:
5881da177e4SLinus Torvalds 			x->props.header_len += sizeof(struct udphdr);
5891da177e4SLinus Torvalds 			break;
5901da177e4SLinus Torvalds 		case UDP_ENCAP_ESPINUDP_NON_IKE:
5911da177e4SLinus Torvalds 			x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);
5921da177e4SLinus Torvalds 			break;
5931da177e4SLinus Torvalds 		}
5941da177e4SLinus Torvalds 	}
59538320c70SHerbert Xu 
59638320c70SHerbert Xu 	align = ALIGN(crypto_aead_blocksize(aead), 4);
59738320c70SHerbert Xu 	if (esp->padlen)
59838320c70SHerbert Xu 		align = max_t(u32, align, esp->padlen);
59938320c70SHerbert Xu 	x->props.trailer_len = align + 1 + crypto_aead_authsize(esp->aead);
6001da177e4SLinus Torvalds 
6011da177e4SLinus Torvalds error:
60238320c70SHerbert Xu 	return err;
6031da177e4SLinus Torvalds }
6041da177e4SLinus Torvalds 
605533cb5b0SEric Dumazet static const struct xfrm_type esp_type =
6061da177e4SLinus Torvalds {
6071da177e4SLinus Torvalds 	.description	= "ESP4",
6081da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
6091da177e4SLinus Torvalds 	.proto	     	= IPPROTO_ESP,
610436a0a40SHerbert Xu 	.flags		= XFRM_TYPE_REPLAY_PROT,
6111da177e4SLinus Torvalds 	.init_state	= esp_init_state,
6121da177e4SLinus Torvalds 	.destructor	= esp_destroy,
613c5c25238SPatrick McHardy 	.get_mtu	= esp4_get_mtu,
6141da177e4SLinus Torvalds 	.input		= esp_input,
6151da177e4SLinus Torvalds 	.output		= esp_output
6161da177e4SLinus Torvalds };
6171da177e4SLinus Torvalds 
618*32613090SAlexey Dobriyan static const struct net_protocol esp4_protocol = {
6191da177e4SLinus Torvalds 	.handler	=	xfrm4_rcv,
6201da177e4SLinus Torvalds 	.err_handler	=	esp4_err,
6211da177e4SLinus Torvalds 	.no_policy	=	1,
6224fb236baSAlexey Dobriyan 	.netns_ok	=	1,
6231da177e4SLinus Torvalds };
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds static int __init esp4_init(void)
6261da177e4SLinus Torvalds {
6271da177e4SLinus Torvalds 	if (xfrm_register_type(&esp_type, AF_INET) < 0) {
6281da177e4SLinus Torvalds 		printk(KERN_INFO "ip esp init: can't add xfrm type\n");
6291da177e4SLinus Torvalds 		return -EAGAIN;
6301da177e4SLinus Torvalds 	}
6311da177e4SLinus Torvalds 	if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
6321da177e4SLinus Torvalds 		printk(KERN_INFO "ip esp init: can't add protocol\n");
6331da177e4SLinus Torvalds 		xfrm_unregister_type(&esp_type, AF_INET);
6341da177e4SLinus Torvalds 		return -EAGAIN;
6351da177e4SLinus Torvalds 	}
6361da177e4SLinus Torvalds 	return 0;
6371da177e4SLinus Torvalds }
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds static void __exit esp4_fini(void)
6401da177e4SLinus Torvalds {
6411da177e4SLinus Torvalds 	if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
6421da177e4SLinus Torvalds 		printk(KERN_INFO "ip esp close: can't remove protocol\n");
6431da177e4SLinus Torvalds 	if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
6441da177e4SLinus Torvalds 		printk(KERN_INFO "ip esp close: can't remove xfrm type\n");
6451da177e4SLinus Torvalds }
6461da177e4SLinus Torvalds 
6471da177e4SLinus Torvalds module_init(esp4_init);
6481da177e4SLinus Torvalds module_exit(esp4_fini);
6491da177e4SLinus Torvalds MODULE_LICENSE("GPL");
650d3d6dd3aSMasahide NAKAMURA MODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_ESP);
651