Commit 6fccab671f2f0a24b799f29a4ec878f62d34656c

Authored by Herbert Xu
Committed by David S. Miller
1 parent cffe1c5d7a

ipsec: ipcomp - Merge IPComp implementations

This patch merges the IPv4/IPv6 IPComp implementations since most
of the code is identical.  As a result future enhancements will no
longer need to be duplicated.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 8 changed files with 377 additions and 606 deletions Side-by-side Diff

include/net/ipcomp.h
... ... @@ -14,6 +14,12 @@
14 14  
15 15 struct ip_comp_hdr;
16 16 struct sk_buff;
  17 +struct xfrm_state;
  18 +
  19 +int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb);
  20 +int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb);
  21 +void ipcomp_destroy(struct xfrm_state *x);
  22 +int ipcomp_init_state(struct xfrm_state *x);
17 23  
18 24 static inline struct ip_comp_hdr *ip_comp_hdr(const struct sk_buff *skb)
19 25 {
... ... @@ -356,10 +356,8 @@
356 356  
357 357 config INET_IPCOMP
358 358 tristate "IP: IPComp transformation"
359   - select XFRM
360 359 select INET_XFRM_TUNNEL
361   - select CRYPTO
362   - select CRYPTO_DEFLATE
  360 + select XFRM_IPCOMP
363 361 ---help---
364 362 Support for IP Payload Compression Protocol (IPComp) (RFC3173),
365 363 typically needed for IPsec.
... ... @@ -14,154 +14,15 @@
14 14 * - Adaptive compression.
15 15 */
16 16 #include <linux/module.h>
17   -#include <linux/crypto.h>
18 17 #include <linux/err.h>
19   -#include <linux/pfkeyv2.h>
20   -#include <linux/percpu.h>
21   -#include <linux/smp.h>
22   -#include <linux/list.h>
23   -#include <linux/vmalloc.h>
24 18 #include <linux/rtnetlink.h>
25   -#include <linux/mutex.h>
26 19 #include <net/ip.h>
27 20 #include <net/xfrm.h>
28 21 #include <net/icmp.h>
29 22 #include <net/ipcomp.h>
30 23 #include <net/protocol.h>
  24 +#include <net/sock.h>
31 25  
32   -struct ipcomp_tfms {
33   - struct list_head list;
34   - struct crypto_comp **tfms;
35   - int users;
36   -};
37   -
38   -static DEFINE_MUTEX(ipcomp_resource_mutex);
39   -static void **ipcomp_scratches;
40   -static int ipcomp_scratch_users;
41   -static LIST_HEAD(ipcomp_tfms_list);
42   -
43   -static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
44   -{
45   - struct ipcomp_data *ipcd = x->data;
46   - const int plen = skb->len;
47   - int dlen = IPCOMP_SCRATCH_SIZE;
48   - const u8 *start = skb->data;
49   - const int cpu = get_cpu();
50   - u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
51   - struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
52   - int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
53   -
54   - if (err)
55   - goto out;
56   -
57   - if (dlen < (plen + sizeof(struct ip_comp_hdr))) {
58   - err = -EINVAL;
59   - goto out;
60   - }
61   -
62   - err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
63   - if (err)
64   - goto out;
65   -
66   - skb->truesize += dlen - plen;
67   - __skb_put(skb, dlen - plen);
68   - skb_copy_to_linear_data(skb, scratch, dlen);
69   -out:
70   - put_cpu();
71   - return err;
72   -}
73   -
74   -static int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb)
75   -{
76   - int nexthdr;
77   - int err = -ENOMEM;
78   - struct ip_comp_hdr *ipch;
79   -
80   - if (skb_linearize_cow(skb))
81   - goto out;
82   -
83   - skb->ip_summed = CHECKSUM_NONE;
84   -
85   - /* Remove ipcomp header and decompress original payload */
86   - ipch = (void *)skb->data;
87   - nexthdr = ipch->nexthdr;
88   -
89   - skb->transport_header = skb->network_header + sizeof(*ipch);
90   - __skb_pull(skb, sizeof(*ipch));
91   - err = ipcomp_decompress(x, skb);
92   - if (err)
93   - goto out;
94   -
95   - err = nexthdr;
96   -
97   -out:
98   - return err;
99   -}
100   -
101   -static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
102   -{
103   - struct ipcomp_data *ipcd = x->data;
104   - const int plen = skb->len;
105   - int dlen = IPCOMP_SCRATCH_SIZE;
106   - u8 *start = skb->data;
107   - const int cpu = get_cpu();
108   - u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
109   - struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
110   - int err;
111   -
112   - local_bh_disable();
113   - err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
114   - local_bh_enable();
115   - if (err)
116   - goto out;
117   -
118   - if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) {
119   - err = -EMSGSIZE;
120   - goto out;
121   - }
122   -
123   - memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
124   - put_cpu();
125   -
126   - pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr));
127   - return 0;
128   -
129   -out:
130   - put_cpu();
131   - return err;
132   -}
133   -
134   -static int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb)
135   -{
136   - int err;
137   - struct ip_comp_hdr *ipch;
138   - struct ipcomp_data *ipcd = x->data;
139   -
140   - if (skb->len < ipcd->threshold) {
141   - /* Don't bother compressing */
142   - goto out_ok;
143   - }
144   -
145   - if (skb_linearize_cow(skb))
146   - goto out_ok;
147   -
148   - err = ipcomp_compress(x, skb);
149   -
150   - if (err) {
151   - goto out_ok;
152   - }
153   -
154   - /* Install ipcomp header, convert into ipcomp datagram. */
155   - ipch = ip_comp_hdr(skb);
156   - ipch->nexthdr = *skb_mac_header(skb);
157   - ipch->flags = 0;
158   - ipch->cpi = htons((u16 )ntohl(x->id.spi));
159   - *skb_mac_header(skb) = IPPROTO_COMP;
160   -out_ok:
161   - skb_push(skb, -skb_network_offset(skb));
162   - return 0;
163   -}
164   -
165 26 static void ipcomp4_err(struct sk_buff *skb, u32 info)
166 27 {
167 28 __be32 spi;
168 29  
169 30  
... ... @@ -241,156 +102,12 @@
241 102 return err;
242 103 }
243 104  
244   -static void ipcomp_free_scratches(void)
  105 +static int ipcomp4_init_state(struct xfrm_state *x)
245 106 {
246   - int i;
247   - void **scratches;
248   -
249   - if (--ipcomp_scratch_users)
250   - return;
251   -
252   - scratches = ipcomp_scratches;
253   - if (!scratches)
254   - return;
255   -
256   - for_each_possible_cpu(i)
257   - vfree(*per_cpu_ptr(scratches, i));
258   -
259   - free_percpu(scratches);
260   -}
261   -
262   -static void **ipcomp_alloc_scratches(void)
263   -{
264   - int i;
265   - void **scratches;
266   -
267   - if (ipcomp_scratch_users++)
268   - return ipcomp_scratches;
269   -
270   - scratches = alloc_percpu(void *);
271   - if (!scratches)
272   - return NULL;
273   -
274   - ipcomp_scratches = scratches;
275   -
276   - for_each_possible_cpu(i) {
277   - void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE);
278   - if (!scratch)
279   - return NULL;
280   - *per_cpu_ptr(scratches, i) = scratch;
281   - }
282   -
283   - return scratches;
284   -}
285   -
286   -static void ipcomp_free_tfms(struct crypto_comp **tfms)
287   -{
288   - struct ipcomp_tfms *pos;
289   - int cpu;
290   -
291   - list_for_each_entry(pos, &ipcomp_tfms_list, list) {
292   - if (pos->tfms == tfms)
293   - break;
294   - }
295   -
296   - BUG_TRAP(pos);
297   -
298   - if (--pos->users)
299   - return;
300   -
301   - list_del(&pos->list);
302   - kfree(pos);
303   -
304   - if (!tfms)
305   - return;
306   -
307   - for_each_possible_cpu(cpu) {
308   - struct crypto_comp *tfm = *per_cpu_ptr(tfms, cpu);
309   - crypto_free_comp(tfm);
310   - }
311   - free_percpu(tfms);
312   -}
313   -
314   -static struct crypto_comp **ipcomp_alloc_tfms(const char *alg_name)
315   -{
316   - struct ipcomp_tfms *pos;
317   - struct crypto_comp **tfms;
318   - int cpu;
319   -
320   - /* This can be any valid CPU ID so we don't need locking. */
321   - cpu = raw_smp_processor_id();
322   -
323   - list_for_each_entry(pos, &ipcomp_tfms_list, list) {
324   - struct crypto_comp *tfm;
325   -
326   - tfms = pos->tfms;
327   - tfm = *per_cpu_ptr(tfms, cpu);
328   -
329   - if (!strcmp(crypto_comp_name(tfm), alg_name)) {
330   - pos->users++;
331   - return tfms;
332   - }
333   - }
334   -
335   - pos = kmalloc(sizeof(*pos), GFP_KERNEL);
336   - if (!pos)
337   - return NULL;
338   -
339   - pos->users = 1;
340   - INIT_LIST_HEAD(&pos->list);
341   - list_add(&pos->list, &ipcomp_tfms_list);
342   -
343   - pos->tfms = tfms = alloc_percpu(struct crypto_comp *);
344   - if (!tfms)
345   - goto error;
346   -
347   - for_each_possible_cpu(cpu) {
348   - struct crypto_comp *tfm = crypto_alloc_comp(alg_name, 0,
349   - CRYPTO_ALG_ASYNC);
350   - if (IS_ERR(tfm))
351   - goto error;
352   - *per_cpu_ptr(tfms, cpu) = tfm;
353   - }
354   -
355   - return tfms;
356   -
357   -error:
358   - ipcomp_free_tfms(tfms);
359   - return NULL;
360   -}
361   -
362   -static void ipcomp_free_data(struct ipcomp_data *ipcd)
363   -{
364   - if (ipcd->tfms)
365   - ipcomp_free_tfms(ipcd->tfms);
366   - ipcomp_free_scratches();
367   -}
368   -
369   -static void ipcomp_destroy(struct xfrm_state *x)
370   -{
371   - struct ipcomp_data *ipcd = x->data;
372   - if (!ipcd)
373   - return;
374   - xfrm_state_delete_tunnel(x);
375   - mutex_lock(&ipcomp_resource_mutex);
376   - ipcomp_free_data(ipcd);
377   - mutex_unlock(&ipcomp_resource_mutex);
378   - kfree(ipcd);
379   -}
380   -
381   -static int ipcomp_init_state(struct xfrm_state *x)
382   -{
383 107 int err;
384 108 struct ipcomp_data *ipcd;
385 109 struct xfrm_algo_desc *calg_desc;
386 110  
387   - err = -EINVAL;
388   - if (!x->calg)
389   - goto out;
390   -
391   - if (x->encap)
392   - goto out;
393   -
394 111 x->props.header_len = 0;
395 112 switch (x->props.mode) {
396 113 case XFRM_MODE_TRANSPORT:
397 114  
398 115  
399 116  
... ... @@ -402,40 +119,22 @@
402 119 goto out;
403 120 }
404 121  
405   - err = -ENOMEM;
406   - ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL);
407   - if (!ipcd)
  122 + err = ipcomp_init_state(x);
  123 + if (err)
408 124 goto out;
409 125  
410   - mutex_lock(&ipcomp_resource_mutex);
411   - if (!ipcomp_alloc_scratches())
412   - goto error;
413   -
414   - ipcd->tfms = ipcomp_alloc_tfms(x->calg->alg_name);
415   - if (!ipcd->tfms)
416   - goto error;
417   - mutex_unlock(&ipcomp_resource_mutex);
418   -
419 126 if (x->props.mode == XFRM_MODE_TUNNEL) {
420 127 err = ipcomp_tunnel_attach(x);
421 128 if (err)
422 129 goto error_tunnel;
423 130 }
424 131  
425   - calg_desc = xfrm_calg_get_byname(x->calg->alg_name, 0);
426   - BUG_ON(!calg_desc);
427   - ipcd->threshold = calg_desc->uinfo.comp.threshold;
428   - x->data = ipcd;
429 132 err = 0;
430 133 out:
431 134 return err;
432 135  
433 136 error_tunnel:
434   - mutex_lock(&ipcomp_resource_mutex);
435   -error:
436   - ipcomp_free_data(ipcd);
437   - mutex_unlock(&ipcomp_resource_mutex);
438   - kfree(ipcd);
  137 + ipcomp_destroy(x);
439 138 goto out;
440 139 }
441 140  
... ... @@ -443,7 +142,7 @@
443 142 .description = "IPCOMP4",
444 143 .owner = THIS_MODULE,
445 144 .proto = IPPROTO_COMP,
446   - .init_state = ipcomp_init_state,
  145 + .init_state = ipcomp4_init_state,
447 146 .destructor = ipcomp_destroy,
448 147 .input = ipcomp_input,
449 148 .output = ipcomp_output
... ... @@ -481,7 +180,7 @@
481 180 module_exit(ipcomp4_fini);
482 181  
483 182 MODULE_LICENSE("GPL");
484   -MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3173");
  183 +MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp/IPv4) - RFC3173");
485 184 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
486 185  
487 186 MODULE_ALIAS_XFRM_TYPE(AF_INET, XFRM_PROTO_COMP);
... ... @@ -96,10 +96,8 @@
96 96  
97 97 config INET6_IPCOMP
98 98 tristate "IPv6: IPComp transformation"
99   - select XFRM
100 99 select INET6_XFRM_TUNNEL
101   - select CRYPTO
102   - select CRYPTO_DEFLATE
  100 + select XFRM_IPCOMP
103 101 ---help---
104 102 Support for IP Payload Compression Protocol (IPComp) (RFC3173),
105 103 typically needed for IPsec.
... ... @@ -50,125 +50,6 @@
50 50 #include <linux/icmpv6.h>
51 51 #include <linux/mutex.h>
52 52  
53   -struct ipcomp6_tfms {
54   - struct list_head list;
55   - struct crypto_comp **tfms;
56   - int users;
57   -};
58   -
59   -static DEFINE_MUTEX(ipcomp6_resource_mutex);
60   -static void **ipcomp6_scratches;
61   -static int ipcomp6_scratch_users;
62   -static LIST_HEAD(ipcomp6_tfms_list);
63   -
64   -static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)
65   -{
66   - int nexthdr;
67   - int err = -ENOMEM;
68   - struct ip_comp_hdr *ipch;
69   - int plen, dlen;
70   - struct ipcomp_data *ipcd = x->data;
71   - u8 *start, *scratch;
72   - struct crypto_comp *tfm;
73   - int cpu;
74   -
75   - if (skb_linearize_cow(skb))
76   - goto out;
77   -
78   - skb->ip_summed = CHECKSUM_NONE;
79   -
80   - /* Remove ipcomp header and decompress original payload */
81   - ipch = (void *)skb->data;
82   - nexthdr = ipch->nexthdr;
83   -
84   - skb->transport_header = skb->network_header + sizeof(*ipch);
85   - __skb_pull(skb, sizeof(*ipch));
86   -
87   - /* decompression */
88   - plen = skb->len;
89   - dlen = IPCOMP_SCRATCH_SIZE;
90   - start = skb->data;
91   -
92   - cpu = get_cpu();
93   - scratch = *per_cpu_ptr(ipcomp6_scratches, cpu);
94   - tfm = *per_cpu_ptr(ipcd->tfms, cpu);
95   -
96   - err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
97   - if (err)
98   - goto out_put_cpu;
99   -
100   - if (dlen < (plen + sizeof(*ipch))) {
101   - err = -EINVAL;
102   - goto out_put_cpu;
103   - }
104   -
105   - err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
106   - if (err) {
107   - goto out_put_cpu;
108   - }
109   -
110   - skb->truesize += dlen - plen;
111   - __skb_put(skb, dlen - plen);
112   - skb_copy_to_linear_data(skb, scratch, dlen);
113   - err = nexthdr;
114   -
115   -out_put_cpu:
116   - put_cpu();
117   -out:
118   - return err;
119   -}
120   -
121   -static int ipcomp6_output(struct xfrm_state *x, struct sk_buff *skb)
122   -{
123   - int err;
124   - struct ip_comp_hdr *ipch;
125   - struct ipcomp_data *ipcd = x->data;
126   - int plen, dlen;
127   - u8 *start, *scratch;
128   - struct crypto_comp *tfm;
129   - int cpu;
130   -
131   - /* check whether datagram len is larger than threshold */
132   - if (skb->len < ipcd->threshold) {
133   - goto out_ok;
134   - }
135   -
136   - if (skb_linearize_cow(skb))
137   - goto out_ok;
138   -
139   - /* compression */
140   - plen = skb->len;
141   - dlen = IPCOMP_SCRATCH_SIZE;
142   - start = skb->data;
143   -
144   - cpu = get_cpu();
145   - scratch = *per_cpu_ptr(ipcomp6_scratches, cpu);
146   - tfm = *per_cpu_ptr(ipcd->tfms, cpu);
147   -
148   - local_bh_disable();
149   - err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
150   - local_bh_enable();
151   - if (err || (dlen + sizeof(*ipch)) >= plen) {
152   - put_cpu();
153   - goto out_ok;
154   - }
155   - memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
156   - put_cpu();
157   - pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr));
158   -
159   - /* insert ipcomp header and replace datagram */
160   - ipch = ip_comp_hdr(skb);
161   - ipch->nexthdr = *skb_mac_header(skb);
162   - ipch->flags = 0;
163   - ipch->cpi = htons((u16 )ntohl(x->id.spi));
164   - *skb_mac_header(skb) = IPPROTO_COMP;
165   -
166   -out_ok:
167   - skb_push(skb, -skb_network_offset(skb));
168   -
169   - return 0;
170   -}
171   -
172 53 static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
173 54 int type, int code, int offset, __be32 info)
174 55 {
175 56  
... ... @@ -251,161 +132,12 @@
251 132 return err;
252 133 }
253 134  
254   -static void ipcomp6_free_scratches(void)
255   -{
256   - int i;
257   - void **scratches;
258   -
259   - if (--ipcomp6_scratch_users)
260   - return;
261   -
262   - scratches = ipcomp6_scratches;
263   - if (!scratches)
264   - return;
265   -
266   - for_each_possible_cpu(i) {
267   - void *scratch = *per_cpu_ptr(scratches, i);
268   -
269   - vfree(scratch);
270   - }
271   -
272   - free_percpu(scratches);
273   -}
274   -
275   -static void **ipcomp6_alloc_scratches(void)
276   -{
277   - int i;
278   - void **scratches;
279   -
280   - if (ipcomp6_scratch_users++)
281   - return ipcomp6_scratches;
282   -
283   - scratches = alloc_percpu(void *);
284   - if (!scratches)
285   - return NULL;
286   -
287   - ipcomp6_scratches = scratches;
288   -
289   - for_each_possible_cpu(i) {
290   - void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE);
291   - if (!scratch)
292   - return NULL;
293   - *per_cpu_ptr(scratches, i) = scratch;
294   - }
295   -
296   - return scratches;
297   -}
298   -
299   -static void ipcomp6_free_tfms(struct crypto_comp **tfms)
300   -{
301   - struct ipcomp6_tfms *pos;
302   - int cpu;
303   -
304   - list_for_each_entry(pos, &ipcomp6_tfms_list, list) {
305   - if (pos->tfms == tfms)
306   - break;
307   - }
308   -
309   - BUG_TRAP(pos);
310   -
311   - if (--pos->users)
312   - return;
313   -
314   - list_del(&pos->list);
315   - kfree(pos);
316   -
317   - if (!tfms)
318   - return;
319   -
320   - for_each_possible_cpu(cpu) {
321   - struct crypto_comp *tfm = *per_cpu_ptr(tfms, cpu);
322   - crypto_free_comp(tfm);
323   - }
324   - free_percpu(tfms);
325   -}
326   -
327   -static struct crypto_comp **ipcomp6_alloc_tfms(const char *alg_name)
328   -{
329   - struct ipcomp6_tfms *pos;
330   - struct crypto_comp **tfms;
331   - int cpu;
332   -
333   - /* This can be any valid CPU ID so we don't need locking. */
334   - cpu = raw_smp_processor_id();
335   -
336   - list_for_each_entry(pos, &ipcomp6_tfms_list, list) {
337   - struct crypto_comp *tfm;
338   -
339   - tfms = pos->tfms;
340   - tfm = *per_cpu_ptr(tfms, cpu);
341   -
342   - if (!strcmp(crypto_comp_name(tfm), alg_name)) {
343   - pos->users++;
344   - return tfms;
345   - }
346   - }
347   -
348   - pos = kmalloc(sizeof(*pos), GFP_KERNEL);
349   - if (!pos)
350   - return NULL;
351   -
352   - pos->users = 1;
353   - INIT_LIST_HEAD(&pos->list);
354   - list_add(&pos->list, &ipcomp6_tfms_list);
355   -
356   - pos->tfms = tfms = alloc_percpu(struct crypto_comp *);
357   - if (!tfms)
358   - goto error;
359   -
360   - for_each_possible_cpu(cpu) {
361   - struct crypto_comp *tfm = crypto_alloc_comp(alg_name, 0,
362   - CRYPTO_ALG_ASYNC);
363   - if (IS_ERR(tfm))
364   - goto error;
365   - *per_cpu_ptr(tfms, cpu) = tfm;
366   - }
367   -
368   - return tfms;
369   -
370   -error:
371   - ipcomp6_free_tfms(tfms);
372   - return NULL;
373   -}
374   -
375   -static void ipcomp6_free_data(struct ipcomp_data *ipcd)
376   -{
377   - if (ipcd->tfms)
378   - ipcomp6_free_tfms(ipcd->tfms);
379   - ipcomp6_free_scratches();
380   -}
381   -
382   -static void ipcomp6_destroy(struct xfrm_state *x)
383   -{
384   - struct ipcomp_data *ipcd = x->data;
385   - if (!ipcd)
386   - return;
387   - xfrm_state_delete_tunnel(x);
388   - mutex_lock(&ipcomp6_resource_mutex);
389   - ipcomp6_free_data(ipcd);
390   - mutex_unlock(&ipcomp6_resource_mutex);
391   - kfree(ipcd);
392   -
393   - xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr);
394   -}
395   -
396 135 static int ipcomp6_init_state(struct xfrm_state *x)
397 136 {
398 137 int err;
399 138 struct ipcomp_data *ipcd;
400 139 struct xfrm_algo_desc *calg_desc;
401 140  
402   - err = -EINVAL;
403   - if (!x->calg)
404   - goto out;
405   -
406   - if (x->encap)
407   - goto out;
408   -
409 141 x->props.header_len = 0;
410 142 switch (x->props.mode) {
411 143 case XFRM_MODE_TRANSPORT:
412 144  
413 145  
414 146  
... ... @@ -417,39 +149,21 @@
417 149 goto out;
418 150 }
419 151  
420   - err = -ENOMEM;
421   - ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL);
422   - if (!ipcd)
  152 + err = ipcomp_init_state(x);
  153 + if (err)
423 154 goto out;
424 155  
425   - mutex_lock(&ipcomp6_resource_mutex);
426   - if (!ipcomp6_alloc_scratches())
427   - goto error;
428   -
429   - ipcd->tfms = ipcomp6_alloc_tfms(x->calg->alg_name);
430   - if (!ipcd->tfms)
431   - goto error;
432   - mutex_unlock(&ipcomp6_resource_mutex);
433   -
434 156 if (x->props.mode == XFRM_MODE_TUNNEL) {
435 157 err = ipcomp6_tunnel_attach(x);
436 158 if (err)
437 159 goto error_tunnel;
438 160 }
439 161  
440   - calg_desc = xfrm_calg_get_byname(x->calg->alg_name, 0);
441   - BUG_ON(!calg_desc);
442   - ipcd->threshold = calg_desc->uinfo.comp.threshold;
443   - x->data = ipcd;
444 162 err = 0;
445 163 out:
446 164 return err;
447 165 error_tunnel:
448   - mutex_lock(&ipcomp6_resource_mutex);
449   -error:
450   - ipcomp6_free_data(ipcd);
451   - mutex_unlock(&ipcomp6_resource_mutex);
452   - kfree(ipcd);
  166 + ipcomp_destroy(x);
453 167  
454 168 goto out;
455 169 }
... ... @@ -460,9 +174,9 @@
460 174 .owner = THIS_MODULE,
461 175 .proto = IPPROTO_COMP,
462 176 .init_state = ipcomp6_init_state,
463   - .destructor = ipcomp6_destroy,
464   - .input = ipcomp6_input,
465   - .output = ipcomp6_output,
  177 + .destructor = ipcomp_destroy,
  178 + .input = ipcomp_input,
  179 + .output = ipcomp_output,
466 180 .hdr_offset = xfrm6_find_1stfragopt,
467 181 };
468 182  
... ... @@ -46,6 +46,12 @@
46 46  
47 47 If unsure, say N.
48 48  
  49 +config XFRM_IPCOMP
  50 + tristate
  51 + select XFRM
  52 + select CRYPTO
  53 + select CRYPTO_DEFLATE
  54 +
49 55 config NET_KEY
50 56 tristate "PF_KEY sockets"
51 57 select XFRM
... ... @@ -6,4 +6,5 @@
6 6 xfrm_input.o xfrm_output.o xfrm_algo.o
7 7 obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
8 8 obj-$(CONFIG_XFRM_USER) += xfrm_user.o
  9 +obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o
net/xfrm/xfrm_ipcomp.c
  1 +/*
  2 + * IP Payload Compression Protocol (IPComp) - RFC3173.
  3 + *
  4 + * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
  5 + * Copyright (c) 2003-2008 Herbert Xu <herbert@gondor.apana.org.au>
  6 + *
  7 + * This program is free software; you can redistribute it and/or modify it
  8 + * under the terms of the GNU General Public License as published by the Free
  9 + * Software Foundation; either version 2 of the License, or (at your option)
  10 + * any later version.
  11 + *
  12 + * Todo:
  13 + * - Tunable compression parameters.
  14 + * - Compression stats.
  15 + * - Adaptive compression.
  16 + */
  17 +
  18 +#include <linux/crypto.h>
  19 +#include <linux/err.h>
  20 +#include <linux/list.h>
  21 +#include <linux/module.h>
  22 +#include <linux/mutex.h>
  23 +#include <linux/percpu.h>
  24 +#include <linux/rtnetlink.h>
  25 +#include <linux/smp.h>
  26 +#include <linux/vmalloc.h>
  27 +#include <net/ip.h>
  28 +#include <net/ipcomp.h>
  29 +#include <net/xfrm.h>
  30 +
  31 +struct ipcomp_tfms {
  32 + struct list_head list;
  33 + struct crypto_comp **tfms;
  34 + int users;
  35 +};
  36 +
  37 +static DEFINE_MUTEX(ipcomp_resource_mutex);
  38 +static void **ipcomp_scratches;
  39 +static int ipcomp_scratch_users;
  40 +static LIST_HEAD(ipcomp_tfms_list);
  41 +
  42 +static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
  43 +{
  44 + struct ipcomp_data *ipcd = x->data;
  45 + const int plen = skb->len;
  46 + int dlen = IPCOMP_SCRATCH_SIZE;
  47 + const u8 *start = skb->data;
  48 + const int cpu = get_cpu();
  49 + u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
  50 + struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
  51 + int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
  52 +
  53 + if (err)
  54 + goto out;
  55 +
  56 + if (dlen < (plen + sizeof(struct ip_comp_hdr))) {
  57 + err = -EINVAL;
  58 + goto out;
  59 + }
  60 +
  61 + err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
  62 + if (err)
  63 + goto out;
  64 +
  65 + skb->truesize += dlen - plen;
  66 + __skb_put(skb, dlen - plen);
  67 + skb_copy_to_linear_data(skb, scratch, dlen);
  68 +out:
  69 + put_cpu();
  70 + return err;
  71 +}
  72 +
  73 +int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb)
  74 +{
  75 + int nexthdr;
  76 + int err = -ENOMEM;
  77 + struct ip_comp_hdr *ipch;
  78 +
  79 + if (skb_linearize_cow(skb))
  80 + goto out;
  81 +
  82 + skb->ip_summed = CHECKSUM_NONE;
  83 +
  84 + /* Remove ipcomp header and decompress original payload */
  85 + ipch = (void *)skb->data;
  86 + nexthdr = ipch->nexthdr;
  87 +
  88 + skb->transport_header = skb->network_header + sizeof(*ipch);
  89 + __skb_pull(skb, sizeof(*ipch));
  90 + err = ipcomp_decompress(x, skb);
  91 + if (err)
  92 + goto out;
  93 +
  94 + err = nexthdr;
  95 +
  96 +out:
  97 + return err;
  98 +}
  99 +EXPORT_SYMBOL_GPL(ipcomp_input);
  100 +
  101 +static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
  102 +{
  103 + struct ipcomp_data *ipcd = x->data;
  104 + const int plen = skb->len;
  105 + int dlen = IPCOMP_SCRATCH_SIZE;
  106 + u8 *start = skb->data;
  107 + const int cpu = get_cpu();
  108 + u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
  109 + struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
  110 + int err;
  111 +
  112 + local_bh_disable();
  113 + err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
  114 + local_bh_enable();
  115 + if (err)
  116 + goto out;
  117 +
  118 + if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) {
  119 + err = -EMSGSIZE;
  120 + goto out;
  121 + }
  122 +
  123 + memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
  124 + put_cpu();
  125 +
  126 + pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr));
  127 + return 0;
  128 +
  129 +out:
  130 + put_cpu();
  131 + return err;
  132 +}
  133 +
  134 +int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb)
  135 +{
  136 + int err;
  137 + struct ip_comp_hdr *ipch;
  138 + struct ipcomp_data *ipcd = x->data;
  139 +
  140 + if (skb->len < ipcd->threshold) {
  141 + /* Don't bother compressing */
  142 + goto out_ok;
  143 + }
  144 +
  145 + if (skb_linearize_cow(skb))
  146 + goto out_ok;
  147 +
  148 + err = ipcomp_compress(x, skb);
  149 +
  150 + if (err) {
  151 + goto out_ok;
  152 + }
  153 +
  154 + /* Install ipcomp header, convert into ipcomp datagram. */
  155 + ipch = ip_comp_hdr(skb);
  156 + ipch->nexthdr = *skb_mac_header(skb);
  157 + ipch->flags = 0;
  158 + ipch->cpi = htons((u16 )ntohl(x->id.spi));
  159 + *skb_mac_header(skb) = IPPROTO_COMP;
  160 +out_ok:
  161 + skb_push(skb, -skb_network_offset(skb));
  162 + return 0;
  163 +}
  164 +EXPORT_SYMBOL_GPL(ipcomp_output);
  165 +
  166 +static void ipcomp_free_scratches(void)
  167 +{
  168 + int i;
  169 + void **scratches;
  170 +
  171 + if (--ipcomp_scratch_users)
  172 + return;
  173 +
  174 + scratches = ipcomp_scratches;
  175 + if (!scratches)
  176 + return;
  177 +
  178 + for_each_possible_cpu(i)
  179 + vfree(*per_cpu_ptr(scratches, i));
  180 +
  181 + free_percpu(scratches);
  182 +}
  183 +
  184 +static void **ipcomp_alloc_scratches(void)
  185 +{
  186 + int i;
  187 + void **scratches;
  188 +
  189 + if (ipcomp_scratch_users++)
  190 + return ipcomp_scratches;
  191 +
  192 + scratches = alloc_percpu(void *);
  193 + if (!scratches)
  194 + return NULL;
  195 +
  196 + ipcomp_scratches = scratches;
  197 +
  198 + for_each_possible_cpu(i) {
  199 + void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE);
  200 + if (!scratch)
  201 + return NULL;
  202 + *per_cpu_ptr(scratches, i) = scratch;
  203 + }
  204 +
  205 + return scratches;
  206 +}
  207 +
  208 +static void ipcomp_free_tfms(struct crypto_comp **tfms)
  209 +{
  210 + struct ipcomp_tfms *pos;
  211 + int cpu;
  212 +
  213 + list_for_each_entry(pos, &ipcomp_tfms_list, list) {
  214 + if (pos->tfms == tfms)
  215 + break;
  216 + }
  217 +
  218 + BUG_TRAP(pos);
  219 +
  220 + if (--pos->users)
  221 + return;
  222 +
  223 + list_del(&pos->list);
  224 + kfree(pos);
  225 +
  226 + if (!tfms)
  227 + return;
  228 +
  229 + for_each_possible_cpu(cpu) {
  230 + struct crypto_comp *tfm = *per_cpu_ptr(tfms, cpu);
  231 + crypto_free_comp(tfm);
  232 + }
  233 + free_percpu(tfms);
  234 +}
  235 +
  236 +static struct crypto_comp **ipcomp_alloc_tfms(const char *alg_name)
  237 +{
  238 + struct ipcomp_tfms *pos;
  239 + struct crypto_comp **tfms;
  240 + int cpu;
  241 +
  242 + /* This can be any valid CPU ID so we don't need locking. */
  243 + cpu = raw_smp_processor_id();
  244 +
  245 + list_for_each_entry(pos, &ipcomp_tfms_list, list) {
  246 + struct crypto_comp *tfm;
  247 +
  248 + tfms = pos->tfms;
  249 + tfm = *per_cpu_ptr(tfms, cpu);
  250 +
  251 + if (!strcmp(crypto_comp_name(tfm), alg_name)) {
  252 + pos->users++;
  253 + return tfms;
  254 + }
  255 + }
  256 +
  257 + pos = kmalloc(sizeof(*pos), GFP_KERNEL);
  258 + if (!pos)
  259 + return NULL;
  260 +
  261 + pos->users = 1;
  262 + INIT_LIST_HEAD(&pos->list);
  263 + list_add(&pos->list, &ipcomp_tfms_list);
  264 +
  265 + pos->tfms = tfms = alloc_percpu(struct crypto_comp *);
  266 + if (!tfms)
  267 + goto error;
  268 +
  269 + for_each_possible_cpu(cpu) {
  270 + struct crypto_comp *tfm = crypto_alloc_comp(alg_name, 0,
  271 + CRYPTO_ALG_ASYNC);
  272 + if (IS_ERR(tfm))
  273 + goto error;
  274 + *per_cpu_ptr(tfms, cpu) = tfm;
  275 + }
  276 +
  277 + return tfms;
  278 +
  279 +error:
  280 + ipcomp_free_tfms(tfms);
  281 + return NULL;
  282 +}
  283 +
  284 +static void ipcomp_free_data(struct ipcomp_data *ipcd)
  285 +{
  286 + if (ipcd->tfms)
  287 + ipcomp_free_tfms(ipcd->tfms);
  288 + ipcomp_free_scratches();
  289 +}
  290 +
  291 +void ipcomp_destroy(struct xfrm_state *x)
  292 +{
  293 + struct ipcomp_data *ipcd = x->data;
  294 + if (!ipcd)
  295 + return;
  296 + xfrm_state_delete_tunnel(x);
  297 + mutex_lock(&ipcomp_resource_mutex);
  298 + ipcomp_free_data(ipcd);
  299 + mutex_unlock(&ipcomp_resource_mutex);
  300 + kfree(ipcd);
  301 +}
  302 +EXPORT_SYMBOL_GPL(ipcomp_destroy);
  303 +
  304 +int ipcomp_init_state(struct xfrm_state *x)
  305 +{
  306 + int err;
  307 + struct ipcomp_data *ipcd;
  308 + struct xfrm_algo_desc *calg_desc;
  309 +
  310 + err = -EINVAL;
  311 + if (!x->calg)
  312 + goto out;
  313 +
  314 + if (x->encap)
  315 + goto out;
  316 +
  317 + err = -ENOMEM;
  318 + ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL);
  319 + if (!ipcd)
  320 + goto out;
  321 +
  322 + mutex_lock(&ipcomp_resource_mutex);
  323 + if (!ipcomp_alloc_scratches())
  324 + goto error;
  325 +
  326 + ipcd->tfms = ipcomp_alloc_tfms(x->calg->alg_name);
  327 + if (!ipcd->tfms)
  328 + goto error;
  329 + mutex_unlock(&ipcomp_resource_mutex);
  330 +
  331 + calg_desc = xfrm_calg_get_byname(x->calg->alg_name, 0);
  332 + BUG_ON(!calg_desc);
  333 + ipcd->threshold = calg_desc->uinfo.comp.threshold;
  334 + x->data = ipcd;
  335 + err = 0;
  336 +out:
  337 + return err;
  338 +
  339 +error:
  340 + ipcomp_free_data(ipcd);
  341 + mutex_unlock(&ipcomp_resource_mutex);
  342 + kfree(ipcd);
  343 + goto out;
  344 +}
  345 +EXPORT_SYMBOL_GPL(ipcomp_init_state);
  346 +
  347 +MODULE_LICENSE("GPL");
  348 +MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3173");
  349 +MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");