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/slab.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 * __percpu *tfms; 34 int users; 35 }; 36 37 static DEFINE_MUTEX(ipcomp_resource_mutex); 38 static void * __percpu *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 int len; 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 len = dlen - plen; 63 if (len > skb_tailroom(skb)) 64 len = skb_tailroom(skb); 65 66 __skb_put(skb, len); 67 68 len += plen; 69 skb_copy_to_linear_data(skb, scratch, len); 70 71 while ((scratch += len, dlen -= len) > 0) { 72 skb_frag_t *frag; 73 74 err = -EMSGSIZE; 75 if (WARN_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) 76 goto out; 77 78 frag = skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags; 79 frag->page = alloc_page(GFP_ATOMIC); 80 81 err = -ENOMEM; 82 if (!frag->page) 83 goto out; 84 85 len = PAGE_SIZE; 86 if (dlen < len) 87 len = dlen; 88 89 memcpy(page_address(frag->page), scratch, len); 90 91 frag->page_offset = 0; 92 frag->size = len; 93 skb->truesize += len; 94 skb->data_len += len; 95 skb->len += len; 96 97 skb_shinfo(skb)->nr_frags++; 98 } 99 100 err = 0; 101 102 out: 103 put_cpu(); 104 return err; 105 } 106 107 int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb) 108 { 109 int nexthdr; 110 int err = -ENOMEM; 111 struct ip_comp_hdr *ipch; 112 113 if (skb_linearize_cow(skb)) 114 goto out; 115 116 skb->ip_summed = CHECKSUM_NONE; 117 118 /* Remove ipcomp header and decompress original payload */ 119 ipch = (void *)skb->data; 120 nexthdr = ipch->nexthdr; 121 122 skb->transport_header = skb->network_header + sizeof(*ipch); 123 __skb_pull(skb, sizeof(*ipch)); 124 err = ipcomp_decompress(x, skb); 125 if (err) 126 goto out; 127 128 err = nexthdr; 129 130 out: 131 return err; 132 } 133 EXPORT_SYMBOL_GPL(ipcomp_input); 134 135 static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb) 136 { 137 struct ipcomp_data *ipcd = x->data; 138 const int plen = skb->len; 139 int dlen = IPCOMP_SCRATCH_SIZE; 140 u8 *start = skb->data; 141 const int cpu = get_cpu(); 142 u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); 143 struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu); 144 int err; 145 146 local_bh_disable(); 147 err = crypto_comp_compress(tfm, start, plen, scratch, &dlen); 148 local_bh_enable(); 149 if (err) 150 goto out; 151 152 if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) { 153 err = -EMSGSIZE; 154 goto out; 155 } 156 157 memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen); 158 put_cpu(); 159 160 pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr)); 161 return 0; 162 163 out: 164 put_cpu(); 165 return err; 166 } 167 168 int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb) 169 { 170 int err; 171 struct ip_comp_hdr *ipch; 172 struct ipcomp_data *ipcd = x->data; 173 174 if (skb->len < ipcd->threshold) { 175 /* Don't bother compressing */ 176 goto out_ok; 177 } 178 179 if (skb_linearize_cow(skb)) 180 goto out_ok; 181 182 err = ipcomp_compress(x, skb); 183 184 if (err) { 185 goto out_ok; 186 } 187 188 /* Install ipcomp header, convert into ipcomp datagram. */ 189 ipch = ip_comp_hdr(skb); 190 ipch->nexthdr = *skb_mac_header(skb); 191 ipch->flags = 0; 192 ipch->cpi = htons((u16 )ntohl(x->id.spi)); 193 *skb_mac_header(skb) = IPPROTO_COMP; 194 out_ok: 195 skb_push(skb, -skb_network_offset(skb)); 196 return 0; 197 } 198 EXPORT_SYMBOL_GPL(ipcomp_output); 199 200 static void ipcomp_free_scratches(void) 201 { 202 int i; 203 void * __percpu *scratches; 204 205 if (--ipcomp_scratch_users) 206 return; 207 208 scratches = ipcomp_scratches; 209 if (!scratches) 210 return; 211 212 for_each_possible_cpu(i) 213 vfree(*per_cpu_ptr(scratches, i)); 214 215 free_percpu(scratches); 216 } 217 218 static void * __percpu *ipcomp_alloc_scratches(void) 219 { 220 int i; 221 void * __percpu *scratches; 222 223 if (ipcomp_scratch_users++) 224 return ipcomp_scratches; 225 226 scratches = alloc_percpu(void *); 227 if (!scratches) 228 return NULL; 229 230 ipcomp_scratches = scratches; 231 232 for_each_possible_cpu(i) { 233 void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE); 234 if (!scratch) 235 return NULL; 236 *per_cpu_ptr(scratches, i) = scratch; 237 } 238 239 return scratches; 240 } 241 242 static void ipcomp_free_tfms(struct crypto_comp * __percpu *tfms) 243 { 244 struct ipcomp_tfms *pos; 245 int cpu; 246 247 list_for_each_entry(pos, &ipcomp_tfms_list, list) { 248 if (pos->tfms == tfms) 249 break; 250 } 251 252 WARN_ON(!pos); 253 254 if (--pos->users) 255 return; 256 257 list_del(&pos->list); 258 kfree(pos); 259 260 if (!tfms) 261 return; 262 263 for_each_possible_cpu(cpu) { 264 struct crypto_comp *tfm = *per_cpu_ptr(tfms, cpu); 265 crypto_free_comp(tfm); 266 } 267 free_percpu(tfms); 268 } 269 270 static struct crypto_comp * __percpu *ipcomp_alloc_tfms(const char *alg_name) 271 { 272 struct ipcomp_tfms *pos; 273 struct crypto_comp * __percpu *tfms; 274 int cpu; 275 276 /* This can be any valid CPU ID so we don't need locking. */ 277 cpu = raw_smp_processor_id(); 278 279 list_for_each_entry(pos, &ipcomp_tfms_list, list) { 280 struct crypto_comp *tfm; 281 282 tfms = pos->tfms; 283 tfm = *per_cpu_ptr(tfms, cpu); 284 285 if (!strcmp(crypto_comp_name(tfm), alg_name)) { 286 pos->users++; 287 return tfms; 288 } 289 } 290 291 pos = kmalloc(sizeof(*pos), GFP_KERNEL); 292 if (!pos) 293 return NULL; 294 295 pos->users = 1; 296 INIT_LIST_HEAD(&pos->list); 297 list_add(&pos->list, &ipcomp_tfms_list); 298 299 pos->tfms = tfms = alloc_percpu(struct crypto_comp *); 300 if (!tfms) 301 goto error; 302 303 for_each_possible_cpu(cpu) { 304 struct crypto_comp *tfm = crypto_alloc_comp(alg_name, 0, 305 CRYPTO_ALG_ASYNC); 306 if (IS_ERR(tfm)) 307 goto error; 308 *per_cpu_ptr(tfms, cpu) = tfm; 309 } 310 311 return tfms; 312 313 error: 314 ipcomp_free_tfms(tfms); 315 return NULL; 316 } 317 318 static void ipcomp_free_data(struct ipcomp_data *ipcd) 319 { 320 if (ipcd->tfms) 321 ipcomp_free_tfms(ipcd->tfms); 322 ipcomp_free_scratches(); 323 } 324 325 void ipcomp_destroy(struct xfrm_state *x) 326 { 327 struct ipcomp_data *ipcd = x->data; 328 if (!ipcd) 329 return; 330 xfrm_state_delete_tunnel(x); 331 mutex_lock(&ipcomp_resource_mutex); 332 ipcomp_free_data(ipcd); 333 mutex_unlock(&ipcomp_resource_mutex); 334 kfree(ipcd); 335 } 336 EXPORT_SYMBOL_GPL(ipcomp_destroy); 337 338 int ipcomp_init_state(struct xfrm_state *x) 339 { 340 int err; 341 struct ipcomp_data *ipcd; 342 struct xfrm_algo_desc *calg_desc; 343 344 err = -EINVAL; 345 if (!x->calg) 346 goto out; 347 348 if (x->encap) 349 goto out; 350 351 err = -ENOMEM; 352 ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL); 353 if (!ipcd) 354 goto out; 355 356 mutex_lock(&ipcomp_resource_mutex); 357 if (!ipcomp_alloc_scratches()) 358 goto error; 359 360 ipcd->tfms = ipcomp_alloc_tfms(x->calg->alg_name); 361 if (!ipcd->tfms) 362 goto error; 363 mutex_unlock(&ipcomp_resource_mutex); 364 365 calg_desc = xfrm_calg_get_byname(x->calg->alg_name, 0); 366 BUG_ON(!calg_desc); 367 ipcd->threshold = calg_desc->uinfo.comp.threshold; 368 x->data = ipcd; 369 err = 0; 370 out: 371 return err; 372 373 error: 374 ipcomp_free_data(ipcd); 375 mutex_unlock(&ipcomp_resource_mutex); 376 kfree(ipcd); 377 goto out; 378 } 379 EXPORT_SYMBOL_GPL(ipcomp_init_state); 380 381 MODULE_LICENSE("GPL"); 382 MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3173"); 383 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 384