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