162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ppp_deflate.c - interface the zlib procedures for Deflate compression 462306a36Sopenharmony_ci * and decompression (as used by gzip) to the PPP code. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright 1994-1998 Paul Mackerras. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/vmalloc.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/ppp_defs.h> 1662306a36Sopenharmony_ci#include <linux/ppp-comp.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/zlib.h> 1962306a36Sopenharmony_ci#include <asm/unaligned.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * State for a Deflate (de)compressor. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistruct ppp_deflate_state { 2562306a36Sopenharmony_ci int seqno; 2662306a36Sopenharmony_ci int w_size; 2762306a36Sopenharmony_ci int unit; 2862306a36Sopenharmony_ci int mru; 2962306a36Sopenharmony_ci int debug; 3062306a36Sopenharmony_ci z_stream strm; 3162306a36Sopenharmony_ci struct compstat stats; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define DEFLATE_OVHD 2 /* Deflate overhead/packet */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic void *z_comp_alloc(unsigned char *options, int opt_len); 3762306a36Sopenharmony_cistatic void *z_decomp_alloc(unsigned char *options, int opt_len); 3862306a36Sopenharmony_cistatic void z_comp_free(void *state); 3962306a36Sopenharmony_cistatic void z_decomp_free(void *state); 4062306a36Sopenharmony_cistatic int z_comp_init(void *state, unsigned char *options, 4162306a36Sopenharmony_ci int opt_len, 4262306a36Sopenharmony_ci int unit, int hdrlen, int debug); 4362306a36Sopenharmony_cistatic int z_decomp_init(void *state, unsigned char *options, 4462306a36Sopenharmony_ci int opt_len, 4562306a36Sopenharmony_ci int unit, int hdrlen, int mru, int debug); 4662306a36Sopenharmony_cistatic int z_compress(void *state, unsigned char *rptr, 4762306a36Sopenharmony_ci unsigned char *obuf, 4862306a36Sopenharmony_ci int isize, int osize); 4962306a36Sopenharmony_cistatic void z_incomp(void *state, unsigned char *ibuf, int icnt); 5062306a36Sopenharmony_cistatic int z_decompress(void *state, unsigned char *ibuf, 5162306a36Sopenharmony_ci int isize, unsigned char *obuf, int osize); 5262306a36Sopenharmony_cistatic void z_comp_reset(void *state); 5362306a36Sopenharmony_cistatic void z_decomp_reset(void *state); 5462306a36Sopenharmony_cistatic void z_comp_stats(void *state, struct compstat *stats); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * z_comp_free - free the memory used by a compressor 5862306a36Sopenharmony_ci * @arg: pointer to the private state for the compressor. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistatic void z_comp_free(void *arg) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (state) { 6562306a36Sopenharmony_ci zlib_deflateEnd(&state->strm); 6662306a36Sopenharmony_ci vfree(state->strm.workspace); 6762306a36Sopenharmony_ci kfree(state); 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/** 7262306a36Sopenharmony_ci * z_comp_alloc - allocate space for a compressor. 7362306a36Sopenharmony_ci * @options: pointer to CCP option data 7462306a36Sopenharmony_ci * @opt_len: length of the CCP option at @options. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * The @options pointer points to the a buffer containing the 7762306a36Sopenharmony_ci * CCP option data for the compression being negotiated. It is 7862306a36Sopenharmony_ci * formatted according to RFC1979, and describes the window 7962306a36Sopenharmony_ci * size that the peer is requesting that we use in compressing 8062306a36Sopenharmony_ci * data to be sent to it. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Returns the pointer to the private state for the compressor, 8362306a36Sopenharmony_ci * or NULL if we could not allocate enough memory. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic void *z_comp_alloc(unsigned char *options, int opt_len) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct ppp_deflate_state *state; 8862306a36Sopenharmony_ci int w_size; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (opt_len != CILEN_DEFLATE || 9162306a36Sopenharmony_ci (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || 9262306a36Sopenharmony_ci options[1] != CILEN_DEFLATE || 9362306a36Sopenharmony_ci DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || 9462306a36Sopenharmony_ci options[3] != DEFLATE_CHK_SEQUENCE) 9562306a36Sopenharmony_ci return NULL; 9662306a36Sopenharmony_ci w_size = DEFLATE_SIZE(options[2]); 9762306a36Sopenharmony_ci if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) 9862306a36Sopenharmony_ci return NULL; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci state = kzalloc(sizeof(*state), 10162306a36Sopenharmony_ci GFP_KERNEL); 10262306a36Sopenharmony_ci if (state == NULL) 10362306a36Sopenharmony_ci return NULL; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci state->strm.next_in = NULL; 10662306a36Sopenharmony_ci state->w_size = w_size; 10762306a36Sopenharmony_ci state->strm.workspace = vmalloc(zlib_deflate_workspacesize(-w_size, 8)); 10862306a36Sopenharmony_ci if (state->strm.workspace == NULL) 10962306a36Sopenharmony_ci goto out_free; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (zlib_deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION, 11262306a36Sopenharmony_ci DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY) 11362306a36Sopenharmony_ci != Z_OK) 11462306a36Sopenharmony_ci goto out_free; 11562306a36Sopenharmony_ci return (void *) state; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciout_free: 11862306a36Sopenharmony_ci z_comp_free(state); 11962306a36Sopenharmony_ci return NULL; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/** 12362306a36Sopenharmony_ci * z_comp_init - initialize a previously-allocated compressor. 12462306a36Sopenharmony_ci * @arg: pointer to the private state for the compressor 12562306a36Sopenharmony_ci * @options: pointer to the CCP option data describing the 12662306a36Sopenharmony_ci * compression that was negotiated with the peer 12762306a36Sopenharmony_ci * @opt_len: length of the CCP option data at @options 12862306a36Sopenharmony_ci * @unit: PPP unit number for diagnostic messages 12962306a36Sopenharmony_ci * @hdrlen: ignored (present for backwards compatibility) 13062306a36Sopenharmony_ci * @debug: debug flag; if non-zero, debug messages are printed. 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * The CCP options described by @options must match the options 13362306a36Sopenharmony_ci * specified when the compressor was allocated. The compressor 13462306a36Sopenharmony_ci * history is reset. Returns 0 for failure (CCP options don't 13562306a36Sopenharmony_ci * match) or 1 for success. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_cistatic int z_comp_init(void *arg, unsigned char *options, int opt_len, 13862306a36Sopenharmony_ci int unit, int hdrlen, int debug) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (opt_len < CILEN_DEFLATE || 14362306a36Sopenharmony_ci (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || 14462306a36Sopenharmony_ci options[1] != CILEN_DEFLATE || 14562306a36Sopenharmony_ci DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || 14662306a36Sopenharmony_ci DEFLATE_SIZE(options[2]) != state->w_size || 14762306a36Sopenharmony_ci options[3] != DEFLATE_CHK_SEQUENCE) 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci state->seqno = 0; 15162306a36Sopenharmony_ci state->unit = unit; 15262306a36Sopenharmony_ci state->debug = debug; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci zlib_deflateReset(&state->strm); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 1; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/** 16062306a36Sopenharmony_ci * z_comp_reset - reset a previously-allocated compressor. 16162306a36Sopenharmony_ci * @arg: pointer to private state for the compressor. 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * This clears the history for the compressor and makes it 16462306a36Sopenharmony_ci * ready to start emitting a new compressed stream. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic void z_comp_reset(void *arg) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci state->seqno = 0; 17162306a36Sopenharmony_ci zlib_deflateReset(&state->strm); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * z_compress - compress a PPP packet with Deflate compression. 17662306a36Sopenharmony_ci * @arg: pointer to private state for the compressor 17762306a36Sopenharmony_ci * @rptr: uncompressed packet (input) 17862306a36Sopenharmony_ci * @obuf: compressed packet (output) 17962306a36Sopenharmony_ci * @isize: size of uncompressed packet 18062306a36Sopenharmony_ci * @osize: space available at @obuf 18162306a36Sopenharmony_ci * 18262306a36Sopenharmony_ci * Returns the length of the compressed packet, or 0 if the 18362306a36Sopenharmony_ci * packet is incompressible. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistatic int z_compress(void *arg, unsigned char *rptr, unsigned char *obuf, 18662306a36Sopenharmony_ci int isize, int osize) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 18962306a36Sopenharmony_ci int r, proto, off, olen, oavail; 19062306a36Sopenharmony_ci unsigned char *wptr; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * Check that the protocol is in the range we handle. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci proto = PPP_PROTOCOL(rptr); 19662306a36Sopenharmony_ci if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Don't generate compressed packets which are larger than 20062306a36Sopenharmony_ci the uncompressed packet. */ 20162306a36Sopenharmony_ci if (osize > isize) 20262306a36Sopenharmony_ci osize = isize; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci wptr = obuf; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Copy over the PPP header and store the 2-byte sequence number. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci wptr[0] = PPP_ADDRESS(rptr); 21062306a36Sopenharmony_ci wptr[1] = PPP_CONTROL(rptr); 21162306a36Sopenharmony_ci put_unaligned_be16(PPP_COMP, wptr + 2); 21262306a36Sopenharmony_ci wptr += PPP_HDRLEN; 21362306a36Sopenharmony_ci put_unaligned_be16(state->seqno, wptr); 21462306a36Sopenharmony_ci wptr += DEFLATE_OVHD; 21562306a36Sopenharmony_ci olen = PPP_HDRLEN + DEFLATE_OVHD; 21662306a36Sopenharmony_ci state->strm.next_out = wptr; 21762306a36Sopenharmony_ci state->strm.avail_out = oavail = osize - olen; 21862306a36Sopenharmony_ci ++state->seqno; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci off = (proto > 0xff) ? 2 : 3; /* skip 1st proto byte if 0 */ 22162306a36Sopenharmony_ci rptr += off; 22262306a36Sopenharmony_ci state->strm.next_in = rptr; 22362306a36Sopenharmony_ci state->strm.avail_in = (isize - off); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci for (;;) { 22662306a36Sopenharmony_ci r = zlib_deflate(&state->strm, Z_PACKET_FLUSH); 22762306a36Sopenharmony_ci if (r != Z_OK) { 22862306a36Sopenharmony_ci if (state->debug) 22962306a36Sopenharmony_ci printk(KERN_ERR 23062306a36Sopenharmony_ci "z_compress: deflate returned %d\n", r); 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci if (state->strm.avail_out == 0) { 23462306a36Sopenharmony_ci olen += oavail; 23562306a36Sopenharmony_ci state->strm.next_out = NULL; 23662306a36Sopenharmony_ci state->strm.avail_out = oavail = 1000000; 23762306a36Sopenharmony_ci } else { 23862306a36Sopenharmony_ci break; /* all done */ 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci olen += oavail - state->strm.avail_out; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * See if we managed to reduce the size of the packet. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ci if (olen < isize && olen <= osize) { 24762306a36Sopenharmony_ci state->stats.comp_bytes += olen; 24862306a36Sopenharmony_ci state->stats.comp_packets++; 24962306a36Sopenharmony_ci } else { 25062306a36Sopenharmony_ci state->stats.inc_bytes += isize; 25162306a36Sopenharmony_ci state->stats.inc_packets++; 25262306a36Sopenharmony_ci olen = 0; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci state->stats.unc_bytes += isize; 25562306a36Sopenharmony_ci state->stats.unc_packets++; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return olen; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/** 26162306a36Sopenharmony_ci * z_comp_stats - return compression statistics for a compressor 26262306a36Sopenharmony_ci * or decompressor. 26362306a36Sopenharmony_ci * @arg: pointer to private space for the (de)compressor 26462306a36Sopenharmony_ci * @stats: pointer to a struct compstat to receive the result. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_cistatic void z_comp_stats(void *arg, struct compstat *stats) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci *stats = state->stats; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/** 27462306a36Sopenharmony_ci * z_decomp_free - Free the memory used by a decompressor. 27562306a36Sopenharmony_ci * @arg: pointer to private space for the decompressor. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_cistatic void z_decomp_free(void *arg) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (state) { 28262306a36Sopenharmony_ci vfree(state->strm.workspace); 28362306a36Sopenharmony_ci kfree(state); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/** 28862306a36Sopenharmony_ci * z_decomp_alloc - allocate space for a decompressor. 28962306a36Sopenharmony_ci * @options: pointer to CCP option data 29062306a36Sopenharmony_ci * @opt_len: length of the CCP option at @options. 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * The @options pointer points to the a buffer containing the 29362306a36Sopenharmony_ci * CCP option data for the compression being negotiated. It is 29462306a36Sopenharmony_ci * formatted according to RFC1979, and describes the window 29562306a36Sopenharmony_ci * size that we are requesting the peer to use in compressing 29662306a36Sopenharmony_ci * data to be sent to us. 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * Returns the pointer to the private state for the decompressor, 29962306a36Sopenharmony_ci * or NULL if we could not allocate enough memory. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_cistatic void *z_decomp_alloc(unsigned char *options, int opt_len) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct ppp_deflate_state *state; 30462306a36Sopenharmony_ci int w_size; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (opt_len != CILEN_DEFLATE || 30762306a36Sopenharmony_ci (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || 30862306a36Sopenharmony_ci options[1] != CILEN_DEFLATE || 30962306a36Sopenharmony_ci DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || 31062306a36Sopenharmony_ci options[3] != DEFLATE_CHK_SEQUENCE) 31162306a36Sopenharmony_ci return NULL; 31262306a36Sopenharmony_ci w_size = DEFLATE_SIZE(options[2]); 31362306a36Sopenharmony_ci if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) 31462306a36Sopenharmony_ci return NULL; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 31762306a36Sopenharmony_ci if (state == NULL) 31862306a36Sopenharmony_ci return NULL; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci state->w_size = w_size; 32162306a36Sopenharmony_ci state->strm.next_out = NULL; 32262306a36Sopenharmony_ci state->strm.workspace = vmalloc(zlib_inflate_workspacesize()); 32362306a36Sopenharmony_ci if (state->strm.workspace == NULL) 32462306a36Sopenharmony_ci goto out_free; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (zlib_inflateInit2(&state->strm, -w_size) != Z_OK) 32762306a36Sopenharmony_ci goto out_free; 32862306a36Sopenharmony_ci return (void *) state; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciout_free: 33162306a36Sopenharmony_ci z_decomp_free(state); 33262306a36Sopenharmony_ci return NULL; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/** 33662306a36Sopenharmony_ci * z_decomp_init - initialize a previously-allocated decompressor. 33762306a36Sopenharmony_ci * @arg: pointer to the private state for the decompressor 33862306a36Sopenharmony_ci * @options: pointer to the CCP option data describing the 33962306a36Sopenharmony_ci * compression that was negotiated with the peer 34062306a36Sopenharmony_ci * @opt_len: length of the CCP option data at @options 34162306a36Sopenharmony_ci * @unit: PPP unit number for diagnostic messages 34262306a36Sopenharmony_ci * @hdrlen: ignored (present for backwards compatibility) 34362306a36Sopenharmony_ci * @mru: maximum length of decompressed packets 34462306a36Sopenharmony_ci * @debug: debug flag; if non-zero, debug messages are printed. 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * The CCP options described by @options must match the options 34762306a36Sopenharmony_ci * specified when the decompressor was allocated. The decompressor 34862306a36Sopenharmony_ci * history is reset. Returns 0 for failure (CCP options don't 34962306a36Sopenharmony_ci * match) or 1 for success. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_cistatic int z_decomp_init(void *arg, unsigned char *options, int opt_len, 35262306a36Sopenharmony_ci int unit, int hdrlen, int mru, int debug) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (opt_len < CILEN_DEFLATE || 35762306a36Sopenharmony_ci (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || 35862306a36Sopenharmony_ci options[1] != CILEN_DEFLATE || 35962306a36Sopenharmony_ci DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || 36062306a36Sopenharmony_ci DEFLATE_SIZE(options[2]) != state->w_size || 36162306a36Sopenharmony_ci options[3] != DEFLATE_CHK_SEQUENCE) 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci state->seqno = 0; 36562306a36Sopenharmony_ci state->unit = unit; 36662306a36Sopenharmony_ci state->debug = debug; 36762306a36Sopenharmony_ci state->mru = mru; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci zlib_inflateReset(&state->strm); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return 1; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/** 37562306a36Sopenharmony_ci * z_decomp_reset - reset a previously-allocated decompressor. 37662306a36Sopenharmony_ci * @arg: pointer to private state for the decompressor. 37762306a36Sopenharmony_ci * 37862306a36Sopenharmony_ci * This clears the history for the decompressor and makes it 37962306a36Sopenharmony_ci * ready to receive a new compressed stream. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistatic void z_decomp_reset(void *arg) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci state->seqno = 0; 38662306a36Sopenharmony_ci zlib_inflateReset(&state->strm); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/** 39062306a36Sopenharmony_ci * z_decompress - decompress a Deflate-compressed packet. 39162306a36Sopenharmony_ci * @arg: pointer to private state for the decompressor 39262306a36Sopenharmony_ci * @ibuf: pointer to input (compressed) packet data 39362306a36Sopenharmony_ci * @isize: length of input packet 39462306a36Sopenharmony_ci * @obuf: pointer to space for output (decompressed) packet 39562306a36Sopenharmony_ci * @osize: amount of space available at @obuf 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * Because of patent problems, we return DECOMP_ERROR for errors 39862306a36Sopenharmony_ci * found by inspecting the input data and for system problems, but 39962306a36Sopenharmony_ci * DECOMP_FATALERROR for any errors which could possibly be said to 40062306a36Sopenharmony_ci * be being detected "after" decompression. For DECOMP_ERROR, 40162306a36Sopenharmony_ci * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be 40262306a36Sopenharmony_ci * infringing a patent of Motorola's if we do, so we take CCP down 40362306a36Sopenharmony_ci * instead. 40462306a36Sopenharmony_ci * 40562306a36Sopenharmony_ci * Given that the frame has the correct sequence number and a good FCS, 40662306a36Sopenharmony_ci * errors such as invalid codes in the input most likely indicate a 40762306a36Sopenharmony_ci * bug, so we return DECOMP_FATALERROR for them in order to turn off 40862306a36Sopenharmony_ci * compression, even though they are detected by inspecting the input. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_cistatic int z_decompress(void *arg, unsigned char *ibuf, int isize, 41162306a36Sopenharmony_ci unsigned char *obuf, int osize) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 41462306a36Sopenharmony_ci int olen, seq, r; 41562306a36Sopenharmony_ci int decode_proto, overflow; 41662306a36Sopenharmony_ci unsigned char overflow_buf[1]; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (isize <= PPP_HDRLEN + DEFLATE_OVHD) { 41962306a36Sopenharmony_ci if (state->debug) 42062306a36Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: short pkt (%d)\n", 42162306a36Sopenharmony_ci state->unit, isize); 42262306a36Sopenharmony_ci return DECOMP_ERROR; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Check the sequence number. */ 42662306a36Sopenharmony_ci seq = get_unaligned_be16(ibuf + PPP_HDRLEN); 42762306a36Sopenharmony_ci if (seq != (state->seqno & 0xffff)) { 42862306a36Sopenharmony_ci if (state->debug) 42962306a36Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n", 43062306a36Sopenharmony_ci state->unit, seq, state->seqno & 0xffff); 43162306a36Sopenharmony_ci return DECOMP_ERROR; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci ++state->seqno; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* 43662306a36Sopenharmony_ci * Fill in the first part of the PPP header. The protocol field 43762306a36Sopenharmony_ci * comes from the decompressed data. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci obuf[0] = PPP_ADDRESS(ibuf); 44062306a36Sopenharmony_ci obuf[1] = PPP_CONTROL(ibuf); 44162306a36Sopenharmony_ci obuf[2] = 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * Set up to call inflate. We set avail_out to 1 initially so we can 44562306a36Sopenharmony_ci * look at the first byte of the output and decide whether we have 44662306a36Sopenharmony_ci * a 1-byte or 2-byte protocol field. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci state->strm.next_in = ibuf + PPP_HDRLEN + DEFLATE_OVHD; 44962306a36Sopenharmony_ci state->strm.avail_in = isize - (PPP_HDRLEN + DEFLATE_OVHD); 45062306a36Sopenharmony_ci state->strm.next_out = obuf + 3; 45162306a36Sopenharmony_ci state->strm.avail_out = 1; 45262306a36Sopenharmony_ci decode_proto = 1; 45362306a36Sopenharmony_ci overflow = 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * Call inflate, supplying more input or output as needed. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci for (;;) { 45962306a36Sopenharmony_ci r = zlib_inflate(&state->strm, Z_PACKET_FLUSH); 46062306a36Sopenharmony_ci if (r != Z_OK) { 46162306a36Sopenharmony_ci if (state->debug) 46262306a36Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: inflate returned %d (%s)\n", 46362306a36Sopenharmony_ci state->unit, r, (state->strm.msg? state->strm.msg: "")); 46462306a36Sopenharmony_ci return DECOMP_FATALERROR; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci if (state->strm.avail_out != 0) 46762306a36Sopenharmony_ci break; /* all done */ 46862306a36Sopenharmony_ci if (decode_proto) { 46962306a36Sopenharmony_ci state->strm.avail_out = osize - PPP_HDRLEN; 47062306a36Sopenharmony_ci if ((obuf[3] & 1) == 0) { 47162306a36Sopenharmony_ci /* 2-byte protocol field */ 47262306a36Sopenharmony_ci obuf[2] = obuf[3]; 47362306a36Sopenharmony_ci --state->strm.next_out; 47462306a36Sopenharmony_ci ++state->strm.avail_out; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci decode_proto = 0; 47762306a36Sopenharmony_ci } else if (!overflow) { 47862306a36Sopenharmony_ci /* 47962306a36Sopenharmony_ci * We've filled up the output buffer; the only way to 48062306a36Sopenharmony_ci * find out whether inflate has any more characters 48162306a36Sopenharmony_ci * left is to give it another byte of output space. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci state->strm.next_out = overflow_buf; 48462306a36Sopenharmony_ci state->strm.avail_out = 1; 48562306a36Sopenharmony_ci overflow = 1; 48662306a36Sopenharmony_ci } else { 48762306a36Sopenharmony_ci if (state->debug) 48862306a36Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: ran out of mru\n", 48962306a36Sopenharmony_ci state->unit); 49062306a36Sopenharmony_ci return DECOMP_FATALERROR; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (decode_proto) { 49562306a36Sopenharmony_ci if (state->debug) 49662306a36Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: didn't get proto\n", 49762306a36Sopenharmony_ci state->unit); 49862306a36Sopenharmony_ci return DECOMP_ERROR; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci olen = osize + overflow - state->strm.avail_out; 50262306a36Sopenharmony_ci state->stats.unc_bytes += olen; 50362306a36Sopenharmony_ci state->stats.unc_packets++; 50462306a36Sopenharmony_ci state->stats.comp_bytes += isize; 50562306a36Sopenharmony_ci state->stats.comp_packets++; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return olen; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci/** 51162306a36Sopenharmony_ci * z_incomp - add incompressible input data to the history. 51262306a36Sopenharmony_ci * @arg: pointer to private state for the decompressor 51362306a36Sopenharmony_ci * @ibuf: pointer to input packet data 51462306a36Sopenharmony_ci * @icnt: length of input data. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_cistatic void z_incomp(void *arg, unsigned char *ibuf, int icnt) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 51962306a36Sopenharmony_ci int proto, r; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * Check that the protocol is one we handle. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci proto = PPP_PROTOCOL(ibuf); 52562306a36Sopenharmony_ci if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) 52662306a36Sopenharmony_ci return; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci ++state->seqno; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * We start at the either the 1st or 2nd byte of the protocol field, 53262306a36Sopenharmony_ci * depending on whether the protocol value is compressible. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci state->strm.next_in = ibuf + 3; 53562306a36Sopenharmony_ci state->strm.avail_in = icnt - 3; 53662306a36Sopenharmony_ci if (proto > 0xff) { 53762306a36Sopenharmony_ci --state->strm.next_in; 53862306a36Sopenharmony_ci ++state->strm.avail_in; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci r = zlib_inflateIncomp(&state->strm); 54262306a36Sopenharmony_ci if (r != Z_OK) { 54362306a36Sopenharmony_ci /* gak! */ 54462306a36Sopenharmony_ci if (state->debug) { 54562306a36Sopenharmony_ci printk(KERN_DEBUG "z_incomp%d: inflateIncomp returned %d (%s)\n", 54662306a36Sopenharmony_ci state->unit, r, (state->strm.msg? state->strm.msg: "")); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci return; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* 55262306a36Sopenharmony_ci * Update stats. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci state->stats.inc_bytes += icnt; 55562306a36Sopenharmony_ci state->stats.inc_packets++; 55662306a36Sopenharmony_ci state->stats.unc_bytes += icnt; 55762306a36Sopenharmony_ci state->stats.unc_packets++; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci/************************************************************* 56162306a36Sopenharmony_ci * Module interface table 56262306a36Sopenharmony_ci *************************************************************/ 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* These are in ppp_generic.c */ 56562306a36Sopenharmony_ciextern int ppp_register_compressor (struct compressor *cp); 56662306a36Sopenharmony_ciextern void ppp_unregister_compressor (struct compressor *cp); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci/* 56962306a36Sopenharmony_ci * Procedures exported to if_ppp.c. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_cistatic struct compressor ppp_deflate = { 57262306a36Sopenharmony_ci .compress_proto = CI_DEFLATE, 57362306a36Sopenharmony_ci .comp_alloc = z_comp_alloc, 57462306a36Sopenharmony_ci .comp_free = z_comp_free, 57562306a36Sopenharmony_ci .comp_init = z_comp_init, 57662306a36Sopenharmony_ci .comp_reset = z_comp_reset, 57762306a36Sopenharmony_ci .compress = z_compress, 57862306a36Sopenharmony_ci .comp_stat = z_comp_stats, 57962306a36Sopenharmony_ci .decomp_alloc = z_decomp_alloc, 58062306a36Sopenharmony_ci .decomp_free = z_decomp_free, 58162306a36Sopenharmony_ci .decomp_init = z_decomp_init, 58262306a36Sopenharmony_ci .decomp_reset = z_decomp_reset, 58362306a36Sopenharmony_ci .decompress = z_decompress, 58462306a36Sopenharmony_ci .incomp = z_incomp, 58562306a36Sopenharmony_ci .decomp_stat = z_comp_stats, 58662306a36Sopenharmony_ci .owner = THIS_MODULE 58762306a36Sopenharmony_ci}; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic struct compressor ppp_deflate_draft = { 59062306a36Sopenharmony_ci .compress_proto = CI_DEFLATE_DRAFT, 59162306a36Sopenharmony_ci .comp_alloc = z_comp_alloc, 59262306a36Sopenharmony_ci .comp_free = z_comp_free, 59362306a36Sopenharmony_ci .comp_init = z_comp_init, 59462306a36Sopenharmony_ci .comp_reset = z_comp_reset, 59562306a36Sopenharmony_ci .compress = z_compress, 59662306a36Sopenharmony_ci .comp_stat = z_comp_stats, 59762306a36Sopenharmony_ci .decomp_alloc = z_decomp_alloc, 59862306a36Sopenharmony_ci .decomp_free = z_decomp_free, 59962306a36Sopenharmony_ci .decomp_init = z_decomp_init, 60062306a36Sopenharmony_ci .decomp_reset = z_decomp_reset, 60162306a36Sopenharmony_ci .decompress = z_decompress, 60262306a36Sopenharmony_ci .incomp = z_incomp, 60362306a36Sopenharmony_ci .decomp_stat = z_comp_stats, 60462306a36Sopenharmony_ci .owner = THIS_MODULE 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int __init deflate_init(void) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci int rc; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci rc = ppp_register_compressor(&ppp_deflate); 61262306a36Sopenharmony_ci if (rc) 61362306a36Sopenharmony_ci return rc; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci rc = ppp_register_compressor(&ppp_deflate_draft); 61662306a36Sopenharmony_ci if (rc) { 61762306a36Sopenharmony_ci ppp_unregister_compressor(&ppp_deflate); 61862306a36Sopenharmony_ci return rc; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci pr_info("PPP Deflate Compression module registered\n"); 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void __exit deflate_cleanup(void) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci ppp_unregister_compressor(&ppp_deflate); 62862306a36Sopenharmony_ci ppp_unregister_compressor(&ppp_deflate_draft); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cimodule_init(deflate_init); 63262306a36Sopenharmony_cimodule_exit(deflate_cleanup); 63362306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 63462306a36Sopenharmony_ciMODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE)); 63562306a36Sopenharmony_ciMODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE_DRAFT)); 636