Blame view

net/ipv4/esp4.c 16.7 KB
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
1
2
  #include <crypto/aead.h>
  #include <crypto/authenc.h>
6b7326c84   Herbert Xu   [IPSEC] ESP: Use ...
3
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
  #include <linux/module.h>
  #include <net/ip.h>
  #include <net/xfrm.h>
  #include <net/esp.h>
72998d8c8   Adrian Bunk   [INET] ESP: Must ...
8
  #include <linux/scatterlist.h>
a02a64223   Herbert Xu   [IPSEC]: Use ALIG...
9
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
  #include <linux/pfkeyv2.h>
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
11
12
  #include <linux/rtnetlink.h>
  #include <linux/slab.h>
b7c6538cd   Herbert Xu   [IPSEC]: Move sta...
13
  #include <linux/spinlock.h>
2017a72c0   Thomas Graf   [IPv4] ESP: Disca...
14
  #include <linux/in6.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <net/icmp.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
16
  #include <net/protocol.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
  #include <net/udp.h>
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
18
19
20
21
22
23
  struct esp_skb_cb {
  	struct xfrm_skb_cb xfrm;
  	void *tmp;
  };
  
  #define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0]))
d979e20f2   Martin Willi   xfrm: Traffic Flo...
24
  static u32 esp4_get_mtu(struct xfrm_state *x, int mtu);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
25
26
27
28
29
30
31
32
  /*
   * Allocate an AEAD request structure with extra space for SG and IV.
   *
   * For alignment considerations the IV is placed at the front, followed
   * by the request and finally the SG list.
   *
   * TODO: Use spare space in skb for this where possible.
   */
0dc49e9b2   Steffen Klassert   esp4: Add support...
33
  static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqhilen)
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
34
35
  {
  	unsigned int len;
0dc49e9b2   Steffen Klassert   esp4: Add support...
36
37
38
  	len = seqhilen;
  
  	len += crypto_aead_ivsize(aead);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
39
40
41
42
43
44
45
46
47
48
49
50
51
  	if (len) {
  		len += crypto_aead_alignmask(aead) &
  		       ~(crypto_tfm_ctx_alignment() - 1);
  		len = ALIGN(len, crypto_tfm_ctx_alignment());
  	}
  
  	len += sizeof(struct aead_givcrypt_request) + crypto_aead_reqsize(aead);
  	len = ALIGN(len, __alignof__(struct scatterlist));
  
  	len += sizeof(struct scatterlist) * nfrags;
  
  	return kmalloc(len, GFP_ATOMIC);
  }
0dc49e9b2   Steffen Klassert   esp4: Add support...
52
53
54
55
56
  static inline __be32 *esp_tmp_seqhi(void *tmp)
  {
  	return PTR_ALIGN((__be32 *)tmp, __alignof__(__be32));
  }
  static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int seqhilen)
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
57
58
  {
  	return crypto_aead_ivsize(aead) ?
0dc49e9b2   Steffen Klassert   esp4: Add support...
59
60
  	       PTR_ALIGN((u8 *)tmp + seqhilen,
  			 crypto_aead_alignmask(aead) + 1) : tmp + seqhilen;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  }
  
  static inline struct aead_givcrypt_request *esp_tmp_givreq(
  	struct crypto_aead *aead, u8 *iv)
  {
  	struct aead_givcrypt_request *req;
  
  	req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),
  				crypto_tfm_ctx_alignment());
  	aead_givcrypt_set_tfm(req, aead);
  	return req;
  }
  
  static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv)
  {
  	struct aead_request *req;
  
  	req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),
  				crypto_tfm_ctx_alignment());
  	aead_request_set_tfm(req, aead);
  	return req;
  }
  
  static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,
  					     struct aead_request *req)
  {
  	return (void *)ALIGN((unsigned long)(req + 1) +
  			     crypto_aead_reqsize(aead),
  			     __alignof__(struct scatterlist));
  }
  
  static inline struct scatterlist *esp_givreq_sg(
  	struct crypto_aead *aead, struct aead_givcrypt_request *req)
  {
  	return (void *)ALIGN((unsigned long)(req + 1) +
  			     crypto_aead_reqsize(aead),
  			     __alignof__(struct scatterlist));
  }
  
  static void esp_output_done(struct crypto_async_request *base, int err)
  {
  	struct sk_buff *skb = base->data;
  
  	kfree(ESP_SKB_CB(skb)->tmp);
  	xfrm_output_resume(skb, err);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
  static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
  {
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
  	struct ip_esp_hdr *esph;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
111
112
113
114
  	struct crypto_aead *aead;
  	struct aead_givcrypt_request *req;
  	struct scatterlist *sg;
  	struct scatterlist *asg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
  	struct esp_data *esp;
  	struct sk_buff *trailer;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
117
118
  	void *tmp;
  	u8 *iv;
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
119
  	u8 *tail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
  	int blksize;
  	int clen;
  	int alen;
d979e20f2   Martin Willi   xfrm: Traffic Flo...
123
124
  	int plen;
  	int tfclen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
  	int nfrags;
0dc49e9b2   Steffen Klassert   esp4: Add support...
126
127
128
129
  	int assoclen;
  	int sglists;
  	int seqhilen;
  	__be32 *seqhi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130

7b277b1a5   Herbert Xu   [IPSEC]: Set skb-...
131
  	/* skb is pure payload to encrypt */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
  
  	err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
  	esp = x->data;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
135
136
  	aead = esp->aead;
  	alen = crypto_aead_authsize(aead);
d979e20f2   Martin Willi   xfrm: Traffic Flo...
137
138
139
140
141
142
143
144
145
  	tfclen = 0;
  	if (x->tfcpad) {
  		struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb);
  		u32 padto;
  
  		padto = min(x->tfcpad, esp4_get_mtu(x, dst->child_mtu_cached));
  		if (skb->len < padto)
  			tfclen = padto - skb->len;
  	}
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
146
  	blksize = ALIGN(crypto_aead_blocksize(aead), 4);
d979e20f2   Martin Willi   xfrm: Traffic Flo...
147
  	clen = ALIGN(skb->len + 2 + tfclen, blksize);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
148
149
  	if (esp->padlen)
  		clen = ALIGN(clen, esp->padlen);
d979e20f2   Martin Willi   xfrm: Traffic Flo...
150
  	plen = clen - skb->len - tfclen;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
151

d979e20f2   Martin Willi   xfrm: Traffic Flo...
152
153
  	err = skb_cow_data(skb, tfclen + plen + alen, &trailer);
  	if (err < 0)
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
154
155
  		goto error;
  	nfrags = err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156

0dc49e9b2   Steffen Klassert   esp4: Add support...
157
158
159
160
161
162
163
164
165
166
167
  	assoclen = sizeof(*esph);
  	sglists = 1;
  	seqhilen = 0;
  
  	if (x->props.flags & XFRM_STATE_ESN) {
  		sglists += 2;
  		seqhilen += sizeof(__be32);
  		assoclen += seqhilen;
  	}
  
  	tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
168
  	if (!tmp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
  		goto error;
0dc49e9b2   Steffen Klassert   esp4: Add support...
170
171
  	seqhi = esp_tmp_seqhi(tmp);
  	iv = esp_tmp_iv(aead, tmp, seqhilen);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
172
173
  	req = esp_tmp_givreq(aead, iv);
  	asg = esp_givreq_sg(aead, req);
0dc49e9b2   Steffen Klassert   esp4: Add support...
174
  	sg = asg + sglists;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
175

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  	/* Fill padding... */
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
177
  	tail = skb_tail_pointer(trailer);
d979e20f2   Martin Willi   xfrm: Traffic Flo...
178
179
180
181
  	if (tfclen) {
  		memset(tail, 0, tfclen);
  		tail += tfclen;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
  	do {
  		int i;
d979e20f2   Martin Willi   xfrm: Traffic Flo...
184
  		for (i = 0; i < plen - 2; i++)
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
185
  			tail[i] = i + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  	} while (0);
d979e20f2   Martin Willi   xfrm: Traffic Flo...
187
188
  	tail[plen - 2] = plen - 2;
  	tail[plen - 1] = *skb_mac_header(skb);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
189
  	pskb_put(skb, trailer, clen - skb->len + alen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190

7b277b1a5   Herbert Xu   [IPSEC]: Set skb-...
191
  	skb_push(skb, -skb_network_offset(skb));
87bdc48d3   Herbert Xu   [IPSEC]: Get rid ...
192
  	esph = ip_esp_hdr(skb);
37fedd3aa   Herbert Xu   [IPSEC]: Use IPv6...
193
  	*skb_mac_header(skb) = IPPROTO_ESP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
198
  
  	/* this is non-NULL only with UDP Encapsulation */
  	if (x->encap) {
  		struct xfrm_encap_tmpl *encap = x->encap;
  		struct udphdr *uh;
d5a0a1e31   Al Viro   [IPV4]: encapsula...
199
  		__be32 *udpdata32;
5e226e4d9   Al Viro   [IPV4]: esp_outpu...
200
  		__be16 sport, dport;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
201
202
203
204
205
206
207
  		int encap_type;
  
  		spin_lock_bh(&x->lock);
  		sport = encap->encap_sport;
  		dport = encap->encap_dport;
  		encap_type = encap->encap_type;
  		spin_unlock_bh(&x->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
  
  		uh = (struct udphdr *)esph;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
210
211
212
  		uh->source = sport;
  		uh->dest = dport;
  		uh->len = htons(skb->len - skb_transport_offset(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
  		uh->check = 0;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
214
  		switch (encap_type) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
216
217
218
219
  		default:
  		case UDP_ENCAP_ESPINUDP:
  			esph = (struct ip_esp_hdr *)(uh + 1);
  			break;
  		case UDP_ENCAP_ESPINUDP_NON_IKE:
d5a0a1e31   Al Viro   [IPV4]: encapsula...
220
  			udpdata32 = (__be32 *)(uh + 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
222
223
224
  			udpdata32[0] = udpdata32[1] = 0;
  			esph = (struct ip_esp_hdr *)(udpdata32 + 2);
  			break;
  		}
37fedd3aa   Herbert Xu   [IPSEC]: Use IPv6...
225
226
  		*skb_mac_header(skb) = IPPROTO_UDP;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
  
  	esph->spi = x->id.spi;
1ce3644ad   Steffen Klassert   xfrm: Use separat...
229
  	esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230

38320c70d   Herbert Xu   [IPSEC]: Use cryp...
231
232
233
234
  	sg_init_table(sg, nfrags);
  	skb_to_sgvec(skb, sg,
  		     esph->enc_data + crypto_aead_ivsize(aead) - skb->data,
  		     clen + alen);
0dc49e9b2   Steffen Klassert   esp4: Add support...
235
236
237
238
239
240
241
242
243
  
  	if ((x->props.flags & XFRM_STATE_ESN)) {
  		sg_init_table(asg, 3);
  		sg_set_buf(asg, &esph->spi, sizeof(__be32));
  		*seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
  		sg_set_buf(asg + 1, seqhi, seqhilen);
  		sg_set_buf(asg + 2, &esph->seq_no, sizeof(__be32));
  	} else
  		sg_init_one(asg, esph, sizeof(*esph));
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
244
245
246
  
  	aead_givcrypt_set_callback(req, 0, esp_output_done, skb);
  	aead_givcrypt_set_crypt(req, sg, sg, clen, iv);
0dc49e9b2   Steffen Klassert   esp4: Add support...
247
  	aead_givcrypt_set_assoc(req, asg, assoclen);
b318e0e4e   Herbert Xu   [IPSEC]: Fix bogu...
248
  	aead_givcrypt_set_giv(req, esph->enc_data,
1ce3644ad   Steffen Klassert   xfrm: Use separat...
249
  			      XFRM_SKB_CB(skb)->seq.output.low);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
250
251
252
253
254
  
  	ESP_SKB_CB(skb)->tmp = tmp;
  	err = crypto_aead_givencrypt(req);
  	if (err == -EINPROGRESS)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255

38320c70d   Herbert Xu   [IPSEC]: Use cryp...
256
257
  	if (err == -EBUSY)
  		err = NET_XMIT_DROP;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258

38320c70d   Herbert Xu   [IPSEC]: Use cryp...
259
  	kfree(tmp);
b7c6538cd   Herbert Xu   [IPSEC]: Move sta...
260

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
  error:
  	return err;
  }
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
264
  static int esp_input_done2(struct sk_buff *skb, int err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  {
b71d1d426   Eric Dumazet   inet: constify ip...
266
  	const struct iphdr *iph;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
267
  	struct xfrm_state *x = xfrm_input_state(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
  	struct esp_data *esp = x->data;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
269
270
271
272
  	struct crypto_aead *aead = esp->aead;
  	int alen = crypto_aead_authsize(aead);
  	int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead);
  	int elen = skb->len - hlen;
31a4ab930   Herbert Xu   [IPSEC] proto: Mo...
273
  	int ihl;
4bf05ecee   Herbert Xu   [IPSEC] esp: Kill...
274
  	u8 nexthdr[2];
4bf05ecee   Herbert Xu   [IPSEC] esp: Kill...
275
  	int padlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276

38320c70d   Herbert Xu   [IPSEC]: Use cryp...
277
  	kfree(ESP_SKB_CB(skb)->tmp);
0ebea8ef3   Herbert Xu   [IPSEC]: Move sta...
278

6b7326c84   Herbert Xu   [IPSEC] ESP: Use ...
279
  	if (unlikely(err))
668dc8af3   Herbert Xu   [IPSEC]: Move int...
280
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281

4bf05ecee   Herbert Xu   [IPSEC] esp: Kill...
282
283
  	if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
  		BUG();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284

668dc8af3   Herbert Xu   [IPSEC]: Move int...
285
  	err = -EINVAL;
4bf05ecee   Herbert Xu   [IPSEC] esp: Kill...
286
  	padlen = nexthdr[0];
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
287
  	if (padlen + 2 + alen >= elen)
4bf05ecee   Herbert Xu   [IPSEC] esp: Kill...
288
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289

e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
290
  	/* ... check padding bits here. Silly. :-) */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291

eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
292
  	iph = ip_hdr(skb);
31a4ab930   Herbert Xu   [IPSEC] proto: Mo...
293
  	ihl = iph->ihl * 4;
752c1f4c7   Herbert Xu   [IPSEC]: Kill pos...
294
295
  	if (x->encap) {
  		struct xfrm_encap_tmpl *encap = x->encap;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
296
  		struct udphdr *uh = (void *)(skb_network_header(skb) + ihl);
752c1f4c7   Herbert Xu   [IPSEC]: Kill pos...
297
298
299
300
301
302
303
304
305
306
307
308
309
  
  		/*
  		 * 1) if the NAT-T peer's IP or port changed then
  		 *    advertize the change to the keying daemon.
  		 *    This is an inbound SA, so just compare
  		 *    SRC ports.
  		 */
  		if (iph->saddr != x->props.saddr.a4 ||
  		    uh->source != encap->encap_sport) {
  			xfrm_address_t ipaddr;
  
  			ipaddr.a4 = iph->saddr;
  			km_new_mapping(x, &ipaddr, uh->source);
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
310

752c1f4c7   Herbert Xu   [IPSEC]: Kill pos...
311
312
313
314
315
316
317
  			/* XXX: perhaps add an extra
  			 * policy check here, to see
  			 * if we should allow or
  			 * reject a packet from a
  			 * different source
  			 * address/port.
  			 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  		}
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
319

752c1f4c7   Herbert Xu   [IPSEC]: Kill pos...
320
321
322
323
324
325
326
  		/*
  		 * 2) ignore UDP/TCP checksums in case
  		 *    of NAT-T in Transport Mode, or
  		 *    perform other post-processing fixes
  		 *    as per draft-ietf-ipsec-udp-encaps-06,
  		 *    section 3.1.2
  		 */
8bd170750   Herbert Xu   [IPSEC] esp: Remo...
327
  		if (x->props.mode == XFRM_MODE_TRANSPORT)
752c1f4c7   Herbert Xu   [IPSEC]: Kill pos...
328
  			skb->ip_summed = CHECKSUM_UNNECESSARY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329
  	}
4bf05ecee   Herbert Xu   [IPSEC] esp: Kill...
330
  	pskb_trim(skb, skb->len - alen - padlen - 2);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
331
  	__skb_pull(skb, hlen);
967b05f64   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
332
  	skb_set_transport_header(skb, -ihl);
4bf05ecee   Herbert Xu   [IPSEC] esp: Kill...
333

38320c70d   Herbert Xu   [IPSEC]: Use cryp...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  	err = nexthdr[1];
  
  	/* RFC4303: Drop dummy packets without any error */
  	if (err == IPPROTO_NONE)
  		err = -EINVAL;
  
  out:
  	return err;
  }
  
  static void esp_input_done(struct crypto_async_request *base, int err)
  {
  	struct sk_buff *skb = base->data;
  
  	xfrm_input_resume(skb, esp_input_done2(skb, err));
  }
  
  /*
   * Note: detecting truncated vs. non-truncated authentication data is very
   * expensive, so we only support truncated data, which is the recommended
   * and common case.
   */
  static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
  {
  	struct ip_esp_hdr *esph;
  	struct esp_data *esp = x->data;
  	struct crypto_aead *aead = esp->aead;
  	struct aead_request *req;
  	struct sk_buff *trailer;
  	int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead);
  	int nfrags;
0dc49e9b2   Steffen Klassert   esp4: Add support...
365
366
367
368
  	int assoclen;
  	int sglists;
  	int seqhilen;
  	__be32 *seqhi;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
369
370
371
372
373
  	void *tmp;
  	u8 *iv;
  	struct scatterlist *sg;
  	struct scatterlist *asg;
  	int err = -EINVAL;
920fc941a   Thomas Graf   [ESP]: Ensure IV ...
374
  	if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)))
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
375
376
377
378
379
380
381
382
  		goto out;
  
  	if (elen <= 0)
  		goto out;
  
  	if ((err = skb_cow_data(skb, 0, &trailer)) < 0)
  		goto out;
  	nfrags = err;
0dc49e9b2   Steffen Klassert   esp4: Add support...
383
384
385
386
387
388
389
390
391
  	assoclen = sizeof(*esph);
  	sglists = 1;
  	seqhilen = 0;
  
  	if (x->props.flags & XFRM_STATE_ESN) {
  		sglists += 2;
  		seqhilen += sizeof(__be32);
  		assoclen += seqhilen;
  	}
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
392
  	err = -ENOMEM;
0dc49e9b2   Steffen Klassert   esp4: Add support...
393
  	tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
394
395
396
397
  	if (!tmp)
  		goto out;
  
  	ESP_SKB_CB(skb)->tmp = tmp;
0dc49e9b2   Steffen Klassert   esp4: Add support...
398
399
  	seqhi = esp_tmp_seqhi(tmp);
  	iv = esp_tmp_iv(aead, tmp, seqhilen);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
400
401
  	req = esp_tmp_req(aead, iv);
  	asg = esp_req_sg(aead, req);
0dc49e9b2   Steffen Klassert   esp4: Add support...
402
  	sg = asg + sglists;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
403
404
405
406
407
408
409
410
411
412
  
  	skb->ip_summed = CHECKSUM_NONE;
  
  	esph = (struct ip_esp_hdr *)skb->data;
  
  	/* Get ivec. This can be wrong, check against another impls. */
  	iv = esph->enc_data;
  
  	sg_init_table(sg, nfrags);
  	skb_to_sgvec(skb, sg, sizeof(*esph) + crypto_aead_ivsize(aead), elen);
0dc49e9b2   Steffen Klassert   esp4: Add support...
413
414
415
416
417
418
419
420
421
  
  	if ((x->props.flags & XFRM_STATE_ESN)) {
  		sg_init_table(asg, 3);
  		sg_set_buf(asg, &esph->spi, sizeof(__be32));
  		*seqhi = XFRM_SKB_CB(skb)->seq.input.hi;
  		sg_set_buf(asg + 1, seqhi, seqhilen);
  		sg_set_buf(asg + 2, &esph->seq_no, sizeof(__be32));
  	} else
  		sg_init_one(asg, esph, sizeof(*esph));
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
422
423
424
  
  	aead_request_set_callback(req, 0, esp_input_done, skb);
  	aead_request_set_crypt(req, sg, sg, elen, iv);
0dc49e9b2   Steffen Klassert   esp4: Add support...
425
  	aead_request_set_assoc(req, asg, assoclen);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
426
427
428
429
430
431
  
  	err = crypto_aead_decrypt(req);
  	if (err == -EINPROGRESS)
  		goto out;
  
  	err = esp_input_done2(skb, err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
  
  out:
668dc8af3   Herbert Xu   [IPSEC]: Move int...
434
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
  }
c5c252389   Patrick McHardy   [XFRM]: Optimize ...
436
  static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
  {
  	struct esp_data *esp = x->data;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
439
440
  	u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4);
  	u32 align = max_t(u32, blksize, esp->padlen);
c5c252389   Patrick McHardy   [XFRM]: Optimize ...
441
  	u32 rem;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
442
  	mtu -= x->props.header_len + crypto_aead_authsize(esp->aead);
c5c252389   Patrick McHardy   [XFRM]: Optimize ...
443
444
  	rem = mtu & (align - 1);
  	mtu &= ~(align - 1);
0a69452cb   Diego Beltrami   [XFRM]: BEET mode
445
446
447
  
  	switch (x->props.mode) {
  	case XFRM_MODE_TUNNEL:
0a69452cb   Diego Beltrami   [XFRM]: BEET mode
448
449
450
451
  		break;
  	default:
  	case XFRM_MODE_TRANSPORT:
  		/* The worst case */
c5c252389   Patrick McHardy   [XFRM]: Optimize ...
452
453
  		mtu -= blksize - 4;
  		mtu += min_t(u32, blksize - 4, rem);
0a69452cb   Diego Beltrami   [XFRM]: BEET mode
454
455
  		break;
  	case XFRM_MODE_BEET:
e905a9eda   YOSHIFUJI Hideaki   [NET] IPV4: Fix w...
456
  		/* The worst case. */
c5c252389   Patrick McHardy   [XFRM]: Optimize ...
457
  		mtu += min_t(u32, IPV4_BEET_PHMAXLEN, rem);
0a69452cb   Diego Beltrami   [XFRM]: BEET mode
458
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
  	}
0a69452cb   Diego Beltrami   [XFRM]: BEET mode
460

c5c252389   Patrick McHardy   [XFRM]: Optimize ...
461
  	return mtu - 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
  }
  
  static void esp4_err(struct sk_buff *skb, u32 info)
  {
4fb236bac   Alexey Dobriyan   netns xfrm: AH/ES...
466
  	struct net *net = dev_net(skb->dev);
b71d1d426   Eric Dumazet   inet: constify ip...
467
  	const struct iphdr *iph = (const struct iphdr *)skb->data;
d9319100c   Jianjun Kong   net: clean up net...
468
  	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
  	struct xfrm_state *x;
88c7664f1   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
470
471
  	if (icmp_hdr(skb)->type != ICMP_DEST_UNREACH ||
  	    icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
  		return;
b71d1d426   Eric Dumazet   inet: constify ip...
473
474
  	x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
  			      esph->spi, IPPROTO_ESP, AF_INET);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
  	if (!x)
  		return;
64ce20730   Patrick McHardy   [NET]: Make NETDE...
477
478
479
  	NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x
  ",
  		 ntohl(esph->spi), ntohl(iph->daddr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
483
484
485
486
487
488
  	xfrm_state_put(x);
  }
  
  static void esp_destroy(struct xfrm_state *x)
  {
  	struct esp_data *esp = x->data;
  
  	if (!esp)
  		return;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
489
  	crypto_free_aead(esp->aead);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
  	kfree(esp);
  }
1a6509d99   Herbert Xu   [IPSEC]: Add supp...
492
  static int esp_init_aead(struct xfrm_state *x)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
  {
1a6509d99   Herbert Xu   [IPSEC]: Add supp...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
  	struct esp_data *esp = x->data;
  	struct crypto_aead *aead;
  	int err;
  
  	aead = crypto_alloc_aead(x->aead->alg_name, 0, 0);
  	err = PTR_ERR(aead);
  	if (IS_ERR(aead))
  		goto error;
  
  	esp->aead = aead;
  
  	err = crypto_aead_setkey(aead, x->aead->alg_key,
  				 (x->aead->alg_key_len + 7) / 8);
  	if (err)
  		goto error;
  
  	err = crypto_aead_setauthsize(aead, x->aead->alg_icv_len / 8);
  	if (err)
  		goto error;
  
  error:
  	return err;
  }
  
  static int esp_init_authenc(struct xfrm_state *x)
  {
  	struct esp_data *esp = x->data;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
521
522
523
524
525
526
  	struct crypto_aead *aead;
  	struct crypto_authenc_key_param *param;
  	struct rtattr *rta;
  	char *key;
  	char *p;
  	char authenc_name[CRYPTO_MAX_ALG_NAME];
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
527
528
  	unsigned int keylen;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529

1a6509d99   Herbert Xu   [IPSEC]: Add supp...
530
  	err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
  	if (x->ealg == NULL)
1a6509d99   Herbert Xu   [IPSEC]: Add supp...
532
  		goto error;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
533

1a6509d99   Herbert Xu   [IPSEC]: Add supp...
534
  	err = -ENAMETOOLONG;
0dc49e9b2   Steffen Klassert   esp4: Add support...
535
536
537
538
539
540
541
542
543
544
545
546
547
548
  
  	if ((x->props.flags & XFRM_STATE_ESN)) {
  		if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME,
  			     "authencesn(%s,%s)",
  			     x->aalg ? x->aalg->alg_name : "digest_null",
  			     x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME)
  			goto error;
  	} else {
  		if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME,
  			     "authenc(%s,%s)",
  			     x->aalg ? x->aalg->alg_name : "digest_null",
  			     x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME)
  			goto error;
  	}
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
  
  	aead = crypto_alloc_aead(authenc_name, 0, 0);
  	err = PTR_ERR(aead);
  	if (IS_ERR(aead))
  		goto error;
  
  	esp->aead = aead;
  
  	keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) +
  		 (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param));
  	err = -ENOMEM;
  	key = kmalloc(keylen, GFP_KERNEL);
  	if (!key)
  		goto error;
  
  	p = key;
  	rta = (void *)p;
  	rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM;
  	rta->rta_len = RTA_LENGTH(sizeof(*param));
  	param = RTA_DATA(rta);
  	p += RTA_SPACE(sizeof(*param));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
  	if (x->aalg) {
  		struct xfrm_algo_desc *aalg_desc;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
572
573
  		memcpy(p, x->aalg->alg_key, (x->aalg->alg_key_len + 7) / 8);
  		p += (x->aalg->alg_key_len + 7) / 8;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574
575
576
  
  		aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
  		BUG_ON(!aalg_desc);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
577
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
  		if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
579
  		    crypto_aead_authsize(aead)) {
64ce20730   Patrick McHardy   [NET]: Make NETDE...
580
581
582
  			NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu
  ",
  				 x->aalg->alg_name,
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
583
  				 crypto_aead_authsize(aead),
64ce20730   Patrick McHardy   [NET]: Make NETDE...
584
  				 aalg_desc->uinfo.auth.icv_fullbits/8);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
585
  			goto free_key;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
  		}
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
587
  		err = crypto_aead_setauthsize(
8f8a088c2   Martin Willi   xfrm: Use the use...
588
  			aead, x->aalg->alg_trunc_len / 8);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
589
590
  		if (err)
  			goto free_key;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591
  	}
4b7137ff8   Herbert Xu   [IPSEC] esp: Remo...
592

38320c70d   Herbert Xu   [IPSEC]: Use cryp...
593
594
595
596
597
598
599
  	param->enckeylen = cpu_to_be32((x->ealg->alg_key_len + 7) / 8);
  	memcpy(p, x->ealg->alg_key, (x->ealg->alg_key_len + 7) / 8);
  
  	err = crypto_aead_setkey(aead, key, keylen);
  
  free_key:
  	kfree(key);
1a6509d99   Herbert Xu   [IPSEC]: Add supp...
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
  error:
  	return err;
  }
  
  static int esp_init_state(struct xfrm_state *x)
  {
  	struct esp_data *esp;
  	struct crypto_aead *aead;
  	u32 align;
  	int err;
  
  	esp = kzalloc(sizeof(*esp), GFP_KERNEL);
  	if (esp == NULL)
  		return -ENOMEM;
  
  	x->data = esp;
  
  	if (x->aead)
  		err = esp_init_aead(x);
  	else
  		err = esp_init_authenc(x);
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
621
  	if (err)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
  		goto error;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
623

1a6509d99   Herbert Xu   [IPSEC]: Add supp...
624
625
626
  	aead = esp->aead;
  
  	esp->padlen = 0;
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
627
628
  	x->props.header_len = sizeof(struct ip_esp_hdr) +
  			      crypto_aead_ivsize(aead);
7e49e6de3   Masahide NAKAMURA   [XFRM]: Add XFRM_...
629
  	if (x->props.mode == XFRM_MODE_TUNNEL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630
  		x->props.header_len += sizeof(struct iphdr);
eb49e6309   Joakim Koskela   ipsec: Interfamil...
631
  	else if (x->props.mode == XFRM_MODE_BEET && x->sel.family != AF_INET6)
ac758e3c5   Patrick McHardy   [XFRM]: beet: fix...
632
  		x->props.header_len += IPV4_BEET_PHMAXLEN;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
634
635
636
637
638
639
640
641
642
643
644
645
646
  	if (x->encap) {
  		struct xfrm_encap_tmpl *encap = x->encap;
  
  		switch (encap->encap_type) {
  		default:
  			goto error;
  		case UDP_ENCAP_ESPINUDP:
  			x->props.header_len += sizeof(struct udphdr);
  			break;
  		case UDP_ENCAP_ESPINUDP_NON_IKE:
  			x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);
  			break;
  		}
  	}
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
647
648
649
650
651
  
  	align = ALIGN(crypto_aead_blocksize(aead), 4);
  	if (esp->padlen)
  		align = max_t(u32, align, esp->padlen);
  	x->props.trailer_len = align + 1 + crypto_aead_authsize(esp->aead);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
652
653
  
  error:
38320c70d   Herbert Xu   [IPSEC]: Use cryp...
654
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
  }
533cb5b0a   Eric Dumazet   [XFRM]: constify ...
656
  static const struct xfrm_type esp_type =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
658
659
660
  {
  	.description	= "ESP4",
  	.owner		= THIS_MODULE,
  	.proto	     	= IPPROTO_ESP,
436a0a402   Herbert Xu   [IPSEC]: Move out...
661
  	.flags		= XFRM_TYPE_REPLAY_PROT,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
663
  	.init_state	= esp_init_state,
  	.destructor	= esp_destroy,
c5c252389   Patrick McHardy   [XFRM]: Optimize ...
664
  	.get_mtu	= esp4_get_mtu,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
665
  	.input		= esp_input,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
  	.output		= esp_output
  };
32613090a   Alexey Dobriyan   net: constify str...
668
  static const struct net_protocol esp4_protocol = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
670
671
  	.handler	=	xfrm4_rcv,
  	.err_handler	=	esp4_err,
  	.no_policy	=	1,
4fb236bac   Alexey Dobriyan   netns xfrm: AH/ES...
672
  	.netns_ok	=	1,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673
674
675
676
  };
  
  static int __init esp4_init(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  	if (xfrm_register_type(&esp_type, AF_INET) < 0) {
  		printk(KERN_INFO "ip esp init: can't add xfrm type
  ");
  		return -EAGAIN;
  	}
  	if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
  		printk(KERN_INFO "ip esp init: can't add protocol
  ");
  		xfrm_unregister_type(&esp_type, AF_INET);
  		return -EAGAIN;
  	}
  	return 0;
  }
  
  static void __exit esp4_fini(void)
  {
  	if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
  		printk(KERN_INFO "ip esp close: can't remove protocol
  ");
  	if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
  		printk(KERN_INFO "ip esp close: can't remove xfrm type
  ");
  }
  
  module_init(esp4_init);
  module_exit(esp4_fini);
  MODULE_LICENSE("GPL");
d3d6dd3ad   Masahide NAKAMURA   [XFRM]: Add modul...
704
  MODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_ESP);