18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ppp_deflate.c - interface the zlib procedures for Deflate compression 48c2ecf20Sopenharmony_ci * and decompression (as used by gzip) to the PPP code. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 1994-1998 Paul Mackerras. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/ppp_defs.h> 168c2ecf20Sopenharmony_ci#include <linux/ppp-comp.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/zlib.h> 198c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * State for a Deflate (de)compressor. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistruct ppp_deflate_state { 258c2ecf20Sopenharmony_ci int seqno; 268c2ecf20Sopenharmony_ci int w_size; 278c2ecf20Sopenharmony_ci int unit; 288c2ecf20Sopenharmony_ci int mru; 298c2ecf20Sopenharmony_ci int debug; 308c2ecf20Sopenharmony_ci z_stream strm; 318c2ecf20Sopenharmony_ci struct compstat stats; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define DEFLATE_OVHD 2 /* Deflate overhead/packet */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void *z_comp_alloc(unsigned char *options, int opt_len); 378c2ecf20Sopenharmony_cistatic void *z_decomp_alloc(unsigned char *options, int opt_len); 388c2ecf20Sopenharmony_cistatic void z_comp_free(void *state); 398c2ecf20Sopenharmony_cistatic void z_decomp_free(void *state); 408c2ecf20Sopenharmony_cistatic int z_comp_init(void *state, unsigned char *options, 418c2ecf20Sopenharmony_ci int opt_len, 428c2ecf20Sopenharmony_ci int unit, int hdrlen, int debug); 438c2ecf20Sopenharmony_cistatic int z_decomp_init(void *state, unsigned char *options, 448c2ecf20Sopenharmony_ci int opt_len, 458c2ecf20Sopenharmony_ci int unit, int hdrlen, int mru, int debug); 468c2ecf20Sopenharmony_cistatic int z_compress(void *state, unsigned char *rptr, 478c2ecf20Sopenharmony_ci unsigned char *obuf, 488c2ecf20Sopenharmony_ci int isize, int osize); 498c2ecf20Sopenharmony_cistatic void z_incomp(void *state, unsigned char *ibuf, int icnt); 508c2ecf20Sopenharmony_cistatic int z_decompress(void *state, unsigned char *ibuf, 518c2ecf20Sopenharmony_ci int isize, unsigned char *obuf, int osize); 528c2ecf20Sopenharmony_cistatic void z_comp_reset(void *state); 538c2ecf20Sopenharmony_cistatic void z_decomp_reset(void *state); 548c2ecf20Sopenharmony_cistatic void z_comp_stats(void *state, struct compstat *stats); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * z_comp_free - free the memory used by a compressor 588c2ecf20Sopenharmony_ci * @arg: pointer to the private state for the compressor. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic void z_comp_free(void *arg) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (state) { 658c2ecf20Sopenharmony_ci zlib_deflateEnd(&state->strm); 668c2ecf20Sopenharmony_ci vfree(state->strm.workspace); 678c2ecf20Sopenharmony_ci kfree(state); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/** 728c2ecf20Sopenharmony_ci * z_comp_alloc - allocate space for a compressor. 738c2ecf20Sopenharmony_ci * @options: pointer to CCP option data 748c2ecf20Sopenharmony_ci * @opt_len: length of the CCP option at @options. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * The @options pointer points to the a buffer containing the 778c2ecf20Sopenharmony_ci * CCP option data for the compression being negotiated. It is 788c2ecf20Sopenharmony_ci * formatted according to RFC1979, and describes the window 798c2ecf20Sopenharmony_ci * size that the peer is requesting that we use in compressing 808c2ecf20Sopenharmony_ci * data to be sent to it. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Returns the pointer to the private state for the compressor, 838c2ecf20Sopenharmony_ci * or NULL if we could not allocate enough memory. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic void *z_comp_alloc(unsigned char *options, int opt_len) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct ppp_deflate_state *state; 888c2ecf20Sopenharmony_ci int w_size; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (opt_len != CILEN_DEFLATE || 918c2ecf20Sopenharmony_ci (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || 928c2ecf20Sopenharmony_ci options[1] != CILEN_DEFLATE || 938c2ecf20Sopenharmony_ci DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || 948c2ecf20Sopenharmony_ci options[3] != DEFLATE_CHK_SEQUENCE) 958c2ecf20Sopenharmony_ci return NULL; 968c2ecf20Sopenharmony_ci w_size = DEFLATE_SIZE(options[2]); 978c2ecf20Sopenharmony_ci if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) 988c2ecf20Sopenharmony_ci return NULL; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), 1018c2ecf20Sopenharmony_ci GFP_KERNEL); 1028c2ecf20Sopenharmony_ci if (state == NULL) 1038c2ecf20Sopenharmony_ci return NULL; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci state->strm.next_in = NULL; 1068c2ecf20Sopenharmony_ci state->w_size = w_size; 1078c2ecf20Sopenharmony_ci state->strm.workspace = vmalloc(zlib_deflate_workspacesize(-w_size, 8)); 1088c2ecf20Sopenharmony_ci if (state->strm.workspace == NULL) 1098c2ecf20Sopenharmony_ci goto out_free; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (zlib_deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION, 1128c2ecf20Sopenharmony_ci DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY) 1138c2ecf20Sopenharmony_ci != Z_OK) 1148c2ecf20Sopenharmony_ci goto out_free; 1158c2ecf20Sopenharmony_ci return (void *) state; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ciout_free: 1188c2ecf20Sopenharmony_ci z_comp_free(state); 1198c2ecf20Sopenharmony_ci return NULL; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/** 1238c2ecf20Sopenharmony_ci * z_comp_init - initialize a previously-allocated compressor. 1248c2ecf20Sopenharmony_ci * @arg: pointer to the private state for the compressor 1258c2ecf20Sopenharmony_ci * @options: pointer to the CCP option data describing the 1268c2ecf20Sopenharmony_ci * compression that was negotiated with the peer 1278c2ecf20Sopenharmony_ci * @opt_len: length of the CCP option data at @options 1288c2ecf20Sopenharmony_ci * @unit: PPP unit number for diagnostic messages 1298c2ecf20Sopenharmony_ci * @hdrlen: ignored (present for backwards compatibility) 1308c2ecf20Sopenharmony_ci * @debug: debug flag; if non-zero, debug messages are printed. 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * The CCP options described by @options must match the options 1338c2ecf20Sopenharmony_ci * specified when the compressor was allocated. The compressor 1348c2ecf20Sopenharmony_ci * history is reset. Returns 0 for failure (CCP options don't 1358c2ecf20Sopenharmony_ci * match) or 1 for success. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_cistatic int z_comp_init(void *arg, unsigned char *options, int opt_len, 1388c2ecf20Sopenharmony_ci int unit, int hdrlen, int debug) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (opt_len < CILEN_DEFLATE || 1438c2ecf20Sopenharmony_ci (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || 1448c2ecf20Sopenharmony_ci options[1] != CILEN_DEFLATE || 1458c2ecf20Sopenharmony_ci DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || 1468c2ecf20Sopenharmony_ci DEFLATE_SIZE(options[2]) != state->w_size || 1478c2ecf20Sopenharmony_ci options[3] != DEFLATE_CHK_SEQUENCE) 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci state->seqno = 0; 1518c2ecf20Sopenharmony_ci state->unit = unit; 1528c2ecf20Sopenharmony_ci state->debug = debug; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci zlib_deflateReset(&state->strm); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 1; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * z_comp_reset - reset a previously-allocated compressor. 1618c2ecf20Sopenharmony_ci * @arg: pointer to private state for the compressor. 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * This clears the history for the compressor and makes it 1648c2ecf20Sopenharmony_ci * ready to start emitting a new compressed stream. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistatic void z_comp_reset(void *arg) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci state->seqno = 0; 1718c2ecf20Sopenharmony_ci zlib_deflateReset(&state->strm); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/** 1758c2ecf20Sopenharmony_ci * z_compress - compress a PPP packet with Deflate compression. 1768c2ecf20Sopenharmony_ci * @arg: pointer to private state for the compressor 1778c2ecf20Sopenharmony_ci * @rptr: uncompressed packet (input) 1788c2ecf20Sopenharmony_ci * @obuf: compressed packet (output) 1798c2ecf20Sopenharmony_ci * @isize: size of uncompressed packet 1808c2ecf20Sopenharmony_ci * @osize: space available at @obuf 1818c2ecf20Sopenharmony_ci * 1828c2ecf20Sopenharmony_ci * Returns the length of the compressed packet, or 0 if the 1838c2ecf20Sopenharmony_ci * packet is incompressible. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic int z_compress(void *arg, unsigned char *rptr, unsigned char *obuf, 1868c2ecf20Sopenharmony_ci int isize, int osize) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 1898c2ecf20Sopenharmony_ci int r, proto, off, olen, oavail; 1908c2ecf20Sopenharmony_ci unsigned char *wptr; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * Check that the protocol is in the range we handle. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci proto = PPP_PROTOCOL(rptr); 1968c2ecf20Sopenharmony_ci if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Don't generate compressed packets which are larger than 2008c2ecf20Sopenharmony_ci the uncompressed packet. */ 2018c2ecf20Sopenharmony_ci if (osize > isize) 2028c2ecf20Sopenharmony_ci osize = isize; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci wptr = obuf; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * Copy over the PPP header and store the 2-byte sequence number. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci wptr[0] = PPP_ADDRESS(rptr); 2108c2ecf20Sopenharmony_ci wptr[1] = PPP_CONTROL(rptr); 2118c2ecf20Sopenharmony_ci put_unaligned_be16(PPP_COMP, wptr + 2); 2128c2ecf20Sopenharmony_ci wptr += PPP_HDRLEN; 2138c2ecf20Sopenharmony_ci put_unaligned_be16(state->seqno, wptr); 2148c2ecf20Sopenharmony_ci wptr += DEFLATE_OVHD; 2158c2ecf20Sopenharmony_ci olen = PPP_HDRLEN + DEFLATE_OVHD; 2168c2ecf20Sopenharmony_ci state->strm.next_out = wptr; 2178c2ecf20Sopenharmony_ci state->strm.avail_out = oavail = osize - olen; 2188c2ecf20Sopenharmony_ci ++state->seqno; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci off = (proto > 0xff) ? 2 : 3; /* skip 1st proto byte if 0 */ 2218c2ecf20Sopenharmony_ci rptr += off; 2228c2ecf20Sopenharmony_ci state->strm.next_in = rptr; 2238c2ecf20Sopenharmony_ci state->strm.avail_in = (isize - off); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci for (;;) { 2268c2ecf20Sopenharmony_ci r = zlib_deflate(&state->strm, Z_PACKET_FLUSH); 2278c2ecf20Sopenharmony_ci if (r != Z_OK) { 2288c2ecf20Sopenharmony_ci if (state->debug) 2298c2ecf20Sopenharmony_ci printk(KERN_ERR 2308c2ecf20Sopenharmony_ci "z_compress: deflate returned %d\n", r); 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci if (state->strm.avail_out == 0) { 2348c2ecf20Sopenharmony_ci olen += oavail; 2358c2ecf20Sopenharmony_ci state->strm.next_out = NULL; 2368c2ecf20Sopenharmony_ci state->strm.avail_out = oavail = 1000000; 2378c2ecf20Sopenharmony_ci } else { 2388c2ecf20Sopenharmony_ci break; /* all done */ 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci olen += oavail - state->strm.avail_out; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * See if we managed to reduce the size of the packet. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci if (olen < isize && olen <= osize) { 2478c2ecf20Sopenharmony_ci state->stats.comp_bytes += olen; 2488c2ecf20Sopenharmony_ci state->stats.comp_packets++; 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci state->stats.inc_bytes += isize; 2518c2ecf20Sopenharmony_ci state->stats.inc_packets++; 2528c2ecf20Sopenharmony_ci olen = 0; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci state->stats.unc_bytes += isize; 2558c2ecf20Sopenharmony_ci state->stats.unc_packets++; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return olen; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/** 2618c2ecf20Sopenharmony_ci * z_comp_stats - return compression statistics for a compressor 2628c2ecf20Sopenharmony_ci * or decompressor. 2638c2ecf20Sopenharmony_ci * @arg: pointer to private space for the (de)compressor 2648c2ecf20Sopenharmony_ci * @stats: pointer to a struct compstat to receive the result. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cistatic void z_comp_stats(void *arg, struct compstat *stats) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci *stats = state->stats; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/** 2748c2ecf20Sopenharmony_ci * z_decomp_free - Free the memory used by a decompressor. 2758c2ecf20Sopenharmony_ci * @arg: pointer to private space for the decompressor. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cistatic void z_decomp_free(void *arg) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (state) { 2828c2ecf20Sopenharmony_ci zlib_inflateEnd(&state->strm); 2838c2ecf20Sopenharmony_ci vfree(state->strm.workspace); 2848c2ecf20Sopenharmony_ci kfree(state); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * z_decomp_alloc - allocate space for a decompressor. 2908c2ecf20Sopenharmony_ci * @options: pointer to CCP option data 2918c2ecf20Sopenharmony_ci * @opt_len: length of the CCP option at @options. 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * The @options pointer points to the a buffer containing the 2948c2ecf20Sopenharmony_ci * CCP option data for the compression being negotiated. It is 2958c2ecf20Sopenharmony_ci * formatted according to RFC1979, and describes the window 2968c2ecf20Sopenharmony_ci * size that we are requesting the peer to use in compressing 2978c2ecf20Sopenharmony_ci * data to be sent to us. 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * Returns the pointer to the private state for the decompressor, 3008c2ecf20Sopenharmony_ci * or NULL if we could not allocate enough memory. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic void *z_decomp_alloc(unsigned char *options, int opt_len) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct ppp_deflate_state *state; 3058c2ecf20Sopenharmony_ci int w_size; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (opt_len != CILEN_DEFLATE || 3088c2ecf20Sopenharmony_ci (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || 3098c2ecf20Sopenharmony_ci options[1] != CILEN_DEFLATE || 3108c2ecf20Sopenharmony_ci DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || 3118c2ecf20Sopenharmony_ci options[3] != DEFLATE_CHK_SEQUENCE) 3128c2ecf20Sopenharmony_ci return NULL; 3138c2ecf20Sopenharmony_ci w_size = DEFLATE_SIZE(options[2]); 3148c2ecf20Sopenharmony_ci if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) 3158c2ecf20Sopenharmony_ci return NULL; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 3188c2ecf20Sopenharmony_ci if (state == NULL) 3198c2ecf20Sopenharmony_ci return NULL; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci state->w_size = w_size; 3228c2ecf20Sopenharmony_ci state->strm.next_out = NULL; 3238c2ecf20Sopenharmony_ci state->strm.workspace = vmalloc(zlib_inflate_workspacesize()); 3248c2ecf20Sopenharmony_ci if (state->strm.workspace == NULL) 3258c2ecf20Sopenharmony_ci goto out_free; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (zlib_inflateInit2(&state->strm, -w_size) != Z_OK) 3288c2ecf20Sopenharmony_ci goto out_free; 3298c2ecf20Sopenharmony_ci return (void *) state; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ciout_free: 3328c2ecf20Sopenharmony_ci z_decomp_free(state); 3338c2ecf20Sopenharmony_ci return NULL; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/** 3378c2ecf20Sopenharmony_ci * z_decomp_init - initialize a previously-allocated decompressor. 3388c2ecf20Sopenharmony_ci * @arg: pointer to the private state for the decompressor 3398c2ecf20Sopenharmony_ci * @options: pointer to the CCP option data describing the 3408c2ecf20Sopenharmony_ci * compression that was negotiated with the peer 3418c2ecf20Sopenharmony_ci * @opt_len: length of the CCP option data at @options 3428c2ecf20Sopenharmony_ci * @unit: PPP unit number for diagnostic messages 3438c2ecf20Sopenharmony_ci * @hdrlen: ignored (present for backwards compatibility) 3448c2ecf20Sopenharmony_ci * @mru: maximum length of decompressed packets 3458c2ecf20Sopenharmony_ci * @debug: debug flag; if non-zero, debug messages are printed. 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * The CCP options described by @options must match the options 3488c2ecf20Sopenharmony_ci * specified when the decompressor was allocated. The decompressor 3498c2ecf20Sopenharmony_ci * history is reset. Returns 0 for failure (CCP options don't 3508c2ecf20Sopenharmony_ci * match) or 1 for success. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_cistatic int z_decomp_init(void *arg, unsigned char *options, int opt_len, 3538c2ecf20Sopenharmony_ci int unit, int hdrlen, int mru, int debug) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (opt_len < CILEN_DEFLATE || 3588c2ecf20Sopenharmony_ci (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) || 3598c2ecf20Sopenharmony_ci options[1] != CILEN_DEFLATE || 3608c2ecf20Sopenharmony_ci DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL || 3618c2ecf20Sopenharmony_ci DEFLATE_SIZE(options[2]) != state->w_size || 3628c2ecf20Sopenharmony_ci options[3] != DEFLATE_CHK_SEQUENCE) 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci state->seqno = 0; 3668c2ecf20Sopenharmony_ci state->unit = unit; 3678c2ecf20Sopenharmony_ci state->debug = debug; 3688c2ecf20Sopenharmony_ci state->mru = mru; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci zlib_inflateReset(&state->strm); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 1; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/** 3768c2ecf20Sopenharmony_ci * z_decomp_reset - reset a previously-allocated decompressor. 3778c2ecf20Sopenharmony_ci * @arg: pointer to private state for the decompressor. 3788c2ecf20Sopenharmony_ci * 3798c2ecf20Sopenharmony_ci * This clears the history for the decompressor and makes it 3808c2ecf20Sopenharmony_ci * ready to receive a new compressed stream. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_cistatic void z_decomp_reset(void *arg) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci state->seqno = 0; 3878c2ecf20Sopenharmony_ci zlib_inflateReset(&state->strm); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/** 3918c2ecf20Sopenharmony_ci * z_decompress - decompress a Deflate-compressed packet. 3928c2ecf20Sopenharmony_ci * @arg: pointer to private state for the decompressor 3938c2ecf20Sopenharmony_ci * @ibuf: pointer to input (compressed) packet data 3948c2ecf20Sopenharmony_ci * @isize: length of input packet 3958c2ecf20Sopenharmony_ci * @obuf: pointer to space for output (decompressed) packet 3968c2ecf20Sopenharmony_ci * @osize: amount of space available at @obuf 3978c2ecf20Sopenharmony_ci * 3988c2ecf20Sopenharmony_ci * Because of patent problems, we return DECOMP_ERROR for errors 3998c2ecf20Sopenharmony_ci * found by inspecting the input data and for system problems, but 4008c2ecf20Sopenharmony_ci * DECOMP_FATALERROR for any errors which could possibly be said to 4018c2ecf20Sopenharmony_ci * be being detected "after" decompression. For DECOMP_ERROR, 4028c2ecf20Sopenharmony_ci * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be 4038c2ecf20Sopenharmony_ci * infringing a patent of Motorola's if we do, so we take CCP down 4048c2ecf20Sopenharmony_ci * instead. 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * Given that the frame has the correct sequence number and a good FCS, 4078c2ecf20Sopenharmony_ci * errors such as invalid codes in the input most likely indicate a 4088c2ecf20Sopenharmony_ci * bug, so we return DECOMP_FATALERROR for them in order to turn off 4098c2ecf20Sopenharmony_ci * compression, even though they are detected by inspecting the input. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_cistatic int z_decompress(void *arg, unsigned char *ibuf, int isize, 4128c2ecf20Sopenharmony_ci unsigned char *obuf, int osize) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 4158c2ecf20Sopenharmony_ci int olen, seq, r; 4168c2ecf20Sopenharmony_ci int decode_proto, overflow; 4178c2ecf20Sopenharmony_ci unsigned char overflow_buf[1]; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (isize <= PPP_HDRLEN + DEFLATE_OVHD) { 4208c2ecf20Sopenharmony_ci if (state->debug) 4218c2ecf20Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: short pkt (%d)\n", 4228c2ecf20Sopenharmony_ci state->unit, isize); 4238c2ecf20Sopenharmony_ci return DECOMP_ERROR; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Check the sequence number. */ 4278c2ecf20Sopenharmony_ci seq = get_unaligned_be16(ibuf + PPP_HDRLEN); 4288c2ecf20Sopenharmony_ci if (seq != (state->seqno & 0xffff)) { 4298c2ecf20Sopenharmony_ci if (state->debug) 4308c2ecf20Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n", 4318c2ecf20Sopenharmony_ci state->unit, seq, state->seqno & 0xffff); 4328c2ecf20Sopenharmony_ci return DECOMP_ERROR; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci ++state->seqno; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * Fill in the first part of the PPP header. The protocol field 4388c2ecf20Sopenharmony_ci * comes from the decompressed data. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci obuf[0] = PPP_ADDRESS(ibuf); 4418c2ecf20Sopenharmony_ci obuf[1] = PPP_CONTROL(ibuf); 4428c2ecf20Sopenharmony_ci obuf[2] = 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * Set up to call inflate. We set avail_out to 1 initially so we can 4468c2ecf20Sopenharmony_ci * look at the first byte of the output and decide whether we have 4478c2ecf20Sopenharmony_ci * a 1-byte or 2-byte protocol field. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci state->strm.next_in = ibuf + PPP_HDRLEN + DEFLATE_OVHD; 4508c2ecf20Sopenharmony_ci state->strm.avail_in = isize - (PPP_HDRLEN + DEFLATE_OVHD); 4518c2ecf20Sopenharmony_ci state->strm.next_out = obuf + 3; 4528c2ecf20Sopenharmony_ci state->strm.avail_out = 1; 4538c2ecf20Sopenharmony_ci decode_proto = 1; 4548c2ecf20Sopenharmony_ci overflow = 0; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * Call inflate, supplying more input or output as needed. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ci for (;;) { 4608c2ecf20Sopenharmony_ci r = zlib_inflate(&state->strm, Z_PACKET_FLUSH); 4618c2ecf20Sopenharmony_ci if (r != Z_OK) { 4628c2ecf20Sopenharmony_ci if (state->debug) 4638c2ecf20Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: inflate returned %d (%s)\n", 4648c2ecf20Sopenharmony_ci state->unit, r, (state->strm.msg? state->strm.msg: "")); 4658c2ecf20Sopenharmony_ci return DECOMP_FATALERROR; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci if (state->strm.avail_out != 0) 4688c2ecf20Sopenharmony_ci break; /* all done */ 4698c2ecf20Sopenharmony_ci if (decode_proto) { 4708c2ecf20Sopenharmony_ci state->strm.avail_out = osize - PPP_HDRLEN; 4718c2ecf20Sopenharmony_ci if ((obuf[3] & 1) == 0) { 4728c2ecf20Sopenharmony_ci /* 2-byte protocol field */ 4738c2ecf20Sopenharmony_ci obuf[2] = obuf[3]; 4748c2ecf20Sopenharmony_ci --state->strm.next_out; 4758c2ecf20Sopenharmony_ci ++state->strm.avail_out; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci decode_proto = 0; 4788c2ecf20Sopenharmony_ci } else if (!overflow) { 4798c2ecf20Sopenharmony_ci /* 4808c2ecf20Sopenharmony_ci * We've filled up the output buffer; the only way to 4818c2ecf20Sopenharmony_ci * find out whether inflate has any more characters 4828c2ecf20Sopenharmony_ci * left is to give it another byte of output space. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci state->strm.next_out = overflow_buf; 4858c2ecf20Sopenharmony_ci state->strm.avail_out = 1; 4868c2ecf20Sopenharmony_ci overflow = 1; 4878c2ecf20Sopenharmony_ci } else { 4888c2ecf20Sopenharmony_ci if (state->debug) 4898c2ecf20Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: ran out of mru\n", 4908c2ecf20Sopenharmony_ci state->unit); 4918c2ecf20Sopenharmony_ci return DECOMP_FATALERROR; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (decode_proto) { 4968c2ecf20Sopenharmony_ci if (state->debug) 4978c2ecf20Sopenharmony_ci printk(KERN_DEBUG "z_decompress%d: didn't get proto\n", 4988c2ecf20Sopenharmony_ci state->unit); 4998c2ecf20Sopenharmony_ci return DECOMP_ERROR; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci olen = osize + overflow - state->strm.avail_out; 5038c2ecf20Sopenharmony_ci state->stats.unc_bytes += olen; 5048c2ecf20Sopenharmony_ci state->stats.unc_packets++; 5058c2ecf20Sopenharmony_ci state->stats.comp_bytes += isize; 5068c2ecf20Sopenharmony_ci state->stats.comp_packets++; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return olen; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/** 5128c2ecf20Sopenharmony_ci * z_incomp - add incompressible input data to the history. 5138c2ecf20Sopenharmony_ci * @arg: pointer to private state for the decompressor 5148c2ecf20Sopenharmony_ci * @ibuf: pointer to input packet data 5158c2ecf20Sopenharmony_ci * @icnt: length of input data. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_cistatic void z_incomp(void *arg, unsigned char *ibuf, int icnt) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg; 5208c2ecf20Sopenharmony_ci int proto, r; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * Check that the protocol is one we handle. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci proto = PPP_PROTOCOL(ibuf); 5268c2ecf20Sopenharmony_ci if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) 5278c2ecf20Sopenharmony_ci return; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci ++state->seqno; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* 5328c2ecf20Sopenharmony_ci * We start at the either the 1st or 2nd byte of the protocol field, 5338c2ecf20Sopenharmony_ci * depending on whether the protocol value is compressible. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci state->strm.next_in = ibuf + 3; 5368c2ecf20Sopenharmony_ci state->strm.avail_in = icnt - 3; 5378c2ecf20Sopenharmony_ci if (proto > 0xff) { 5388c2ecf20Sopenharmony_ci --state->strm.next_in; 5398c2ecf20Sopenharmony_ci ++state->strm.avail_in; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci r = zlib_inflateIncomp(&state->strm); 5438c2ecf20Sopenharmony_ci if (r != Z_OK) { 5448c2ecf20Sopenharmony_ci /* gak! */ 5458c2ecf20Sopenharmony_ci if (state->debug) { 5468c2ecf20Sopenharmony_ci printk(KERN_DEBUG "z_incomp%d: inflateIncomp returned %d (%s)\n", 5478c2ecf20Sopenharmony_ci state->unit, r, (state->strm.msg? state->strm.msg: "")); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci return; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* 5538c2ecf20Sopenharmony_ci * Update stats. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci state->stats.inc_bytes += icnt; 5568c2ecf20Sopenharmony_ci state->stats.inc_packets++; 5578c2ecf20Sopenharmony_ci state->stats.unc_bytes += icnt; 5588c2ecf20Sopenharmony_ci state->stats.unc_packets++; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/************************************************************* 5628c2ecf20Sopenharmony_ci * Module interface table 5638c2ecf20Sopenharmony_ci *************************************************************/ 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci/* These are in ppp_generic.c */ 5668c2ecf20Sopenharmony_ciextern int ppp_register_compressor (struct compressor *cp); 5678c2ecf20Sopenharmony_ciextern void ppp_unregister_compressor (struct compressor *cp); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci/* 5708c2ecf20Sopenharmony_ci * Procedures exported to if_ppp.c. 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_cistatic struct compressor ppp_deflate = { 5738c2ecf20Sopenharmony_ci .compress_proto = CI_DEFLATE, 5748c2ecf20Sopenharmony_ci .comp_alloc = z_comp_alloc, 5758c2ecf20Sopenharmony_ci .comp_free = z_comp_free, 5768c2ecf20Sopenharmony_ci .comp_init = z_comp_init, 5778c2ecf20Sopenharmony_ci .comp_reset = z_comp_reset, 5788c2ecf20Sopenharmony_ci .compress = z_compress, 5798c2ecf20Sopenharmony_ci .comp_stat = z_comp_stats, 5808c2ecf20Sopenharmony_ci .decomp_alloc = z_decomp_alloc, 5818c2ecf20Sopenharmony_ci .decomp_free = z_decomp_free, 5828c2ecf20Sopenharmony_ci .decomp_init = z_decomp_init, 5838c2ecf20Sopenharmony_ci .decomp_reset = z_decomp_reset, 5848c2ecf20Sopenharmony_ci .decompress = z_decompress, 5858c2ecf20Sopenharmony_ci .incomp = z_incomp, 5868c2ecf20Sopenharmony_ci .decomp_stat = z_comp_stats, 5878c2ecf20Sopenharmony_ci .owner = THIS_MODULE 5888c2ecf20Sopenharmony_ci}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic struct compressor ppp_deflate_draft = { 5918c2ecf20Sopenharmony_ci .compress_proto = CI_DEFLATE_DRAFT, 5928c2ecf20Sopenharmony_ci .comp_alloc = z_comp_alloc, 5938c2ecf20Sopenharmony_ci .comp_free = z_comp_free, 5948c2ecf20Sopenharmony_ci .comp_init = z_comp_init, 5958c2ecf20Sopenharmony_ci .comp_reset = z_comp_reset, 5968c2ecf20Sopenharmony_ci .compress = z_compress, 5978c2ecf20Sopenharmony_ci .comp_stat = z_comp_stats, 5988c2ecf20Sopenharmony_ci .decomp_alloc = z_decomp_alloc, 5998c2ecf20Sopenharmony_ci .decomp_free = z_decomp_free, 6008c2ecf20Sopenharmony_ci .decomp_init = z_decomp_init, 6018c2ecf20Sopenharmony_ci .decomp_reset = z_decomp_reset, 6028c2ecf20Sopenharmony_ci .decompress = z_decompress, 6038c2ecf20Sopenharmony_ci .incomp = z_incomp, 6048c2ecf20Sopenharmony_ci .decomp_stat = z_comp_stats, 6058c2ecf20Sopenharmony_ci .owner = THIS_MODULE 6068c2ecf20Sopenharmony_ci}; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int __init deflate_init(void) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci int rc; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci rc = ppp_register_compressor(&ppp_deflate); 6138c2ecf20Sopenharmony_ci if (rc) 6148c2ecf20Sopenharmony_ci return rc; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci rc = ppp_register_compressor(&ppp_deflate_draft); 6178c2ecf20Sopenharmony_ci if (rc) { 6188c2ecf20Sopenharmony_ci ppp_unregister_compressor(&ppp_deflate); 6198c2ecf20Sopenharmony_ci return rc; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci pr_info("PPP Deflate Compression module registered\n"); 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic void __exit deflate_cleanup(void) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci ppp_unregister_compressor(&ppp_deflate); 6298c2ecf20Sopenharmony_ci ppp_unregister_compressor(&ppp_deflate_draft); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cimodule_init(deflate_init); 6338c2ecf20Sopenharmony_cimodule_exit(deflate_cleanup); 6348c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 6358c2ecf20Sopenharmony_ciMODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE)); 6368c2ecf20Sopenharmony_ciMODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE_DRAFT)); 637