xref: /linux/net/ipv4/ipcomp.c (revision 7b12b9137930eb821b68e1bfa11e9de692208620)
1 /*
2  * IP Payload Compression Protocol (IPComp) - RFC3173.
3  *
4  * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  * Todo:
12  *   - Tunable compression parameters.
13  *   - Compression stats.
14  *   - Adaptive compression.
15  */
16 #include <linux/config.h>
17 #include <linux/module.h>
18 #include <asm/scatterlist.h>
19 #include <asm/semaphore.h>
20 #include <linux/crypto.h>
21 #include <linux/pfkeyv2.h>
22 #include <linux/percpu.h>
23 #include <linux/smp.h>
24 #include <linux/list.h>
25 #include <linux/vmalloc.h>
26 #include <linux/rtnetlink.h>
27 #include <linux/mutex.h>
28 #include <net/ip.h>
29 #include <net/xfrm.h>
30 #include <net/icmp.h>
31 #include <net/ipcomp.h>
32 #include <net/protocol.h>
33 
34 struct ipcomp_tfms {
35 	struct list_head list;
36 	struct crypto_tfm **tfms;
37 	int users;
38 };
39 
40 static DEFINE_MUTEX(ipcomp_resource_mutex);
41 static void **ipcomp_scratches;
42 static int ipcomp_scratch_users;
43 static LIST_HEAD(ipcomp_tfms_list);
44 
45 static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
46 {
47 	int err, plen, dlen;
48 	struct iphdr *iph;
49 	struct ipcomp_data *ipcd = x->data;
50 	u8 *start, *scratch;
51 	struct crypto_tfm *tfm;
52 	int cpu;
53 
54 	plen = skb->len;
55 	dlen = IPCOMP_SCRATCH_SIZE;
56 	start = skb->data;
57 
58 	cpu = get_cpu();
59 	scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
60 	tfm = *per_cpu_ptr(ipcd->tfms, cpu);
61 
62 	err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
63 	if (err)
64 		goto out;
65 
66 	if (dlen < (plen + sizeof(struct ip_comp_hdr))) {
67 		err = -EINVAL;
68 		goto out;
69 	}
70 
71 	err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
72 	if (err)
73 		goto out;
74 
75 	skb_put(skb, dlen - plen);
76 	memcpy(skb->data, scratch, dlen);
77 	iph = skb->nh.iph;
78 	iph->tot_len = htons(dlen + iph->ihl * 4);
79 out:
80 	put_cpu();
81 	return err;
82 }
83 
84 static int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb)
85 {
86 	u8 nexthdr;
87 	int err = 0;
88 	struct iphdr *iph;
89 	union {
90 		struct iphdr	iph;
91 		char 		buf[60];
92 	} tmp_iph;
93 
94 
95 	if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
96 	    skb_linearize(skb, GFP_ATOMIC) != 0) {
97 	    	err = -ENOMEM;
98 	    	goto out;
99 	}
100 
101 	skb->ip_summed = CHECKSUM_NONE;
102 
103 	/* Remove ipcomp header and decompress original payload */
104 	iph = skb->nh.iph;
105 	memcpy(&tmp_iph, iph, iph->ihl * 4);
106 	nexthdr = *(u8 *)skb->data;
107 	skb_pull(skb, sizeof(struct ip_comp_hdr));
108 	skb->nh.raw += sizeof(struct ip_comp_hdr);
109 	memcpy(skb->nh.raw, &tmp_iph, tmp_iph.iph.ihl * 4);
110 	iph = skb->nh.iph;
111 	iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ip_comp_hdr));
112 	iph->protocol = nexthdr;
113 	skb->h.raw = skb->data;
114 	err = ipcomp_decompress(x, skb);
115 
116 out:
117 	return err;
118 }
119 
120 static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
121 {
122 	int err, plen, dlen, ihlen;
123 	struct iphdr *iph = skb->nh.iph;
124 	struct ipcomp_data *ipcd = x->data;
125 	u8 *start, *scratch;
126 	struct crypto_tfm *tfm;
127 	int cpu;
128 
129 	ihlen = iph->ihl * 4;
130 	plen = skb->len - ihlen;
131 	dlen = IPCOMP_SCRATCH_SIZE;
132 	start = skb->data + ihlen;
133 
134 	cpu = get_cpu();
135 	scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
136 	tfm = *per_cpu_ptr(ipcd->tfms, cpu);
137 
138 	err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
139 	if (err)
140 		goto out;
141 
142 	if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) {
143 		err = -EMSGSIZE;
144 		goto out;
145 	}
146 
147 	memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
148 	put_cpu();
149 
150 	pskb_trim(skb, ihlen + dlen + sizeof(struct ip_comp_hdr));
151 	return 0;
152 
153 out:
154 	put_cpu();
155 	return err;
156 }
157 
158 static int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb)
159 {
160 	int err;
161 	struct iphdr *iph;
162 	struct ip_comp_hdr *ipch;
163 	struct ipcomp_data *ipcd = x->data;
164 	int hdr_len = 0;
165 
166 	iph = skb->nh.iph;
167 	iph->tot_len = htons(skb->len);
168 	hdr_len = iph->ihl * 4;
169 	if ((skb->len - hdr_len) < ipcd->threshold) {
170 		/* Don't bother compressing */
171 		goto out_ok;
172 	}
173 
174 	if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
175 	    skb_linearize(skb, GFP_ATOMIC) != 0) {
176 		goto out_ok;
177 	}
178 
179 	err = ipcomp_compress(x, skb);
180 	iph = skb->nh.iph;
181 
182 	if (err) {
183 		goto out_ok;
184 	}
185 
186 	/* Install ipcomp header, convert into ipcomp datagram. */
187 	iph->tot_len = htons(skb->len);
188 	ipch = (struct ip_comp_hdr *)((char *)iph + iph->ihl * 4);
189 	ipch->nexthdr = iph->protocol;
190 	ipch->flags = 0;
191 	ipch->cpi = htons((u16 )ntohl(x->id.spi));
192 	iph->protocol = IPPROTO_COMP;
193 	ip_send_check(iph);
194 	return 0;
195 
196 out_ok:
197 	if (x->props.mode)
198 		ip_send_check(iph);
199 	return 0;
200 }
201 
202 static void ipcomp4_err(struct sk_buff *skb, u32 info)
203 {
204 	u32 spi;
205 	struct iphdr *iph = (struct iphdr *)skb->data;
206 	struct ip_comp_hdr *ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2));
207 	struct xfrm_state *x;
208 
209 	if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
210 	    skb->h.icmph->code != ICMP_FRAG_NEEDED)
211 		return;
212 
213 	spi = ntohl(ntohs(ipch->cpi));
214 	x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr,
215 	                      spi, IPPROTO_COMP, AF_INET);
216 	if (!x)
217 		return;
218 	NETDEBUG(KERN_DEBUG "pmtu discovery on SA IPCOMP/%08x/%u.%u.%u.%u\n",
219 		 spi, NIPQUAD(iph->daddr));
220 	xfrm_state_put(x);
221 }
222 
223 /* We always hold one tunnel user reference to indicate a tunnel */
224 static struct xfrm_state *ipcomp_tunnel_create(struct xfrm_state *x)
225 {
226 	struct xfrm_state *t;
227 
228 	t = xfrm_state_alloc();
229 	if (t == NULL)
230 		goto out;
231 
232 	t->id.proto = IPPROTO_IPIP;
233 	t->id.spi = x->props.saddr.a4;
234 	t->id.daddr.a4 = x->id.daddr.a4;
235 	memcpy(&t->sel, &x->sel, sizeof(t->sel));
236 	t->props.family = AF_INET;
237 	t->props.mode = 1;
238 	t->props.saddr.a4 = x->props.saddr.a4;
239 	t->props.flags = x->props.flags;
240 
241 	if (xfrm_init_state(t))
242 		goto error;
243 
244 	atomic_set(&t->tunnel_users, 1);
245 out:
246 	return t;
247 
248 error:
249 	t->km.state = XFRM_STATE_DEAD;
250 	xfrm_state_put(t);
251 	t = NULL;
252 	goto out;
253 }
254 
255 /*
256  * Must be protected by xfrm_cfg_mutex.  State and tunnel user references are
257  * always incremented on success.
258  */
259 static int ipcomp_tunnel_attach(struct xfrm_state *x)
260 {
261 	int err = 0;
262 	struct xfrm_state *t;
263 
264 	t = xfrm_state_lookup((xfrm_address_t *)&x->id.daddr.a4,
265 	                      x->props.saddr.a4, IPPROTO_IPIP, AF_INET);
266 	if (!t) {
267 		t = ipcomp_tunnel_create(x);
268 		if (!t) {
269 			err = -EINVAL;
270 			goto out;
271 		}
272 		xfrm_state_insert(t);
273 		xfrm_state_hold(t);
274 	}
275 	x->tunnel = t;
276 	atomic_inc(&t->tunnel_users);
277 out:
278 	return err;
279 }
280 
281 static void ipcomp_free_scratches(void)
282 {
283 	int i;
284 	void **scratches;
285 
286 	if (--ipcomp_scratch_users)
287 		return;
288 
289 	scratches = ipcomp_scratches;
290 	if (!scratches)
291 		return;
292 
293 	for_each_possible_cpu(i)
294 		vfree(*per_cpu_ptr(scratches, i));
295 
296 	free_percpu(scratches);
297 }
298 
299 static void **ipcomp_alloc_scratches(void)
300 {
301 	int i;
302 	void **scratches;
303 
304 	if (ipcomp_scratch_users++)
305 		return ipcomp_scratches;
306 
307 	scratches = alloc_percpu(void *);
308 	if (!scratches)
309 		return NULL;
310 
311 	ipcomp_scratches = scratches;
312 
313 	for_each_possible_cpu(i) {
314 		void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE);
315 		if (!scratch)
316 			return NULL;
317 		*per_cpu_ptr(scratches, i) = scratch;
318 	}
319 
320 	return scratches;
321 }
322 
323 static void ipcomp_free_tfms(struct crypto_tfm **tfms)
324 {
325 	struct ipcomp_tfms *pos;
326 	int cpu;
327 
328 	list_for_each_entry(pos, &ipcomp_tfms_list, list) {
329 		if (pos->tfms == tfms)
330 			break;
331 	}
332 
333 	BUG_TRAP(pos);
334 
335 	if (--pos->users)
336 		return;
337 
338 	list_del(&pos->list);
339 	kfree(pos);
340 
341 	if (!tfms)
342 		return;
343 
344 	for_each_possible_cpu(cpu) {
345 		struct crypto_tfm *tfm = *per_cpu_ptr(tfms, cpu);
346 		crypto_free_tfm(tfm);
347 	}
348 	free_percpu(tfms);
349 }
350 
351 static struct crypto_tfm **ipcomp_alloc_tfms(const char *alg_name)
352 {
353 	struct ipcomp_tfms *pos;
354 	struct crypto_tfm **tfms;
355 	int cpu;
356 
357 	/* This can be any valid CPU ID so we don't need locking. */
358 	cpu = raw_smp_processor_id();
359 
360 	list_for_each_entry(pos, &ipcomp_tfms_list, list) {
361 		struct crypto_tfm *tfm;
362 
363 		tfms = pos->tfms;
364 		tfm = *per_cpu_ptr(tfms, cpu);
365 
366 		if (!strcmp(crypto_tfm_alg_name(tfm), alg_name)) {
367 			pos->users++;
368 			return tfms;
369 		}
370 	}
371 
372 	pos = kmalloc(sizeof(*pos), GFP_KERNEL);
373 	if (!pos)
374 		return NULL;
375 
376 	pos->users = 1;
377 	INIT_LIST_HEAD(&pos->list);
378 	list_add(&pos->list, &ipcomp_tfms_list);
379 
380 	pos->tfms = tfms = alloc_percpu(struct crypto_tfm *);
381 	if (!tfms)
382 		goto error;
383 
384 	for_each_possible_cpu(cpu) {
385 		struct crypto_tfm *tfm = crypto_alloc_tfm(alg_name, 0);
386 		if (!tfm)
387 			goto error;
388 		*per_cpu_ptr(tfms, cpu) = tfm;
389 	}
390 
391 	return tfms;
392 
393 error:
394 	ipcomp_free_tfms(tfms);
395 	return NULL;
396 }
397 
398 static void ipcomp_free_data(struct ipcomp_data *ipcd)
399 {
400 	if (ipcd->tfms)
401 		ipcomp_free_tfms(ipcd->tfms);
402 	ipcomp_free_scratches();
403 }
404 
405 static void ipcomp_destroy(struct xfrm_state *x)
406 {
407 	struct ipcomp_data *ipcd = x->data;
408 	if (!ipcd)
409 		return;
410 	xfrm_state_delete_tunnel(x);
411 	mutex_lock(&ipcomp_resource_mutex);
412 	ipcomp_free_data(ipcd);
413 	mutex_unlock(&ipcomp_resource_mutex);
414 	kfree(ipcd);
415 }
416 
417 static int ipcomp_init_state(struct xfrm_state *x)
418 {
419 	int err;
420 	struct ipcomp_data *ipcd;
421 	struct xfrm_algo_desc *calg_desc;
422 
423 	err = -EINVAL;
424 	if (!x->calg)
425 		goto out;
426 
427 	if (x->encap)
428 		goto out;
429 
430 	err = -ENOMEM;
431 	ipcd = kmalloc(sizeof(*ipcd), GFP_KERNEL);
432 	if (!ipcd)
433 		goto out;
434 
435 	memset(ipcd, 0, sizeof(*ipcd));
436 	x->props.header_len = 0;
437 	if (x->props.mode)
438 		x->props.header_len += sizeof(struct iphdr);
439 
440 	mutex_lock(&ipcomp_resource_mutex);
441 	if (!ipcomp_alloc_scratches())
442 		goto error;
443 
444 	ipcd->tfms = ipcomp_alloc_tfms(x->calg->alg_name);
445 	if (!ipcd->tfms)
446 		goto error;
447 	mutex_unlock(&ipcomp_resource_mutex);
448 
449 	if (x->props.mode) {
450 		err = ipcomp_tunnel_attach(x);
451 		if (err)
452 			goto error_tunnel;
453 	}
454 
455 	calg_desc = xfrm_calg_get_byname(x->calg->alg_name, 0);
456 	BUG_ON(!calg_desc);
457 	ipcd->threshold = calg_desc->uinfo.comp.threshold;
458 	x->data = ipcd;
459 	err = 0;
460 out:
461 	return err;
462 
463 error_tunnel:
464 	mutex_lock(&ipcomp_resource_mutex);
465 error:
466 	ipcomp_free_data(ipcd);
467 	mutex_unlock(&ipcomp_resource_mutex);
468 	kfree(ipcd);
469 	goto out;
470 }
471 
472 static struct xfrm_type ipcomp_type = {
473 	.description	= "IPCOMP4",
474 	.owner		= THIS_MODULE,
475 	.proto	     	= IPPROTO_COMP,
476 	.init_state	= ipcomp_init_state,
477 	.destructor	= ipcomp_destroy,
478 	.input		= ipcomp_input,
479 	.output		= ipcomp_output
480 };
481 
482 static struct net_protocol ipcomp4_protocol = {
483 	.handler	=	xfrm4_rcv,
484 	.err_handler	=	ipcomp4_err,
485 	.no_policy	=	1,
486 };
487 
488 static int __init ipcomp4_init(void)
489 {
490 	if (xfrm_register_type(&ipcomp_type, AF_INET) < 0) {
491 		printk(KERN_INFO "ipcomp init: can't add xfrm type\n");
492 		return -EAGAIN;
493 	}
494 	if (inet_add_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0) {
495 		printk(KERN_INFO "ipcomp init: can't add protocol\n");
496 		xfrm_unregister_type(&ipcomp_type, AF_INET);
497 		return -EAGAIN;
498 	}
499 	return 0;
500 }
501 
502 static void __exit ipcomp4_fini(void)
503 {
504 	if (inet_del_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0)
505 		printk(KERN_INFO "ip ipcomp close: can't remove protocol\n");
506 	if (xfrm_unregister_type(&ipcomp_type, AF_INET) < 0)
507 		printk(KERN_INFO "ip ipcomp close: can't remove xfrm type\n");
508 }
509 
510 module_init(ipcomp4_init);
511 module_exit(ipcomp4_fini);
512 
513 MODULE_LICENSE("GPL");
514 MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3173");
515 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
516 
517