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