📄 ipcomp.c
字号:
/* * IPCOMP zlib interface code. * Copyright (C) 2000 Svenning Soerensen <svenning@post5.tele.dk> * Copyright (C) 2000, 2001 Richard Guy Briggs <rgb@conscoop.ottawa.on.ca> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */char ipcomp_c_version[] = "RCSID $Id: ipcomp.c,v 1.37 2004/07/10 19:11:18 mcr Exp $";/* SSS */#include <linux/config.h>#include <linux/version.h>#define __NO_VERSION__#include <linux/module.h>#include <linux/kernel.h> /* printk() */#include "openswan/ipsec_param.h"#ifdef MALLOC_SLAB# include <linux/slab.h> /* kmalloc() */#else /* MALLOC_SLAB */# include <linux/malloc.h> /* kmalloc() */#endif /* MALLOC_SLAB */#include <linux/errno.h> /* error codes */#include <linux/types.h>#include <linux/netdevice.h>#include <linux/ip.h>#include <linux/skbuff.h>#include <linux/netdevice.h> /* struct device, and other headers */#include <linux/etherdevice.h> /* eth_type_trans */#include <linux/ip.h> /* struct iphdr */#include <linux/skbuff.h>#include <openswan.h>#ifdef NET_21# include <net/dst.h># include <asm/uaccess.h># include <linux/in6.h># define proto_priv cb#endif /* NET21 */#include <asm/checksum.h>#include <net/ip.h>#include "openswan/radij.h"#include "openswan/ipsec_encap.h"#include "openswan/ipsec_sa.h"#include "openswan/ipsec_xform.h"#include "openswan/ipsec_tunnel.h"#include "openswan/ipsec_rcv.h" /* sysctl_ipsec_inbound_policy_check */#include "openswan/ipcomp.h"#include "zlib/zlib.h"#include "zlib/zutil.h"#include <pfkeyv2.h> /* SADB_X_CALG_DEFLATE */#ifdef CONFIG_KLIPS_DEBUGint sysctl_ipsec_debug_ipcomp = 0;#endif /* CONFIG_KLIPS_DEBUG */staticstruct sk_buff *skb_copy_ipcomp(struct sk_buff *skb, int data_growth, int gfp_mask);staticvoidpf my_zcalloc(voidpf opaque, uInt items, uInt size){ return (voidpf) kmalloc(items*size, GFP_ATOMIC);}staticvoid my_zfree(voidpf opaque, voidpf address){ kfree(address);}struct sk_buff *skb_compress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags){ struct iphdr *iph; unsigned int iphlen, pyldsz, cpyldsz; unsigned char *buffer; z_stream zs; int zresult; KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: .\n"); if(skb == NULL) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "passed in NULL skb, returning ERROR.\n"); if(flags != NULL) { *flags |= IPCOMP_PARMERROR; } return skb; } if(ips == NULL) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "passed in NULL ipsec_sa needed for cpi, returning ERROR.\n"); if(flags) { *flags |= IPCOMP_PARMERROR; } return skb; } if (flags == NULL) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "passed in NULL flags, returning ERROR.\n"); ipsec_kfree_skb(skb); return NULL; } #ifdef NET_21 iph = skb->nh.iph;#else /* NET_21 */ iph = skb->ip_hdr;#endif /* NET_21 */ switch (iph->protocol) { case IPPROTO_COMP: case IPPROTO_AH: case IPPROTO_ESP: KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "skipping compression of packet with ip protocol %d.\n", iph->protocol); *flags |= IPCOMP_UNCOMPRESSABLE; return skb; } /* Don't compress packets already fragmented */ if (iph->frag_off & __constant_htons(IP_MF | IP_OFFSET)) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "skipping compression of fragmented packet.\n"); *flags |= IPCOMP_UNCOMPRESSABLE; return skb; } iphlen = iph->ihl << 2; pyldsz = ntohs(iph->tot_len) - iphlen; /* Don't compress less than 90 bytes (rfc 2394) */ if (pyldsz < 90) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "skipping compression of tiny packet, len=%d.\n", pyldsz); *flags |= IPCOMP_UNCOMPRESSABLE; return skb; } /* Adaptive decision */ if (ips->ips_comp_adapt_skip) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "skipping compression: ips_comp_adapt_skip=%d.\n", ips->ips_comp_adapt_skip); ips->ips_comp_adapt_skip--; *flags |= IPCOMP_UNCOMPRESSABLE; return skb; } zs.zalloc = my_zcalloc; zs.zfree = my_zfree; zs.opaque = 0; /* We want to use deflateInit2 because we don't want the adler header. */ zresult = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -11, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); if (zresult != Z_OK) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_error:skb_compress: " "deflateInit2() returned error %d (%s), " "skipping compression.\n", zresult, zs.msg ? zs.msg : zError(zresult)); *flags |= IPCOMP_COMPRESSIONERROR; return skb; } /* Max output size. Result should be max this size. * Implementation specific tweak: * If it's not at least 32 bytes and 6.25% smaller than * the original packet, it's probably not worth wasting * the receiver's CPU cycles decompressing it. * Your mileage may vary. */ cpyldsz = pyldsz - sizeof(struct ipcomphdr) - (pyldsz <= 512 ? 32 : pyldsz >> 4); buffer = kmalloc(cpyldsz, GFP_ATOMIC); if (!buffer) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_error:skb_compress: " "unable to kmalloc(%d, GFP_ATOMIC), " "skipping compression.\n", cpyldsz); *flags |= IPCOMP_COMPRESSIONERROR; deflateEnd(&zs); return skb; } #ifdef CONFIG_KLIPS_DEBUG if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { __u8 *c; int i; c = (__u8*)iph + iphlen; for(i = 0; i < pyldsz; i++, c++) { if(!(i % 16)) { printk(KERN_INFO "skb_compress: before:"); } printk("%02x ", *c); if(!((i + 1) % 16)) { printk("\n"); } } if(i % 16) { printk("\n"); } }#endif /* CONFIG_KLIPS_DEBUG */ zs.next_in = (char *) iph + iphlen; /* start of payload */ zs.avail_in = pyldsz; zs.next_out = buffer; /* start of compressed payload */ zs.avail_out = cpyldsz; /* Finish compression in one step */ zresult = deflate(&zs, Z_FINISH); /* Free all dynamically allocated buffers */ deflateEnd(&zs); if (zresult != Z_STREAM_END) { *flags |= IPCOMP_UNCOMPRESSABLE; kfree(buffer); /* Adjust adaptive counters */ if (++(ips->ips_comp_adapt_tries) == IPCOMP_ADAPT_INITIAL_TRIES) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "first %d packets didn't compress, " "skipping next %d\n", IPCOMP_ADAPT_INITIAL_TRIES, IPCOMP_ADAPT_INITIAL_SKIP); ips->ips_comp_adapt_skip = IPCOMP_ADAPT_INITIAL_SKIP; } else if (ips->ips_comp_adapt_tries == IPCOMP_ADAPT_INITIAL_TRIES + IPCOMP_ADAPT_SUBSEQ_TRIES) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "next %d packets didn't compress, " "skipping next %d\n", IPCOMP_ADAPT_SUBSEQ_TRIES, IPCOMP_ADAPT_SUBSEQ_SKIP); ips->ips_comp_adapt_skip = IPCOMP_ADAPT_SUBSEQ_SKIP; ips->ips_comp_adapt_tries = IPCOMP_ADAPT_INITIAL_TRIES; } return skb; } /* resulting compressed size */ cpyldsz -= zs.avail_out; /* Insert IPCOMP header */ ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_nh = iph->protocol; ((struct ipcomphdr*) ((char*) iph + iphlen))->ipcomp_flags = 0; /* use the bottom 16 bits of the spi for the cpi. The top 16 bits are for internal reference only. */ ((struct ipcomphdr*) (((char*)iph) + iphlen))->ipcomp_cpi = htons((__u16)(ntohl(ips->ips_said.spi) & 0x0000ffff)); KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_compress: " "spi=%08x, spi&0xffff=%04x, cpi=%04x, payload size: raw=%d, comp=%d.\n", ntohl(ips->ips_said.spi), ntohl(ips->ips_said.spi) & 0x0000ffff, ntohs(((struct ipcomphdr*)(((char*)iph)+iphlen))->ipcomp_cpi), pyldsz, cpyldsz); /* Update IP header */ iph->protocol = IPPROTO_COMP; iph->tot_len = htons(iphlen + sizeof(struct ipcomphdr) + cpyldsz);#if 1 /* XXX checksum is done by ipsec_tunnel ? */ iph->check = 0; iph->check = ip_fast_csum((char *) iph, iph->ihl);#endif /* Copy compressed payload */ memcpy((char *) iph + iphlen + sizeof(struct ipcomphdr), buffer, cpyldsz); kfree(buffer); /* Update skb length/tail by "unputting" the shrinkage */ skb_put(skb, cpyldsz + sizeof(struct ipcomphdr) - pyldsz); #ifdef CONFIG_KLIPS_DEBUG if(sysctl_ipsec_debug_ipcomp && sysctl_ipsec_debug_verbose) { __u8 *c; int i; c = (__u8*)iph + iphlen + sizeof(struct ipcomphdr); for(i = 0; i < cpyldsz; i++, c++) { if(!(i % 16)) { printk(KERN_INFO "skb_compress: result:"); } printk("%02x ", *c); if(!((i + 1) % 16)) { printk("\n"); } } if(i % 16) { printk("\n"); } }#endif /* CONFIG_KLIPS_DEBUG */ ips->ips_comp_adapt_skip = 0; ips->ips_comp_adapt_tries = 0; return skb;}struct sk_buff *skb_decompress(struct sk_buff *skb, struct ipsec_sa *ips, unsigned int *flags){ struct sk_buff *nskb = NULL; /* original ip header */ struct iphdr *oiph, *iph; unsigned int iphlen, pyldsz, cpyldsz; z_stream zs; int zresult; KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_debug:skb_decompress: .\n"); if(!skb) { KLIPS_PRINT(sysctl_ipsec_debug_ipcomp, "klips_error:skb_decompress: " "passed in NULL skb, returning ERROR.\n"); if (flags) *flags |= IPCOMP_PARMERROR; return skb; } if(!ips && sysctl_ipsec_inbound_policy_check) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -