Commit 6fccab671f2f0a24b799f29a4ec878f62d34656c
Committed by
David S. Miller
1 parent
cffe1c5d7a
Exists in
master
and in
39 other branches
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 | { |
net/ipv4/Kconfig
... | ... | @@ -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. |
net/ipv4/ipcomp.c
... | ... | @@ -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); |
net/ipv6/Kconfig
... | ... | @@ -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. |
net/ipv6/ipcomp6.c
... | ... | @@ -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 |
net/xfrm/Kconfig
net/xfrm/Makefile
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>"); |