1 // SPDX-License-Identifier: GPL-2.0-or-later << 2 /* 1 /* 3 * IP Payload Compression Protocol (IPComp) - 2 * IP Payload Compression Protocol (IPComp) - RFC3173. 4 * 3 * 5 * Copyright (c) 2003 James Morris <jmorris@in 4 * Copyright (c) 2003 James Morris <jmorris@intercode.com.au> 6 * Copyright (c) 2003-2008 Herbert Xu <herbert 5 * Copyright (c) 2003-2008 Herbert Xu <herbert@gondor.apana.org.au> 7 * 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 * 8 * Todo: 12 * Todo: 9 * - Tunable compression parameters. 13 * - Tunable compression parameters. 10 * - Compression stats. 14 * - Compression stats. 11 * - Adaptive compression. 15 * - Adaptive compression. 12 */ 16 */ 13 17 14 #include <linux/crypto.h> 18 #include <linux/crypto.h> 15 #include <linux/err.h> 19 #include <linux/err.h> 16 #include <linux/list.h> 20 #include <linux/list.h> 17 #include <linux/module.h> 21 #include <linux/module.h> 18 #include <linux/mutex.h> 22 #include <linux/mutex.h> 19 #include <linux/percpu.h> 23 #include <linux/percpu.h> 20 #include <linux/slab.h> 24 #include <linux/slab.h> 21 #include <linux/smp.h> 25 #include <linux/smp.h> 22 #include <linux/vmalloc.h> 26 #include <linux/vmalloc.h> 23 #include <net/ip.h> 27 #include <net/ip.h> 24 #include <net/ipcomp.h> 28 #include <net/ipcomp.h> 25 #include <net/xfrm.h> 29 #include <net/xfrm.h> 26 30 27 struct ipcomp_tfms { 31 struct ipcomp_tfms { 28 struct list_head list; 32 struct list_head list; 29 struct crypto_comp * __percpu *tfms; 33 struct crypto_comp * __percpu *tfms; 30 int users; 34 int users; 31 }; 35 }; 32 36 33 static DEFINE_MUTEX(ipcomp_resource_mutex); 37 static DEFINE_MUTEX(ipcomp_resource_mutex); 34 static void * __percpu *ipcomp_scratches; 38 static void * __percpu *ipcomp_scratches; 35 static int ipcomp_scratch_users; 39 static int ipcomp_scratch_users; 36 static LIST_HEAD(ipcomp_tfms_list); 40 static LIST_HEAD(ipcomp_tfms_list); 37 41 38 static int ipcomp_decompress(struct xfrm_state 42 static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb) 39 { 43 { 40 struct ipcomp_data *ipcd = x->data; 44 struct ipcomp_data *ipcd = x->data; 41 const int plen = skb->len; 45 const int plen = skb->len; 42 int dlen = IPCOMP_SCRATCH_SIZE; 46 int dlen = IPCOMP_SCRATCH_SIZE; 43 const u8 *start = skb->data; 47 const u8 *start = skb->data; 44 u8 *scratch = *this_cpu_ptr(ipcomp_scr !! 48 const int cpu = get_cpu(); 45 struct crypto_comp *tfm = *this_cpu_pt !! 49 u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu); >> 50 struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu); 46 int err = crypto_comp_decompress(tfm, 51 int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen); 47 int len; 52 int len; 48 53 49 if (err) 54 if (err) 50 return err; !! 55 goto out; 51 56 52 if (dlen < (plen + sizeof(struct ip_co !! 57 if (dlen < (plen + sizeof(struct ip_comp_hdr))) { 53 return -EINVAL; !! 58 err = -EINVAL; >> 59 goto out; >> 60 } 54 61 55 len = dlen - plen; 62 len = dlen - plen; 56 if (len > skb_tailroom(skb)) 63 if (len > skb_tailroom(skb)) 57 len = skb_tailroom(skb); 64 len = skb_tailroom(skb); 58 65 59 __skb_put(skb, len); 66 __skb_put(skb, len); 60 67 61 len += plen; 68 len += plen; 62 skb_copy_to_linear_data(skb, scratch, 69 skb_copy_to_linear_data(skb, scratch, len); 63 70 64 while ((scratch += len, dlen -= len) > 71 while ((scratch += len, dlen -= len) > 0) { 65 skb_frag_t *frag; 72 skb_frag_t *frag; 66 struct page *page; 73 struct page *page; 67 74 >> 75 err = -EMSGSIZE; 68 if (WARN_ON(skb_shinfo(skb)->n 76 if (WARN_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) 69 return -EMSGSIZE; !! 77 goto out; 70 78 71 frag = skb_shinfo(skb)->frags 79 frag = skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags; 72 page = alloc_page(GFP_ATOMIC); 80 page = alloc_page(GFP_ATOMIC); 73 81 >> 82 err = -ENOMEM; 74 if (!page) 83 if (!page) 75 return -ENOMEM; !! 84 goto out; >> 85 >> 86 __skb_frag_set_page(frag, page); 76 87 77 len = PAGE_SIZE; 88 len = PAGE_SIZE; 78 if (dlen < len) 89 if (dlen < len) 79 len = dlen; 90 len = dlen; 80 91 81 skb_frag_fill_page_desc(frag, !! 92 frag->page_offset = 0; >> 93 skb_frag_size_set(frag, len); 82 memcpy(skb_frag_address(frag), 94 memcpy(skb_frag_address(frag), scratch, len); 83 95 84 skb->truesize += len; 96 skb->truesize += len; 85 skb->data_len += len; 97 skb->data_len += len; 86 skb->len += len; 98 skb->len += len; 87 99 88 skb_shinfo(skb)->nr_frags++; 100 skb_shinfo(skb)->nr_frags++; 89 } 101 } 90 102 91 return 0; !! 103 err = 0; >> 104 >> 105 out: >> 106 put_cpu(); >> 107 return err; 92 } 108 } 93 109 94 int ipcomp_input(struct xfrm_state *x, struct 110 int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb) 95 { 111 { 96 int nexthdr; 112 int nexthdr; 97 int err = -ENOMEM; 113 int err = -ENOMEM; 98 struct ip_comp_hdr *ipch; 114 struct ip_comp_hdr *ipch; 99 115 100 if (skb_linearize_cow(skb)) 116 if (skb_linearize_cow(skb)) 101 goto out; 117 goto out; 102 118 103 skb->ip_summed = CHECKSUM_NONE; 119 skb->ip_summed = CHECKSUM_NONE; 104 120 105 /* Remove ipcomp header and decompress 121 /* Remove ipcomp header and decompress original payload */ 106 ipch = (void *)skb->data; 122 ipch = (void *)skb->data; 107 nexthdr = ipch->nexthdr; 123 nexthdr = ipch->nexthdr; 108 124 109 skb->transport_header = skb->network_h 125 skb->transport_header = skb->network_header + sizeof(*ipch); 110 __skb_pull(skb, sizeof(*ipch)); 126 __skb_pull(skb, sizeof(*ipch)); 111 err = ipcomp_decompress(x, skb); 127 err = ipcomp_decompress(x, skb); 112 if (err) 128 if (err) 113 goto out; 129 goto out; 114 130 115 err = nexthdr; 131 err = nexthdr; 116 132 117 out: 133 out: 118 return err; 134 return err; 119 } 135 } 120 EXPORT_SYMBOL_GPL(ipcomp_input); 136 EXPORT_SYMBOL_GPL(ipcomp_input); 121 137 122 static int ipcomp_compress(struct xfrm_state * 138 static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb) 123 { 139 { 124 struct ipcomp_data *ipcd = x->data; 140 struct ipcomp_data *ipcd = x->data; 125 const int plen = skb->len; 141 const int plen = skb->len; 126 int dlen = IPCOMP_SCRATCH_SIZE; 142 int dlen = IPCOMP_SCRATCH_SIZE; 127 u8 *start = skb->data; 143 u8 *start = skb->data; 128 struct crypto_comp *tfm; 144 struct crypto_comp *tfm; 129 u8 *scratch; 145 u8 *scratch; 130 int err; 146 int err; 131 147 132 local_bh_disable(); 148 local_bh_disable(); 133 scratch = *this_cpu_ptr(ipcomp_scratch 149 scratch = *this_cpu_ptr(ipcomp_scratches); 134 tfm = *this_cpu_ptr(ipcd->tfms); 150 tfm = *this_cpu_ptr(ipcd->tfms); 135 err = crypto_comp_compress(tfm, start, 151 err = crypto_comp_compress(tfm, start, plen, scratch, &dlen); 136 if (err) 152 if (err) 137 goto out; 153 goto out; 138 154 139 if ((dlen + sizeof(struct ip_comp_hdr) 155 if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) { 140 err = -EMSGSIZE; 156 err = -EMSGSIZE; 141 goto out; 157 goto out; 142 } 158 } 143 159 144 memcpy(start + sizeof(struct ip_comp_h 160 memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen); 145 local_bh_enable(); 161 local_bh_enable(); 146 162 147 pskb_trim(skb, dlen + sizeof(struct ip 163 pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr)); 148 return 0; 164 return 0; 149 165 150 out: 166 out: 151 local_bh_enable(); 167 local_bh_enable(); 152 return err; 168 return err; 153 } 169 } 154 170 155 int ipcomp_output(struct xfrm_state *x, struct 171 int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb) 156 { 172 { 157 int err; 173 int err; 158 struct ip_comp_hdr *ipch; 174 struct ip_comp_hdr *ipch; 159 struct ipcomp_data *ipcd = x->data; 175 struct ipcomp_data *ipcd = x->data; 160 176 161 if (skb->len < ipcd->threshold) { 177 if (skb->len < ipcd->threshold) { 162 /* Don't bother compressing */ 178 /* Don't bother compressing */ 163 goto out_ok; 179 goto out_ok; 164 } 180 } 165 181 166 if (skb_linearize_cow(skb)) 182 if (skb_linearize_cow(skb)) 167 goto out_ok; 183 goto out_ok; 168 184 169 err = ipcomp_compress(x, skb); 185 err = ipcomp_compress(x, skb); 170 186 171 if (err) { 187 if (err) { 172 goto out_ok; 188 goto out_ok; 173 } 189 } 174 190 175 /* Install ipcomp header, convert into 191 /* Install ipcomp header, convert into ipcomp datagram. */ 176 ipch = ip_comp_hdr(skb); 192 ipch = ip_comp_hdr(skb); 177 ipch->nexthdr = *skb_mac_header(skb); 193 ipch->nexthdr = *skb_mac_header(skb); 178 ipch->flags = 0; 194 ipch->flags = 0; 179 ipch->cpi = htons((u16 )ntohl(x->id.sp 195 ipch->cpi = htons((u16 )ntohl(x->id.spi)); 180 *skb_mac_header(skb) = IPPROTO_COMP; 196 *skb_mac_header(skb) = IPPROTO_COMP; 181 out_ok: 197 out_ok: 182 skb_push(skb, -skb_network_offset(skb) 198 skb_push(skb, -skb_network_offset(skb)); 183 return 0; 199 return 0; 184 } 200 } 185 EXPORT_SYMBOL_GPL(ipcomp_output); 201 EXPORT_SYMBOL_GPL(ipcomp_output); 186 202 187 static void ipcomp_free_scratches(void) 203 static void ipcomp_free_scratches(void) 188 { 204 { 189 int i; 205 int i; 190 void * __percpu *scratches; 206 void * __percpu *scratches; 191 207 192 if (--ipcomp_scratch_users) 208 if (--ipcomp_scratch_users) 193 return; 209 return; 194 210 195 scratches = ipcomp_scratches; 211 scratches = ipcomp_scratches; 196 if (!scratches) 212 if (!scratches) 197 return; 213 return; 198 214 199 for_each_possible_cpu(i) 215 for_each_possible_cpu(i) 200 vfree(*per_cpu_ptr(scratches, 216 vfree(*per_cpu_ptr(scratches, i)); 201 217 202 free_percpu(scratches); 218 free_percpu(scratches); 203 ipcomp_scratches = NULL; 219 ipcomp_scratches = NULL; 204 } 220 } 205 221 206 static void * __percpu *ipcomp_alloc_scratches 222 static void * __percpu *ipcomp_alloc_scratches(void) 207 { 223 { 208 void * __percpu *scratches; 224 void * __percpu *scratches; 209 int i; 225 int i; 210 226 211 if (ipcomp_scratch_users++) 227 if (ipcomp_scratch_users++) 212 return ipcomp_scratches; 228 return ipcomp_scratches; 213 229 214 scratches = alloc_percpu(void *); 230 scratches = alloc_percpu(void *); 215 if (!scratches) 231 if (!scratches) 216 return NULL; 232 return NULL; 217 233 218 ipcomp_scratches = scratches; 234 ipcomp_scratches = scratches; 219 235 220 for_each_possible_cpu(i) { 236 for_each_possible_cpu(i) { 221 void *scratch; 237 void *scratch; 222 238 223 scratch = vmalloc_node(IPCOMP_ 239 scratch = vmalloc_node(IPCOMP_SCRATCH_SIZE, cpu_to_node(i)); 224 if (!scratch) 240 if (!scratch) 225 return NULL; 241 return NULL; 226 *per_cpu_ptr(scratches, i) = s 242 *per_cpu_ptr(scratches, i) = scratch; 227 } 243 } 228 244 229 return scratches; 245 return scratches; 230 } 246 } 231 247 232 static void ipcomp_free_tfms(struct crypto_com 248 static void ipcomp_free_tfms(struct crypto_comp * __percpu *tfms) 233 { 249 { 234 struct ipcomp_tfms *pos; 250 struct ipcomp_tfms *pos; 235 int cpu; 251 int cpu; 236 252 237 list_for_each_entry(pos, &ipcomp_tfms_ 253 list_for_each_entry(pos, &ipcomp_tfms_list, list) { 238 if (pos->tfms == tfms) 254 if (pos->tfms == tfms) 239 break; 255 break; 240 } 256 } 241 257 242 WARN_ON(list_entry_is_head(pos, &ipcom !! 258 WARN_ON(!pos); 243 259 244 if (--pos->users) 260 if (--pos->users) 245 return; 261 return; 246 262 247 list_del(&pos->list); 263 list_del(&pos->list); 248 kfree(pos); 264 kfree(pos); 249 265 250 if (!tfms) 266 if (!tfms) 251 return; 267 return; 252 268 253 for_each_possible_cpu(cpu) { 269 for_each_possible_cpu(cpu) { 254 struct crypto_comp *tfm = *per 270 struct crypto_comp *tfm = *per_cpu_ptr(tfms, cpu); 255 crypto_free_comp(tfm); 271 crypto_free_comp(tfm); 256 } 272 } 257 free_percpu(tfms); 273 free_percpu(tfms); 258 } 274 } 259 275 260 static struct crypto_comp * __percpu *ipcomp_a 276 static struct crypto_comp * __percpu *ipcomp_alloc_tfms(const char *alg_name) 261 { 277 { 262 struct ipcomp_tfms *pos; 278 struct ipcomp_tfms *pos; 263 struct crypto_comp * __percpu *tfms; 279 struct crypto_comp * __percpu *tfms; 264 int cpu; 280 int cpu; 265 281 266 282 267 list_for_each_entry(pos, &ipcomp_tfms_ 283 list_for_each_entry(pos, &ipcomp_tfms_list, list) { 268 struct crypto_comp *tfm; 284 struct crypto_comp *tfm; 269 285 270 /* This can be any valid CPU I 286 /* This can be any valid CPU ID so we don't need locking. */ 271 tfm = this_cpu_read(*pos->tfms 287 tfm = this_cpu_read(*pos->tfms); 272 288 273 if (!strcmp(crypto_comp_name(t 289 if (!strcmp(crypto_comp_name(tfm), alg_name)) { 274 pos->users++; 290 pos->users++; 275 return pos->tfms; 291 return pos->tfms; 276 } 292 } 277 } 293 } 278 294 279 pos = kmalloc(sizeof(*pos), GFP_KERNEL 295 pos = kmalloc(sizeof(*pos), GFP_KERNEL); 280 if (!pos) 296 if (!pos) 281 return NULL; 297 return NULL; 282 298 283 pos->users = 1; 299 pos->users = 1; 284 INIT_LIST_HEAD(&pos->list); 300 INIT_LIST_HEAD(&pos->list); 285 list_add(&pos->list, &ipcomp_tfms_list 301 list_add(&pos->list, &ipcomp_tfms_list); 286 302 287 pos->tfms = tfms = alloc_percpu(struct 303 pos->tfms = tfms = alloc_percpu(struct crypto_comp *); 288 if (!tfms) 304 if (!tfms) 289 goto error; 305 goto error; 290 306 291 for_each_possible_cpu(cpu) { 307 for_each_possible_cpu(cpu) { 292 struct crypto_comp *tfm = cryp 308 struct crypto_comp *tfm = crypto_alloc_comp(alg_name, 0, 293 309 CRYPTO_ALG_ASYNC); 294 if (IS_ERR(tfm)) 310 if (IS_ERR(tfm)) 295 goto error; 311 goto error; 296 *per_cpu_ptr(tfms, cpu) = tfm; 312 *per_cpu_ptr(tfms, cpu) = tfm; 297 } 313 } 298 314 299 return tfms; 315 return tfms; 300 316 301 error: 317 error: 302 ipcomp_free_tfms(tfms); 318 ipcomp_free_tfms(tfms); 303 return NULL; 319 return NULL; 304 } 320 } 305 321 306 static void ipcomp_free_data(struct ipcomp_dat 322 static void ipcomp_free_data(struct ipcomp_data *ipcd) 307 { 323 { 308 if (ipcd->tfms) 324 if (ipcd->tfms) 309 ipcomp_free_tfms(ipcd->tfms); 325 ipcomp_free_tfms(ipcd->tfms); 310 ipcomp_free_scratches(); 326 ipcomp_free_scratches(); 311 } 327 } 312 328 313 void ipcomp_destroy(struct xfrm_state *x) 329 void ipcomp_destroy(struct xfrm_state *x) 314 { 330 { 315 struct ipcomp_data *ipcd = x->data; 331 struct ipcomp_data *ipcd = x->data; 316 if (!ipcd) 332 if (!ipcd) 317 return; 333 return; 318 xfrm_state_delete_tunnel(x); 334 xfrm_state_delete_tunnel(x); 319 mutex_lock(&ipcomp_resource_mutex); 335 mutex_lock(&ipcomp_resource_mutex); 320 ipcomp_free_data(ipcd); 336 ipcomp_free_data(ipcd); 321 mutex_unlock(&ipcomp_resource_mutex); 337 mutex_unlock(&ipcomp_resource_mutex); 322 kfree(ipcd); 338 kfree(ipcd); 323 } 339 } 324 EXPORT_SYMBOL_GPL(ipcomp_destroy); 340 EXPORT_SYMBOL_GPL(ipcomp_destroy); 325 341 326 int ipcomp_init_state(struct xfrm_state *x, st !! 342 int ipcomp_init_state(struct xfrm_state *x) 327 { 343 { 328 int err; 344 int err; 329 struct ipcomp_data *ipcd; 345 struct ipcomp_data *ipcd; 330 struct xfrm_algo_desc *calg_desc; 346 struct xfrm_algo_desc *calg_desc; 331 347 332 err = -EINVAL; 348 err = -EINVAL; 333 if (!x->calg) { !! 349 if (!x->calg) 334 NL_SET_ERR_MSG(extack, "Missin << 335 goto out; 350 goto out; 336 } << 337 351 338 if (x->encap) { !! 352 if (x->encap) 339 NL_SET_ERR_MSG(extack, "IPComp << 340 goto out; 353 goto out; 341 } << 342 354 343 err = -ENOMEM; 355 err = -ENOMEM; 344 ipcd = kzalloc(sizeof(*ipcd), GFP_KERN 356 ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL); 345 if (!ipcd) 357 if (!ipcd) 346 goto out; 358 goto out; 347 359 348 mutex_lock(&ipcomp_resource_mutex); 360 mutex_lock(&ipcomp_resource_mutex); 349 if (!ipcomp_alloc_scratches()) 361 if (!ipcomp_alloc_scratches()) 350 goto error; 362 goto error; 351 363 352 ipcd->tfms = ipcomp_alloc_tfms(x->calg 364 ipcd->tfms = ipcomp_alloc_tfms(x->calg->alg_name); 353 if (!ipcd->tfms) 365 if (!ipcd->tfms) 354 goto error; 366 goto error; 355 mutex_unlock(&ipcomp_resource_mutex); 367 mutex_unlock(&ipcomp_resource_mutex); 356 368 357 calg_desc = xfrm_calg_get_byname(x->ca 369 calg_desc = xfrm_calg_get_byname(x->calg->alg_name, 0); 358 BUG_ON(!calg_desc); 370 BUG_ON(!calg_desc); 359 ipcd->threshold = calg_desc->uinfo.com 371 ipcd->threshold = calg_desc->uinfo.comp.threshold; 360 x->data = ipcd; 372 x->data = ipcd; 361 err = 0; 373 err = 0; 362 out: 374 out: 363 return err; 375 return err; 364 376 365 error: 377 error: 366 ipcomp_free_data(ipcd); 378 ipcomp_free_data(ipcd); 367 mutex_unlock(&ipcomp_resource_mutex); 379 mutex_unlock(&ipcomp_resource_mutex); 368 kfree(ipcd); 380 kfree(ipcd); 369 goto out; 381 goto out; 370 } 382 } 371 EXPORT_SYMBOL_GPL(ipcomp_init_state); 383 EXPORT_SYMBOL_GPL(ipcomp_init_state); 372 384 373 MODULE_LICENSE("GPL"); 385 MODULE_LICENSE("GPL"); 374 MODULE_DESCRIPTION("IP Payload Compression Pro 386 MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3173"); 375 MODULE_AUTHOR("James Morris <jmorris@intercode 387 MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 376 388
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.