xref: /openbsd-src/sys/net/bsd-comp.c (revision 361206c243c03bb9b4b391f76b791250fa14ed98)
1*361206c2Sjsg /*	$OpenBSD: bsd-comp.c,v 1.17 2021/03/05 09:21:08 jsg Exp $	*/
24b7ce64cSmillert /*	$NetBSD: bsd-comp.c,v 1.6 1996/10/13 02:10:58 christos Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /* Because this code is derived from the 4.3BSD compress source:
5df930be7Sderaadt  *
6df930be7Sderaadt  *
7df930be7Sderaadt  * Copyright (c) 1985, 1986 The Regents of the University of California.
8df930be7Sderaadt  * All rights reserved.
9df930be7Sderaadt  *
10df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
11df930be7Sderaadt  * James A. Woods, derived from original work by Spencer Thomas
12df930be7Sderaadt  * and Joseph Orost.
13df930be7Sderaadt  *
14df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
15df930be7Sderaadt  * modification, are permitted provided that the following conditions
16df930be7Sderaadt  * are met:
17df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
18df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
19df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
20df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
21df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
2229295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
23df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
24df930be7Sderaadt  *    without specific prior written permission.
25df930be7Sderaadt  *
26df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36df930be7Sderaadt  * SUCH DAMAGE.
37df930be7Sderaadt  */
38df930be7Sderaadt 
39df930be7Sderaadt /*
40df930be7Sderaadt  * This version is for use with mbufs on BSD-derived systems.
41df930be7Sderaadt  */
42df930be7Sderaadt 
43df930be7Sderaadt #include <sys/param.h>
44edd75aa1Sniklas #include <sys/systm.h>
45df930be7Sderaadt #include <sys/mbuf.h>
46df930be7Sderaadt #include <sys/socket.h>
47df930be7Sderaadt #include <net/if.h>
480deb6685Smpi #include <net/if_var.h>
49df930be7Sderaadt #include <net/ppp_defs.h>
50df930be7Sderaadt #include <net/if_ppp.h>
51df930be7Sderaadt 
52df930be7Sderaadt #define PACKETPTR	struct mbuf *
53df930be7Sderaadt #include <net/ppp-comp.h>
54df930be7Sderaadt 
55df930be7Sderaadt #if DO_BSD_COMPRESS
56df930be7Sderaadt /*
57df930be7Sderaadt  * PPP "BSD compress" compression
58df930be7Sderaadt  *  The differences between this compression and the classic BSD LZW
59df930be7Sderaadt  *  source are obvious from the requirement that the classic code worked
60df930be7Sderaadt  *  with files while this handles arbitrarily long streams that
61df930be7Sderaadt  *  are broken into packets.  They are:
62df930be7Sderaadt  *
63df930be7Sderaadt  *	When the code size expands, a block of junk is not emitted by
64df930be7Sderaadt  *	    the compressor and not expected by the decompressor.
65df930be7Sderaadt  *
66df930be7Sderaadt  *	New codes are not necessarily assigned every time an old
67df930be7Sderaadt  *	    code is output by the compressor.  This is because a packet
68df930be7Sderaadt  *	    end forces a code to be emitted, but does not imply that a
69df930be7Sderaadt  *	    new sequence has been seen.
70df930be7Sderaadt  *
71df930be7Sderaadt  *	The compression ratio is checked at the first end of a packet
72df930be7Sderaadt  *	    after the appropriate gap.	Besides simplifying and speeding
73df930be7Sderaadt  *	    things up, this makes it more likely that the transmitter
74df930be7Sderaadt  *	    and receiver will agree when the dictionary is cleared when
75df930be7Sderaadt  *	    compression is not going well.
76df930be7Sderaadt  */
77df930be7Sderaadt 
78df930be7Sderaadt /*
79df930be7Sderaadt  * A dictionary for doing BSD compress.
80df930be7Sderaadt  */
81df930be7Sderaadt struct bsd_db {
82df930be7Sderaadt     int	    totlen;			/* length of this structure */
83df930be7Sderaadt     u_int   hsize;			/* size of the hash table */
84df930be7Sderaadt     u_char  hshift;			/* used in hash function */
85df930be7Sderaadt     u_char  n_bits;			/* current bits/code */
86df930be7Sderaadt     u_char  maxbits;
87df930be7Sderaadt     u_char  debug;
88df930be7Sderaadt     u_char  unit;
89df930be7Sderaadt     u_int16_t seqno;			/* sequence # of next packet */
90df930be7Sderaadt     u_int   hdrlen;			/* header length to preallocate */
91df930be7Sderaadt     u_int   mru;
92df930be7Sderaadt     u_int   maxmaxcode;			/* largest valid code */
93df930be7Sderaadt     u_int   max_ent;			/* largest code in use */
94df930be7Sderaadt     u_int   in_count;			/* uncompressed bytes, aged */
95df930be7Sderaadt     u_int   bytes_out;			/* compressed bytes, aged */
96df930be7Sderaadt     u_int   ratio;			/* recent compression ratio */
97df930be7Sderaadt     u_int   checkpoint;			/* when to next check the ratio */
98df930be7Sderaadt     u_int   clear_count;		/* times dictionary cleared */
99df930be7Sderaadt     u_int   incomp_count;		/* incompressible packets */
100df930be7Sderaadt     u_int   incomp_bytes;		/* incompressible bytes */
101df930be7Sderaadt     u_int   uncomp_count;		/* uncompressed packets */
102df930be7Sderaadt     u_int   uncomp_bytes;		/* uncompressed bytes */
103df930be7Sderaadt     u_int   comp_count;			/* compressed packets */
104df930be7Sderaadt     u_int   comp_bytes;			/* compressed bytes */
105df930be7Sderaadt     u_int16_t *lens;			/* array of lengths of codes */
106df930be7Sderaadt     struct bsd_dict {
107df930be7Sderaadt 	union {				/* hash value */
108df930be7Sderaadt 	    u_int32_t	fcode;
109df930be7Sderaadt 	    struct {
110df930be7Sderaadt #if BYTE_ORDER == LITTLE_ENDIAN
111df930be7Sderaadt 		u_int16_t prefix;	/* preceding code */
112df930be7Sderaadt 		u_char	suffix;		/* last character of new code */
113df930be7Sderaadt 		u_char	pad;
114df930be7Sderaadt #else
115df930be7Sderaadt 		u_char	pad;
116df930be7Sderaadt 		u_char	suffix;		/* last character of new code */
117df930be7Sderaadt 		u_int16_t prefix;	/* preceding code */
118df930be7Sderaadt #endif
119df930be7Sderaadt 	    } hs;
120df930be7Sderaadt 	} f;
121df930be7Sderaadt 	u_int16_t codem1;		/* output of hash table -1 */
122df930be7Sderaadt 	u_int16_t cptr;			/* map code to hash table entry */
123df930be7Sderaadt     } dict[1];
124df930be7Sderaadt };
125df930be7Sderaadt 
126df930be7Sderaadt #define BSD_OVHD	2		/* BSD compress overhead/packet */
127df930be7Sderaadt #define BSD_INIT_BITS	BSD_MIN_BITS
128df930be7Sderaadt 
129c4071fd1Smillert static void	*bsd_comp_alloc(u_char *options, int opt_len);
130c4071fd1Smillert static void	*bsd_decomp_alloc(u_char *options, int opt_len);
131c4071fd1Smillert static void	bsd_free(void *state);
132c4071fd1Smillert static int	bsd_comp_init(void *state, u_char *options, int opt_len,
133c4071fd1Smillert 				   int unit, int hdrlen, int debug);
134c4071fd1Smillert static int	bsd_decomp_init(void *state, u_char *options, int opt_len,
135c4071fd1Smillert 				     int unit, int hdrlen, int mru, int debug);
136c4071fd1Smillert static int	bsd_compress(void *state, struct mbuf **mret,
137c4071fd1Smillert 				  struct mbuf *mp, int slen, int maxolen);
138c4071fd1Smillert static void	bsd_incomp(void *state, struct mbuf *dmsg);
139c4071fd1Smillert static int	bsd_decompress(void *state, struct mbuf *cmp,
140c4071fd1Smillert 				    struct mbuf **dmpp);
141c4071fd1Smillert static void	bsd_reset(void *state);
142c4071fd1Smillert static void	bsd_comp_stats(void *state, struct compstat *stats);
143df930be7Sderaadt 
144df930be7Sderaadt /*
145df930be7Sderaadt  * Procedures exported to if_ppp.c.
146df930be7Sderaadt  */
147df930be7Sderaadt struct compressor ppp_bsd_compress = {
148df930be7Sderaadt     CI_BSD_COMPRESS,		/* compress_proto */
149df930be7Sderaadt     bsd_comp_alloc,		/* comp_alloc */
150df930be7Sderaadt     bsd_free,			/* comp_free */
151df930be7Sderaadt     bsd_comp_init,		/* comp_init */
152df930be7Sderaadt     bsd_reset,			/* comp_reset */
153df930be7Sderaadt     bsd_compress,		/* compress */
154df930be7Sderaadt     bsd_comp_stats,		/* comp_stat */
155df930be7Sderaadt     bsd_decomp_alloc,		/* decomp_alloc */
156df930be7Sderaadt     bsd_free,			/* decomp_free */
157df930be7Sderaadt     bsd_decomp_init,		/* decomp_init */
158df930be7Sderaadt     bsd_reset,			/* decomp_reset */
159df930be7Sderaadt     bsd_decompress,		/* decompress */
160df930be7Sderaadt     bsd_incomp,			/* incomp */
161df930be7Sderaadt     bsd_comp_stats,		/* decomp_stat */
162df930be7Sderaadt };
163df930be7Sderaadt 
164df930be7Sderaadt /*
165df930be7Sderaadt  * the next two codes should not be changed lightly, as they must not
166df930be7Sderaadt  * lie within the contiguous general code space.
167df930be7Sderaadt  */
168df930be7Sderaadt #define CLEAR	256			/* table clear output code */
169df930be7Sderaadt #define FIRST	257			/* first free entry */
170df930be7Sderaadt #define LAST	255
171df930be7Sderaadt 
172df930be7Sderaadt #define MAXCODE(b)	((1 << (b)) - 1)
173df930be7Sderaadt #define BADCODEM1	MAXCODE(BSD_MAX_BITS)
174df930be7Sderaadt 
175df930be7Sderaadt #define BSD_HASH(prefix,suffix,hshift)	((((u_int32_t)(suffix)) << (hshift)) \
176df930be7Sderaadt 					 ^ (u_int32_t)(prefix))
177df930be7Sderaadt #define BSD_KEY(prefix,suffix)		((((u_int32_t)(suffix)) << 16) \
178df930be7Sderaadt 					 + (u_int32_t)(prefix))
179df930be7Sderaadt 
180df930be7Sderaadt #define CHECK_GAP	10000		/* Ratio check interval */
181df930be7Sderaadt 
182df930be7Sderaadt #define RATIO_SCALE_LOG	8
183df930be7Sderaadt #define RATIO_SCALE	(1<<RATIO_SCALE_LOG)
184df930be7Sderaadt #define RATIO_MAX	(0x7fffffff>>RATIO_SCALE_LOG)
185df930be7Sderaadt 
186c4071fd1Smillert static void bsd_clear(struct bsd_db *);
187c4071fd1Smillert static int bsd_check(struct bsd_db *);
188c4071fd1Smillert static void *bsd_alloc(u_char *, int, int);
189c4071fd1Smillert static int bsd_init(struct bsd_db *, u_char *, int, int, int, int,
190c4071fd1Smillert 			 int, int);
191edd75aa1Sniklas 
192df930be7Sderaadt /*
193df930be7Sderaadt  * clear the dictionary
194df930be7Sderaadt  */
195df930be7Sderaadt static void
bsd_clear(struct bsd_db * db)196*361206c2Sjsg bsd_clear(struct bsd_db *db)
197df930be7Sderaadt {
198df930be7Sderaadt     db->clear_count++;
199df930be7Sderaadt     db->max_ent = FIRST-1;
200df930be7Sderaadt     db->n_bits = BSD_INIT_BITS;
201df930be7Sderaadt     db->ratio = 0;
202df930be7Sderaadt     db->bytes_out = 0;
203df930be7Sderaadt     db->in_count = 0;
204df930be7Sderaadt     db->incomp_count = 0;
205df930be7Sderaadt     db->checkpoint = CHECK_GAP;
206df930be7Sderaadt }
207df930be7Sderaadt 
208df930be7Sderaadt /*
209df930be7Sderaadt  * If the dictionary is full, then see if it is time to reset it.
210df930be7Sderaadt  *
211df930be7Sderaadt  * Compute the compression ratio using fixed-point arithmetic
212df930be7Sderaadt  * with 8 fractional bits.
213df930be7Sderaadt  *
214df930be7Sderaadt  * Since we have an infinite stream instead of a single file,
215df930be7Sderaadt  * watch only the local compression ratio.
216df930be7Sderaadt  *
217df930be7Sderaadt  * Since both peers must reset the dictionary at the same time even in
218df930be7Sderaadt  * the absence of CLEAR codes (while packets are incompressible), they
219df930be7Sderaadt  * must compute the same ratio.
220df930be7Sderaadt  */
221df930be7Sderaadt static int				/* 1=output CLEAR */
bsd_check(struct bsd_db * db)222*361206c2Sjsg bsd_check(struct bsd_db *db)
223df930be7Sderaadt {
224df930be7Sderaadt     u_int new_ratio;
225df930be7Sderaadt 
226df930be7Sderaadt     if (db->in_count >= db->checkpoint) {
227df930be7Sderaadt 	/* age the ratio by limiting the size of the counts */
228df930be7Sderaadt 	if (db->in_count >= RATIO_MAX
229df930be7Sderaadt 	    || db->bytes_out >= RATIO_MAX) {
230df930be7Sderaadt 	    db->in_count -= db->in_count/4;
231df930be7Sderaadt 	    db->bytes_out -= db->bytes_out/4;
232df930be7Sderaadt 	}
233df930be7Sderaadt 
234df930be7Sderaadt 	db->checkpoint = db->in_count + CHECK_GAP;
235df930be7Sderaadt 
236df930be7Sderaadt 	if (db->max_ent >= db->maxmaxcode) {
237df930be7Sderaadt 	    /* Reset the dictionary only if the ratio is worse,
238df930be7Sderaadt 	     * or if it looks as if it has been poisoned
239df930be7Sderaadt 	     * by incompressible data.
240df930be7Sderaadt 	     *
241df930be7Sderaadt 	     * This does not overflow, because
242df930be7Sderaadt 	     *	db->in_count <= RATIO_MAX.
243df930be7Sderaadt 	     */
244df930be7Sderaadt 	    new_ratio = db->in_count << RATIO_SCALE_LOG;
245df930be7Sderaadt 	    if (db->bytes_out != 0)
246df930be7Sderaadt 		new_ratio /= db->bytes_out;
247df930be7Sderaadt 
248df930be7Sderaadt 	    if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE) {
249df930be7Sderaadt 		bsd_clear(db);
250df930be7Sderaadt 		return 1;
251df930be7Sderaadt 	    }
252df930be7Sderaadt 	    db->ratio = new_ratio;
253df930be7Sderaadt 	}
254df930be7Sderaadt     }
255df930be7Sderaadt     return 0;
256df930be7Sderaadt }
257df930be7Sderaadt 
258df930be7Sderaadt /*
259df930be7Sderaadt  * Return statistics.
260df930be7Sderaadt  */
261df930be7Sderaadt static void
bsd_comp_stats(void * state,struct compstat * stats)262*361206c2Sjsg bsd_comp_stats(void *state, struct compstat *stats)
263df930be7Sderaadt {
264df930be7Sderaadt     struct bsd_db *db = (struct bsd_db *) state;
265df930be7Sderaadt     u_int out;
266df930be7Sderaadt 
267df930be7Sderaadt     stats->unc_bytes = db->uncomp_bytes;
268df930be7Sderaadt     stats->unc_packets = db->uncomp_count;
269df930be7Sderaadt     stats->comp_bytes = db->comp_bytes;
270df930be7Sderaadt     stats->comp_packets = db->comp_count;
271df930be7Sderaadt     stats->inc_bytes = db->incomp_bytes;
272df930be7Sderaadt     stats->inc_packets = db->incomp_count;
273df930be7Sderaadt     stats->ratio = db->in_count;
274df930be7Sderaadt     out = db->bytes_out;
275df930be7Sderaadt     if (stats->ratio <= 0x7fffff)
276df930be7Sderaadt 	stats->ratio <<= 8;
277df930be7Sderaadt     else
278df930be7Sderaadt 	out >>= 8;
279df930be7Sderaadt     if (out != 0)
280df930be7Sderaadt 	stats->ratio /= out;
281df930be7Sderaadt }
282df930be7Sderaadt 
283df930be7Sderaadt /*
284df930be7Sderaadt  * Reset state, as on a CCP ResetReq.
285df930be7Sderaadt  */
286df930be7Sderaadt static void
bsd_reset(void * state)287*361206c2Sjsg bsd_reset(void *state)
288df930be7Sderaadt {
289df930be7Sderaadt     struct bsd_db *db = (struct bsd_db *) state;
290df930be7Sderaadt 
291df930be7Sderaadt     db->seqno = 0;
292df930be7Sderaadt     bsd_clear(db);
293df930be7Sderaadt     db->clear_count = 0;
294df930be7Sderaadt }
295df930be7Sderaadt 
296df930be7Sderaadt /*
297df930be7Sderaadt  * Allocate space for a (de) compressor.
298df930be7Sderaadt  */
299df930be7Sderaadt static void *
bsd_alloc(u_char * options,int opt_len,int decomp)300*361206c2Sjsg bsd_alloc(u_char *options, int opt_len, int decomp)
301df930be7Sderaadt {
302df930be7Sderaadt     int bits;
303df930be7Sderaadt     u_int newlen, hsize, hshift, maxmaxcode;
304df930be7Sderaadt     struct bsd_db *db;
305df930be7Sderaadt 
306d724e01aSderaadt     if (opt_len < CILEN_BSD_COMPRESS || options[0] != CI_BSD_COMPRESS
307df930be7Sderaadt 	|| options[1] != CILEN_BSD_COMPRESS
308df930be7Sderaadt 	|| BSD_VERSION(options[2]) != BSD_CURRENT_VERSION)
309df930be7Sderaadt 	return NULL;
310df930be7Sderaadt     bits = BSD_NBITS(options[2]);
311df930be7Sderaadt     switch (bits) {
312df930be7Sderaadt     case 9:			/* needs 82152 for both directions */
313df930be7Sderaadt     case 10:			/* needs 84144 */
314df930be7Sderaadt     case 11:			/* needs 88240 */
315df930be7Sderaadt     case 12:			/* needs 96432 */
316df930be7Sderaadt 	hsize = 5003;
317df930be7Sderaadt 	hshift = 4;
318df930be7Sderaadt 	break;
319df930be7Sderaadt     case 13:			/* needs 176784 */
320df930be7Sderaadt 	hsize = 9001;
321df930be7Sderaadt 	hshift = 5;
322df930be7Sderaadt 	break;
323df930be7Sderaadt     case 14:			/* needs 353744 */
324df930be7Sderaadt 	hsize = 18013;
325df930be7Sderaadt 	hshift = 6;
326df930be7Sderaadt 	break;
327df930be7Sderaadt     case 15:			/* needs 691440 */
328df930be7Sderaadt 	hsize = 35023;
329df930be7Sderaadt 	hshift = 7;
330df930be7Sderaadt 	break;
331df930be7Sderaadt     case 16:			/* needs 1366160--far too much, */
332df930be7Sderaadt 	/* hsize = 69001; */	/* and 69001 is too big for cptr */
333df930be7Sderaadt 	/* hshift = 8; */	/* in struct bsd_db */
334df930be7Sderaadt 	/* break; */
335df930be7Sderaadt     default:
336df930be7Sderaadt 	return NULL;
337df930be7Sderaadt     }
338df930be7Sderaadt 
339df930be7Sderaadt     maxmaxcode = MAXCODE(bits);
340df930be7Sderaadt     newlen = sizeof(*db) + (hsize-1) * (sizeof(db->dict[0]));
3418b28ab37Shenning     db = malloc(newlen, M_DEVBUF, M_NOWAIT|M_ZERO);
342df930be7Sderaadt     if (!db)
343df930be7Sderaadt 	return NULL;
344df930be7Sderaadt 
345df930be7Sderaadt     if (!decomp) {
346df930be7Sderaadt 	db->lens = NULL;
347df930be7Sderaadt     } else {
34883bfce96Stedu 	db->lens = mallocarray(maxmaxcode + 1, sizeof(db->lens[0]), M_DEVBUF,
3498b28ab37Shenning 	    M_NOWAIT);
350df930be7Sderaadt 	if (!db->lens) {
3519b9fad9cSderaadt 	    free(db, M_DEVBUF, newlen);
352df930be7Sderaadt 	    return NULL;
353df930be7Sderaadt 	}
354df930be7Sderaadt     }
355df930be7Sderaadt 
356df930be7Sderaadt     db->totlen = newlen;
357df930be7Sderaadt     db->hsize = hsize;
358df930be7Sderaadt     db->hshift = hshift;
359df930be7Sderaadt     db->maxmaxcode = maxmaxcode;
360df930be7Sderaadt     db->maxbits = bits;
361df930be7Sderaadt 
362df930be7Sderaadt     return (void *) db;
363df930be7Sderaadt }
364df930be7Sderaadt 
365df930be7Sderaadt static void
bsd_free(void * state)366*361206c2Sjsg bsd_free(void *state)
367df930be7Sderaadt {
368df930be7Sderaadt     struct bsd_db *db = (struct bsd_db *) state;
369df930be7Sderaadt 
370df930be7Sderaadt     if (db->lens)
3719b9fad9cSderaadt 	free(db->lens, M_DEVBUF, (db->maxmaxcode + 1) * sizeof(db->lens[0]));
3729b9fad9cSderaadt     free(db, M_DEVBUF, db->totlen);
373df930be7Sderaadt }
374df930be7Sderaadt 
375df930be7Sderaadt static void *
bsd_comp_alloc(u_char * options,int opt_len)376*361206c2Sjsg bsd_comp_alloc(u_char *options, int opt_len)
377df930be7Sderaadt {
378df930be7Sderaadt     return bsd_alloc(options, opt_len, 0);
379df930be7Sderaadt }
380df930be7Sderaadt 
381df930be7Sderaadt static void *
bsd_decomp_alloc(u_char * options,int opt_len)382*361206c2Sjsg bsd_decomp_alloc(u_char *options, int opt_len)
383df930be7Sderaadt {
384df930be7Sderaadt     return bsd_alloc(options, opt_len, 1);
385df930be7Sderaadt }
386df930be7Sderaadt 
387df930be7Sderaadt /*
388df930be7Sderaadt  * Initialize the database.
389df930be7Sderaadt  */
390df930be7Sderaadt static int
bsd_init(struct bsd_db * db,u_char * options,int opt_len,int unit,int hdrlen,int mru,int debug,int decomp)391*361206c2Sjsg bsd_init(struct bsd_db *db, u_char *options, int opt_len, int unit, int hdrlen,
392*361206c2Sjsg     int mru, int debug, int decomp)
393df930be7Sderaadt {
394df930be7Sderaadt     int i;
395df930be7Sderaadt 
396d724e01aSderaadt     if (opt_len < CILEN_BSD_COMPRESS || options[0] != CI_BSD_COMPRESS
397df930be7Sderaadt 	|| options[1] != CILEN_BSD_COMPRESS
398df930be7Sderaadt 	|| BSD_VERSION(options[2]) != BSD_CURRENT_VERSION
399df930be7Sderaadt 	|| BSD_NBITS(options[2]) != db->maxbits
400edd75aa1Sniklas 	|| (decomp && db->lens == NULL))
401df930be7Sderaadt 	return 0;
402df930be7Sderaadt 
403df930be7Sderaadt     if (decomp) {
404df930be7Sderaadt 	i = LAST+1;
405df930be7Sderaadt 	while (i != 0)
406df930be7Sderaadt 	    db->lens[--i] = 1;
407df930be7Sderaadt     }
408df930be7Sderaadt     i = db->hsize;
409df930be7Sderaadt     while (i != 0) {
410df930be7Sderaadt 	db->dict[--i].codem1 = BADCODEM1;
411df930be7Sderaadt 	db->dict[i].cptr = 0;
412df930be7Sderaadt     }
413df930be7Sderaadt 
414df930be7Sderaadt     db->unit = unit;
415df930be7Sderaadt     db->hdrlen = hdrlen;
416df930be7Sderaadt     db->mru = mru;
417df930be7Sderaadt #ifndef DEBUG
418df930be7Sderaadt     if (debug)
419df930be7Sderaadt #endif
420df930be7Sderaadt 	db->debug = 1;
421df930be7Sderaadt 
422df930be7Sderaadt     bsd_reset(db);
423df930be7Sderaadt 
424df930be7Sderaadt     return 1;
425df930be7Sderaadt }
426df930be7Sderaadt 
427df930be7Sderaadt static int
bsd_comp_init(void * state,u_char * options,int opt_len,int unit,int hdrlen,int debug)428*361206c2Sjsg bsd_comp_init(void *state, u_char *options, int opt_len, int unit, int hdrlen,
429*361206c2Sjsg     int debug)
430df930be7Sderaadt {
431df930be7Sderaadt     return bsd_init((struct bsd_db *) state, options, opt_len,
432df930be7Sderaadt 		    unit, hdrlen, 0, debug, 0);
433df930be7Sderaadt }
434df930be7Sderaadt 
435df930be7Sderaadt static int
bsd_decomp_init(void * state,u_char * options,int opt_len,int unit,int hdrlen,int mru,int debug)436*361206c2Sjsg bsd_decomp_init(void *state, u_char *options, int opt_len, int unit, int hdrlen,
437*361206c2Sjsg     int mru, int debug)
438df930be7Sderaadt {
439df930be7Sderaadt     return bsd_init((struct bsd_db *) state, options, opt_len,
440df930be7Sderaadt 		    unit, hdrlen, mru, debug, 1);
441df930be7Sderaadt }
442df930be7Sderaadt 
443df930be7Sderaadt 
444df930be7Sderaadt /*
445df930be7Sderaadt  * compress a packet
446df930be7Sderaadt  *	One change from the BSD compress command is that when the
447df930be7Sderaadt  *	code size expands, we do not output a bunch of padding.
448df930be7Sderaadt  */
449df930be7Sderaadt int					/* new slen */
bsd_compress(void * state,struct mbuf ** mret,struct mbuf * mp,int slen,int maxolen)450*361206c2Sjsg bsd_compress(void *state,
451*361206c2Sjsg     struct mbuf **mret,		/* return compressed mbuf chain here */
452*361206c2Sjsg     struct mbuf *mp,		/* from here */
453*361206c2Sjsg     int slen,			/* uncompressed length */
454*361206c2Sjsg     int maxolen)		/* max compressed length */
455df930be7Sderaadt {
456df930be7Sderaadt     struct bsd_db *db = (struct bsd_db *) state;
457df930be7Sderaadt     int hshift = db->hshift;
458df930be7Sderaadt     u_int max_ent = db->max_ent;
459df930be7Sderaadt     u_int n_bits = db->n_bits;
460df930be7Sderaadt     u_int bitno = 32;
461df930be7Sderaadt     u_int32_t accm = 0, fcode;
462df930be7Sderaadt     struct bsd_dict *dictp;
463df930be7Sderaadt     u_char c;
464df930be7Sderaadt     int hval, disp, ent, ilen;
465df930be7Sderaadt     u_char *rptr, *wptr;
466df930be7Sderaadt     u_char *cp_end;
467df930be7Sderaadt     int olen;
468edd75aa1Sniklas     struct mbuf *m;
469df930be7Sderaadt 
470df930be7Sderaadt #define PUTBYTE(v) {					\
471df930be7Sderaadt     ++olen;						\
472df930be7Sderaadt     if (wptr) {						\
473df930be7Sderaadt 	*wptr++ = (v);					\
474df930be7Sderaadt 	if (wptr >= cp_end) {				\
475df930be7Sderaadt 	    m->m_len = wptr - mtod(m, u_char *);	\
476df930be7Sderaadt 	    MGET(m->m_next, M_DONTWAIT, MT_DATA);	\
477df930be7Sderaadt 	    m = m->m_next;				\
478df930be7Sderaadt 	    if (m) {					\
479df930be7Sderaadt 		m->m_len = 0;				\
480df930be7Sderaadt 		if (maxolen - olen > MLEN)		\
481df930be7Sderaadt 		    MCLGET(m, M_DONTWAIT);		\
482df930be7Sderaadt 		wptr = mtod(m, u_char *);		\
483b5b7f62eSclaudio 		cp_end = wptr + m_trailingspace(m);	\
484df930be7Sderaadt 	    } else					\
485df930be7Sderaadt 		wptr = NULL;				\
486df930be7Sderaadt 	}						\
487df930be7Sderaadt     }							\
488df930be7Sderaadt }
489df930be7Sderaadt 
490df930be7Sderaadt #define OUTPUT(ent) {					\
491df930be7Sderaadt     bitno -= n_bits;					\
492df930be7Sderaadt     accm |= ((ent) << bitno);				\
493df930be7Sderaadt     do {						\
494df930be7Sderaadt 	PUTBYTE(accm >> 24);				\
495df930be7Sderaadt 	accm <<= 8;					\
496df930be7Sderaadt 	bitno += 8;					\
497df930be7Sderaadt     } while (bitno <= 24);				\
498df930be7Sderaadt }
499df930be7Sderaadt 
500df930be7Sderaadt     /*
501df930be7Sderaadt      * If the protocol is not in the range we're interested in,
502df930be7Sderaadt      * just return without compressing the packet.  If it is,
503df930be7Sderaadt      * the protocol becomes the first byte to compress.
504df930be7Sderaadt      */
505df930be7Sderaadt     rptr = mtod(mp, u_char *);
506df930be7Sderaadt     ent = PPP_PROTOCOL(rptr);
507df930be7Sderaadt     if (ent < 0x21 || ent > 0xf9) {
508df930be7Sderaadt 	*mret = NULL;
509df930be7Sderaadt 	return slen;
510df930be7Sderaadt     }
511df930be7Sderaadt 
512df930be7Sderaadt     /* Don't generate compressed packets which are larger than
513df930be7Sderaadt        the uncompressed packet. */
514df930be7Sderaadt     if (maxolen > slen)
515df930be7Sderaadt 	maxolen = slen;
516df930be7Sderaadt 
517df930be7Sderaadt     /* Allocate one mbuf to start with. */
518df930be7Sderaadt     MGET(m, M_DONTWAIT, MT_DATA);
519df930be7Sderaadt     *mret = m;
520df930be7Sderaadt     if (m != NULL) {
521df930be7Sderaadt 	m->m_len = 0;
522df930be7Sderaadt 	if (maxolen + db->hdrlen > MLEN)
523df930be7Sderaadt 	    MCLGET(m, M_DONTWAIT);
524df930be7Sderaadt 	m->m_data += db->hdrlen;
525df930be7Sderaadt 	wptr = mtod(m, u_char *);
526b5b7f62eSclaudio 	cp_end = wptr + m_trailingspace(m);
527df930be7Sderaadt     } else
528df930be7Sderaadt 	wptr = cp_end = NULL;
529df930be7Sderaadt 
530df930be7Sderaadt     /*
531df930be7Sderaadt      * Copy the PPP header over, changing the protocol,
532df930be7Sderaadt      * and install the 2-byte packet sequence number.
533df930be7Sderaadt      */
534df930be7Sderaadt     if (wptr) {
535df930be7Sderaadt 	*wptr++ = PPP_ADDRESS(rptr);	/* assumes the ppp header is */
536df930be7Sderaadt 	*wptr++ = PPP_CONTROL(rptr);	/* all in one mbuf */
537df930be7Sderaadt 	*wptr++ = 0;			/* change the protocol */
538df930be7Sderaadt 	*wptr++ = PPP_COMP;
539df930be7Sderaadt 	*wptr++ = db->seqno >> 8;
540df930be7Sderaadt 	*wptr++ = db->seqno;
541df930be7Sderaadt     }
542df930be7Sderaadt     ++db->seqno;
543df930be7Sderaadt 
544df930be7Sderaadt     olen = 0;
545df930be7Sderaadt     rptr += PPP_HDRLEN;
546df930be7Sderaadt     slen = mp->m_len - PPP_HDRLEN;
547df930be7Sderaadt     ilen = slen + 1;
548df930be7Sderaadt     for (;;) {
549df930be7Sderaadt 	if (slen <= 0) {
550df930be7Sderaadt 	    mp = mp->m_next;
551df930be7Sderaadt 	    if (!mp)
552df930be7Sderaadt 		break;
553df930be7Sderaadt 	    rptr = mtod(mp, u_char *);
554df930be7Sderaadt 	    slen = mp->m_len;
555df930be7Sderaadt 	    if (!slen)
556df930be7Sderaadt 		continue;   /* handle 0-length buffers */
557df930be7Sderaadt 	    ilen += slen;
558df930be7Sderaadt 	}
559df930be7Sderaadt 
560df930be7Sderaadt 	slen--;
561df930be7Sderaadt 	c = *rptr++;
562df930be7Sderaadt 	fcode = BSD_KEY(ent, c);
563df930be7Sderaadt 	hval = BSD_HASH(ent, c, hshift);
564df930be7Sderaadt 	dictp = &db->dict[hval];
565df930be7Sderaadt 
566df930be7Sderaadt 	/* Validate and then check the entry. */
567df930be7Sderaadt 	if (dictp->codem1 >= max_ent)
568df930be7Sderaadt 	    goto nomatch;
569df930be7Sderaadt 	if (dictp->f.fcode == fcode) {
570df930be7Sderaadt 	    ent = dictp->codem1+1;
571df930be7Sderaadt 	    continue;	/* found (prefix,suffix) */
572df930be7Sderaadt 	}
573df930be7Sderaadt 
574df930be7Sderaadt 	/* continue probing until a match or invalid entry */
575df930be7Sderaadt 	disp = (hval == 0) ? 1 : hval;
576df930be7Sderaadt 	do {
577df930be7Sderaadt 	    hval += disp;
578df930be7Sderaadt 	    if (hval >= db->hsize)
579df930be7Sderaadt 		hval -= db->hsize;
580df930be7Sderaadt 	    dictp = &db->dict[hval];
581df930be7Sderaadt 	    if (dictp->codem1 >= max_ent)
582df930be7Sderaadt 		goto nomatch;
583df930be7Sderaadt 	} while (dictp->f.fcode != fcode);
584df930be7Sderaadt 	ent = dictp->codem1 + 1;	/* finally found (prefix,suffix) */
585df930be7Sderaadt 	continue;
586df930be7Sderaadt 
587df930be7Sderaadt     nomatch:
588df930be7Sderaadt 	OUTPUT(ent);		/* output the prefix */
589df930be7Sderaadt 
590df930be7Sderaadt 	/* code -> hashtable */
591df930be7Sderaadt 	if (max_ent < db->maxmaxcode) {
592df930be7Sderaadt 	    struct bsd_dict *dictp2;
593df930be7Sderaadt 	    /* expand code size if needed */
594df930be7Sderaadt 	    if (max_ent >= MAXCODE(n_bits))
595df930be7Sderaadt 		db->n_bits = ++n_bits;
596df930be7Sderaadt 
597df930be7Sderaadt 	    /* Invalidate old hash table entry using
598df930be7Sderaadt 	     * this code, and then take it over.
599df930be7Sderaadt 	     */
600df930be7Sderaadt 	    dictp2 = &db->dict[max_ent+1];
601df930be7Sderaadt 	    if (db->dict[dictp2->cptr].codem1 == max_ent)
602df930be7Sderaadt 		db->dict[dictp2->cptr].codem1 = BADCODEM1;
603df930be7Sderaadt 	    dictp2->cptr = hval;
604df930be7Sderaadt 	    dictp->codem1 = max_ent;
605df930be7Sderaadt 	    dictp->f.fcode = fcode;
606df930be7Sderaadt 
607df930be7Sderaadt 	    db->max_ent = ++max_ent;
608df930be7Sderaadt 	}
609df930be7Sderaadt 	ent = c;
610df930be7Sderaadt     }
611df930be7Sderaadt 
612df930be7Sderaadt     OUTPUT(ent);		/* output the last code */
613df930be7Sderaadt     db->bytes_out += olen;
614df930be7Sderaadt     db->in_count += ilen;
615df930be7Sderaadt     if (bitno < 32)
616df930be7Sderaadt 	++db->bytes_out;	/* count complete bytes */
617df930be7Sderaadt 
618df930be7Sderaadt     if (bsd_check(db))
619df930be7Sderaadt 	OUTPUT(CLEAR);		/* do not count the CLEAR */
620df930be7Sderaadt 
621df930be7Sderaadt     /*
622df930be7Sderaadt      * Pad dribble bits of last code with ones.
623df930be7Sderaadt      * Do not emit a completely useless byte of ones.
624df930be7Sderaadt      */
625df930be7Sderaadt     if (bitno != 32)
626df930be7Sderaadt 	PUTBYTE((accm | (0xff << (bitno-8))) >> 24);
627df930be7Sderaadt 
628df930be7Sderaadt     if (m != NULL) {
629df930be7Sderaadt 	m->m_len = wptr - mtod(m, u_char *);
630df930be7Sderaadt 	m->m_next = NULL;
631df930be7Sderaadt     }
632df930be7Sderaadt 
633df930be7Sderaadt     /*
634df930be7Sderaadt      * Increase code size if we would have without the packet
635df930be7Sderaadt      * boundary and as the decompressor will.
636df930be7Sderaadt      */
637df930be7Sderaadt     if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode)
638df930be7Sderaadt 	db->n_bits++;
639df930be7Sderaadt 
640df930be7Sderaadt     db->uncomp_bytes += ilen;
641df930be7Sderaadt     ++db->uncomp_count;
642df930be7Sderaadt     if (olen + PPP_HDRLEN + BSD_OVHD > maxolen) {
643df930be7Sderaadt 	/* throw away the compressed stuff if it is longer than uncompressed */
64436c0109eSbluhm 	m_freemp(mret);
64509284026Sderaadt 
646df930be7Sderaadt 	++db->incomp_count;
647df930be7Sderaadt 	db->incomp_bytes += ilen;
648df930be7Sderaadt     } else {
649df930be7Sderaadt 	++db->comp_count;
650df930be7Sderaadt 	db->comp_bytes += olen + BSD_OVHD;
651df930be7Sderaadt     }
652df930be7Sderaadt 
653df930be7Sderaadt     return olen + PPP_HDRLEN + BSD_OVHD;
654df930be7Sderaadt #undef OUTPUT
655df930be7Sderaadt #undef PUTBYTE
656df930be7Sderaadt }
657df930be7Sderaadt 
658df930be7Sderaadt 
659df930be7Sderaadt /*
660df930be7Sderaadt  * Update the "BSD Compress" dictionary on the receiver for
661df930be7Sderaadt  * incompressible data by pretending to compress the incoming data.
662df930be7Sderaadt  */
663df930be7Sderaadt static void
bsd_incomp(void * state,struct mbuf * dmsg)664*361206c2Sjsg bsd_incomp(void *state, struct mbuf *dmsg)
665df930be7Sderaadt {
666df930be7Sderaadt     struct bsd_db *db = (struct bsd_db *) state;
667df930be7Sderaadt     u_int hshift = db->hshift;
668df930be7Sderaadt     u_int max_ent = db->max_ent;
669df930be7Sderaadt     u_int n_bits = db->n_bits;
670df930be7Sderaadt     struct bsd_dict *dictp;
671df930be7Sderaadt     u_int32_t fcode;
672df930be7Sderaadt     u_char c;
673df930be7Sderaadt     u_int32_t hval, disp;
674df930be7Sderaadt     int slen, ilen;
675df930be7Sderaadt     u_int bitno = 7;
676df930be7Sderaadt     u_char *rptr;
677df930be7Sderaadt     u_int ent;
678df930be7Sderaadt 
679df930be7Sderaadt     /*
680df930be7Sderaadt      * If the protocol is not in the range we're interested in,
681df930be7Sderaadt      * just return without looking at the packet.  If it is,
682df930be7Sderaadt      * the protocol becomes the first byte to "compress".
683df930be7Sderaadt      */
684df930be7Sderaadt     rptr = mtod(dmsg, u_char *);
685df930be7Sderaadt     ent = PPP_PROTOCOL(rptr);
686df930be7Sderaadt     if (ent < 0x21 || ent > 0xf9)
687df930be7Sderaadt 	return;
688df930be7Sderaadt 
689df930be7Sderaadt     db->incomp_count++;
690df930be7Sderaadt     db->seqno++;
691df930be7Sderaadt     ilen = 1;		/* count the protocol as 1 byte */
692df930be7Sderaadt     rptr += PPP_HDRLEN;
693df930be7Sderaadt     slen = dmsg->m_len - PPP_HDRLEN;
694df930be7Sderaadt     for (;;) {
695df930be7Sderaadt 	if (slen <= 0) {
696df930be7Sderaadt 	    dmsg = dmsg->m_next;
697df930be7Sderaadt 	    if (!dmsg)
698df930be7Sderaadt 		break;
699df930be7Sderaadt 	    rptr = mtod(dmsg, u_char *);
700df930be7Sderaadt 	    slen = dmsg->m_len;
701df930be7Sderaadt 	    continue;
702df930be7Sderaadt 	}
703df930be7Sderaadt 	ilen += slen;
704df930be7Sderaadt 
705df930be7Sderaadt 	do {
706df930be7Sderaadt 	    c = *rptr++;
707df930be7Sderaadt 	    fcode = BSD_KEY(ent, c);
708df930be7Sderaadt 	    hval = BSD_HASH(ent, c, hshift);
709df930be7Sderaadt 	    dictp = &db->dict[hval];
710df930be7Sderaadt 
711df930be7Sderaadt 	    /* validate and then check the entry */
712df930be7Sderaadt 	    if (dictp->codem1 >= max_ent)
713df930be7Sderaadt 		goto nomatch;
714df930be7Sderaadt 	    if (dictp->f.fcode == fcode) {
715df930be7Sderaadt 		ent = dictp->codem1+1;
716df930be7Sderaadt 		continue;   /* found (prefix,suffix) */
717df930be7Sderaadt 	    }
718df930be7Sderaadt 
719df930be7Sderaadt 	    /* continue probing until a match or invalid entry */
720df930be7Sderaadt 	    disp = (hval == 0) ? 1 : hval;
721df930be7Sderaadt 	    do {
722df930be7Sderaadt 		hval += disp;
723df930be7Sderaadt 		if (hval >= db->hsize)
724df930be7Sderaadt 		    hval -= db->hsize;
725df930be7Sderaadt 		dictp = &db->dict[hval];
726df930be7Sderaadt 		if (dictp->codem1 >= max_ent)
727df930be7Sderaadt 		    goto nomatch;
728df930be7Sderaadt 	    } while (dictp->f.fcode != fcode);
729df930be7Sderaadt 	    ent = dictp->codem1+1;
730df930be7Sderaadt 	    continue;	/* finally found (prefix,suffix) */
731df930be7Sderaadt 
732df930be7Sderaadt 	nomatch:		/* output (count) the prefix */
733df930be7Sderaadt 	    bitno += n_bits;
734df930be7Sderaadt 
735df930be7Sderaadt 	    /* code -> hashtable */
736df930be7Sderaadt 	    if (max_ent < db->maxmaxcode) {
737df930be7Sderaadt 		struct bsd_dict *dictp2;
738df930be7Sderaadt 		/* expand code size if needed */
739df930be7Sderaadt 		if (max_ent >= MAXCODE(n_bits))
740df930be7Sderaadt 		    db->n_bits = ++n_bits;
741df930be7Sderaadt 
742df930be7Sderaadt 		/* Invalidate previous hash table entry
743df930be7Sderaadt 		 * assigned this code, and then take it over.
744df930be7Sderaadt 		 */
745df930be7Sderaadt 		dictp2 = &db->dict[max_ent+1];
746df930be7Sderaadt 		if (db->dict[dictp2->cptr].codem1 == max_ent)
747df930be7Sderaadt 		    db->dict[dictp2->cptr].codem1 = BADCODEM1;
748df930be7Sderaadt 		dictp2->cptr = hval;
749df930be7Sderaadt 		dictp->codem1 = max_ent;
750df930be7Sderaadt 		dictp->f.fcode = fcode;
751df930be7Sderaadt 
752df930be7Sderaadt 		db->max_ent = ++max_ent;
753df930be7Sderaadt 		db->lens[max_ent] = db->lens[ent]+1;
754df930be7Sderaadt 	    }
755df930be7Sderaadt 	    ent = c;
756df930be7Sderaadt 	} while (--slen != 0);
757df930be7Sderaadt     }
758df930be7Sderaadt     bitno += n_bits;		/* output (count) the last code */
759df930be7Sderaadt     db->bytes_out += bitno/8;
760df930be7Sderaadt     db->in_count += ilen;
761df930be7Sderaadt     (void)bsd_check(db);
762df930be7Sderaadt 
763df930be7Sderaadt     ++db->incomp_count;
764df930be7Sderaadt     db->incomp_bytes += ilen;
765df930be7Sderaadt     ++db->uncomp_count;
766df930be7Sderaadt     db->uncomp_bytes += ilen;
767df930be7Sderaadt 
768df930be7Sderaadt     /* Increase code size if we would have without the packet
769df930be7Sderaadt      * boundary and as the decompressor will.
770df930be7Sderaadt      */
771df930be7Sderaadt     if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode)
772df930be7Sderaadt 	db->n_bits++;
773df930be7Sderaadt }
774df930be7Sderaadt 
775df930be7Sderaadt 
776df930be7Sderaadt /*
777df930be7Sderaadt  * Decompress "BSD Compress".
778df930be7Sderaadt  *
779df930be7Sderaadt  * Because of patent problems, we return DECOMP_ERROR for errors
780df930be7Sderaadt  * found by inspecting the input data and for system problems, but
781df930be7Sderaadt  * DECOMP_FATALERROR for any errors which could possibly be said to
782df930be7Sderaadt  * be being detected "after" decompression.  For DECOMP_ERROR,
783df930be7Sderaadt  * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
784df930be7Sderaadt  * infringing a patent of Motorola's if we do, so we take CCP down
785df930be7Sderaadt  * instead.
786df930be7Sderaadt  *
787df930be7Sderaadt  * Given that the frame has the correct sequence number and a good FCS,
788df930be7Sderaadt  * errors such as invalid codes in the input most likely indicate a
789df930be7Sderaadt  * bug, so we return DECOMP_FATALERROR for them in order to turn off
790df930be7Sderaadt  * compression, even though they are detected by inspecting the input.
791df930be7Sderaadt  */
792df930be7Sderaadt int
bsd_decompress(void * state,struct mbuf * cmp,struct mbuf ** dmpp)793*361206c2Sjsg bsd_decompress(void *state, struct mbuf *cmp, struct mbuf **dmpp)
794df930be7Sderaadt {
795df930be7Sderaadt     struct bsd_db *db = (struct bsd_db *) state;
796df930be7Sderaadt     u_int max_ent = db->max_ent;
797df930be7Sderaadt     u_int32_t accm = 0;
798df930be7Sderaadt     u_int bitno = 32;		/* 1st valid bit in accm */
799df930be7Sderaadt     u_int n_bits = db->n_bits;
800df930be7Sderaadt     u_int tgtbitno = 32-n_bits;	/* bitno when we have a code */
801df930be7Sderaadt     struct bsd_dict *dictp;
802df930be7Sderaadt     int explen, i, seq, len;
803df930be7Sderaadt     u_int incode, oldcode, finchar;
804df930be7Sderaadt     u_char *p, *rptr, *wptr;
805df930be7Sderaadt     struct mbuf *m, *dmp, *mret;
806df930be7Sderaadt     int adrs, ctrl, ilen;
807df930be7Sderaadt     int space, codelen, extra;
808df930be7Sderaadt 
809df930be7Sderaadt     /*
810df930be7Sderaadt      * Save the address/control from the PPP header
811df930be7Sderaadt      * and then get the sequence number.
812df930be7Sderaadt      */
813df930be7Sderaadt     *dmpp = NULL;
814df930be7Sderaadt     rptr = mtod(cmp, u_char *);
815df930be7Sderaadt     adrs = PPP_ADDRESS(rptr);
816df930be7Sderaadt     ctrl = PPP_CONTROL(rptr);
817df930be7Sderaadt     rptr += PPP_HDRLEN;
818df930be7Sderaadt     len = cmp->m_len - PPP_HDRLEN;
819df930be7Sderaadt     seq = 0;
820df930be7Sderaadt     for (i = 0; i < 2; ++i) {
821df930be7Sderaadt 	while (len <= 0) {
822df930be7Sderaadt 	    cmp = cmp->m_next;
823df930be7Sderaadt 	    if (cmp == NULL)
824df930be7Sderaadt 		return DECOMP_ERROR;
825df930be7Sderaadt 	    rptr = mtod(cmp, u_char *);
826df930be7Sderaadt 	    len = cmp->m_len;
827df930be7Sderaadt 	}
828df930be7Sderaadt 	seq = (seq << 8) + *rptr++;
829df930be7Sderaadt 	--len;
830df930be7Sderaadt     }
831df930be7Sderaadt 
832df930be7Sderaadt     /*
833df930be7Sderaadt      * Check the sequence number and give up if it differs from
834df930be7Sderaadt      * the value we're expecting.
835df930be7Sderaadt      */
836df930be7Sderaadt     if (seq != db->seqno) {
837df930be7Sderaadt 	if (db->debug)
838df930be7Sderaadt 	    printf("bsd_decomp%d: bad sequence # %d, expected %d\n",
839df930be7Sderaadt 		   db->unit, seq, db->seqno - 1);
840df930be7Sderaadt 	return DECOMP_ERROR;
841df930be7Sderaadt     }
842df930be7Sderaadt     ++db->seqno;
843df930be7Sderaadt 
844df930be7Sderaadt     /*
845df930be7Sderaadt      * Allocate one mbuf to start with.
846df930be7Sderaadt      */
847df930be7Sderaadt     MGETHDR(dmp, M_DONTWAIT, MT_DATA);
848df930be7Sderaadt     if (dmp == NULL)
849df930be7Sderaadt 	return DECOMP_ERROR;
850df930be7Sderaadt     mret = dmp;
851df930be7Sderaadt     dmp->m_len = 0;
852df930be7Sderaadt     dmp->m_next = NULL;
853df930be7Sderaadt     MCLGET(dmp, M_DONTWAIT);
854df930be7Sderaadt     dmp->m_data += db->hdrlen;
855df930be7Sderaadt     wptr = mtod(dmp, u_char *);
856b5b7f62eSclaudio     space = m_trailingspace(dmp) - PPP_HDRLEN + 1;
857df930be7Sderaadt 
858df930be7Sderaadt     /*
859df930be7Sderaadt      * Fill in the ppp header, but not the last byte of the protocol
860df930be7Sderaadt      * (that comes from the decompressed data).
861df930be7Sderaadt      */
862df930be7Sderaadt     wptr[0] = adrs;
863df930be7Sderaadt     wptr[1] = ctrl;
864df930be7Sderaadt     wptr[2] = 0;
865df930be7Sderaadt     wptr += PPP_HDRLEN - 1;
866df930be7Sderaadt 
867df930be7Sderaadt     ilen = len;
868df930be7Sderaadt     oldcode = CLEAR;
869df930be7Sderaadt     explen = 0;
870df930be7Sderaadt     for (;;) {
871df930be7Sderaadt 	if (len == 0) {
872df930be7Sderaadt 	    cmp = cmp->m_next;
873df930be7Sderaadt 	    if (!cmp)		/* quit at end of message */
874df930be7Sderaadt 		break;
875df930be7Sderaadt 	    rptr = mtod(cmp, u_char *);
876df930be7Sderaadt 	    len = cmp->m_len;
877df930be7Sderaadt 	    ilen += len;
878df930be7Sderaadt 	    continue;		/* handle 0-length buffers */
879df930be7Sderaadt 	}
880df930be7Sderaadt 
881df930be7Sderaadt 	/*
882df930be7Sderaadt 	 * Accumulate bytes until we have a complete code.
883df930be7Sderaadt 	 * Then get the next code, relying on the 32-bit,
884df930be7Sderaadt 	 * unsigned accm to mask the result.
885df930be7Sderaadt 	 */
886df930be7Sderaadt 	bitno -= 8;
887df930be7Sderaadt 	accm |= *rptr++ << bitno;
888df930be7Sderaadt 	--len;
889df930be7Sderaadt 	if (tgtbitno < bitno)
890df930be7Sderaadt 	    continue;
891df930be7Sderaadt 	incode = accm >> tgtbitno;
892df930be7Sderaadt 	accm <<= n_bits;
893df930be7Sderaadt 	bitno += n_bits;
894df930be7Sderaadt 
895df930be7Sderaadt 	if (incode == CLEAR) {
896df930be7Sderaadt 	    /*
897df930be7Sderaadt 	     * The dictionary must only be cleared at
898df930be7Sderaadt 	     * the end of a packet.  But there could be an
899df930be7Sderaadt 	     * empty mbuf at the end.
900df930be7Sderaadt 	     */
901df930be7Sderaadt 	    if (len > 0 || cmp->m_next != NULL) {
902df930be7Sderaadt 		while ((cmp = cmp->m_next) != NULL)
903df930be7Sderaadt 		    len += cmp->m_len;
904df930be7Sderaadt 		if (len > 0) {
905df930be7Sderaadt 		    m_freem(mret);
906df930be7Sderaadt 		    if (db->debug)
907df930be7Sderaadt 			printf("bsd_decomp%d: bad CLEAR\n", db->unit);
908df930be7Sderaadt 		    return DECOMP_FATALERROR;	/* probably a bug */
909df930be7Sderaadt 		}
910df930be7Sderaadt 	    }
911df930be7Sderaadt 	    bsd_clear(db);
912df930be7Sderaadt 	    explen = ilen = 0;
913df930be7Sderaadt 	    break;
914df930be7Sderaadt 	}
915df930be7Sderaadt 
916df930be7Sderaadt 	if (incode > max_ent + 2 || incode > db->maxmaxcode
917edd75aa1Sniklas 	    || (incode > max_ent && oldcode == CLEAR)) {
918df930be7Sderaadt 	    m_freem(mret);
919df930be7Sderaadt 	    if (db->debug) {
920df930be7Sderaadt 		printf("bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
921df930be7Sderaadt 		       db->unit, incode, oldcode);
922df930be7Sderaadt 		printf("max_ent=0x%x explen=%d seqno=%d\n",
923df930be7Sderaadt 		       max_ent, explen, db->seqno);
924df930be7Sderaadt 	    }
925df930be7Sderaadt 	    return DECOMP_FATALERROR;	/* probably a bug */
926df930be7Sderaadt 	}
927df930be7Sderaadt 
928df930be7Sderaadt 	/* Special case for KwKwK string. */
929df930be7Sderaadt 	if (incode > max_ent) {
930df930be7Sderaadt 	    finchar = oldcode;
931df930be7Sderaadt 	    extra = 1;
932df930be7Sderaadt 	} else {
933df930be7Sderaadt 	    finchar = incode;
934df930be7Sderaadt 	    extra = 0;
935df930be7Sderaadt 	}
936df930be7Sderaadt 
937df930be7Sderaadt 	codelen = db->lens[finchar];
938df930be7Sderaadt 	explen += codelen + extra;
939df930be7Sderaadt 	if (explen > db->mru + 1) {
940df930be7Sderaadt 	    m_freem(mret);
941df930be7Sderaadt 	    if (db->debug) {
942df930be7Sderaadt 		printf("bsd_decomp%d: ran out of mru\n", db->unit);
943df930be7Sderaadt #ifdef DEBUG
944df930be7Sderaadt 		while ((cmp = cmp->m_next) != NULL)
945df930be7Sderaadt 		    len += cmp->m_len;
946df930be7Sderaadt 		printf("  len=%d, finchar=0x%x, codelen=%d, explen=%d\n",
947df930be7Sderaadt 		       len, finchar, codelen, explen);
948df930be7Sderaadt #endif
949df930be7Sderaadt 	    }
950df930be7Sderaadt 	    return DECOMP_FATALERROR;
951df930be7Sderaadt 	}
952df930be7Sderaadt 
953df930be7Sderaadt 	/*
954df930be7Sderaadt 	 * For simplicity, the decoded characters go in a single mbuf,
955df930be7Sderaadt 	 * so we allocate a single extra cluster mbuf if necessary.
956df930be7Sderaadt 	 */
957df930be7Sderaadt 	if ((space -= codelen + extra) < 0) {
958df930be7Sderaadt 	    dmp->m_len = wptr - mtod(dmp, u_char *);
959df930be7Sderaadt 	    MGET(m, M_DONTWAIT, MT_DATA);
960df930be7Sderaadt 	    if (m == NULL) {
961df930be7Sderaadt 		m_freem(mret);
962df930be7Sderaadt 		return DECOMP_ERROR;
963df930be7Sderaadt 	    }
964df930be7Sderaadt 	    m->m_len = 0;
965df930be7Sderaadt 	    m->m_next = NULL;
966df930be7Sderaadt 	    dmp->m_next = m;
967df930be7Sderaadt 	    MCLGET(m, M_DONTWAIT);
968b5b7f62eSclaudio 	    space = m_trailingspace(m) - (codelen + extra);
969df930be7Sderaadt 	    if (space < 0) {
970df930be7Sderaadt 		/* now that's what I call *compression*. */
971df930be7Sderaadt 		m_freem(mret);
972df930be7Sderaadt 		return DECOMP_ERROR;
973df930be7Sderaadt 	    }
974df930be7Sderaadt 	    dmp = m;
975df930be7Sderaadt 	    wptr = mtod(dmp, u_char *);
976df930be7Sderaadt 	}
977df930be7Sderaadt 
978df930be7Sderaadt 	/*
979df930be7Sderaadt 	 * Decode this code and install it in the decompressed buffer.
980df930be7Sderaadt 	 */
981df930be7Sderaadt 	p = (wptr += codelen);
982df930be7Sderaadt 	while (finchar > LAST) {
983df930be7Sderaadt 	    dictp = &db->dict[db->dict[finchar].cptr];
984df930be7Sderaadt #ifdef DEBUG
985df930be7Sderaadt 	    if (--codelen <= 0 || dictp->codem1 != finchar-1)
986df930be7Sderaadt 		goto bad;
987df930be7Sderaadt #endif
988df930be7Sderaadt 	    *--p = dictp->f.hs.suffix;
989df930be7Sderaadt 	    finchar = dictp->f.hs.prefix;
990df930be7Sderaadt 	}
991df930be7Sderaadt 	*--p = finchar;
992df930be7Sderaadt 
993df930be7Sderaadt #ifdef DEBUG
994df930be7Sderaadt 	if (--codelen != 0)
995df930be7Sderaadt 	    printf("bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n",
996df930be7Sderaadt 		   db->unit, codelen, incode, max_ent);
997df930be7Sderaadt #endif
998df930be7Sderaadt 
999df930be7Sderaadt 	if (extra)		/* the KwKwK case again */
1000df930be7Sderaadt 	    *wptr++ = finchar;
1001df930be7Sderaadt 
1002df930be7Sderaadt 	/*
1003df930be7Sderaadt 	 * If not first code in a packet, and
1004df930be7Sderaadt 	 * if not out of code space, then allocate a new code.
1005df930be7Sderaadt 	 *
1006df930be7Sderaadt 	 * Keep the hash table correct so it can be used
1007df930be7Sderaadt 	 * with uncompressed packets.
1008df930be7Sderaadt 	 */
1009df930be7Sderaadt 	if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
1010df930be7Sderaadt 	    struct bsd_dict *dictp2;
1011df930be7Sderaadt 	    u_int32_t fcode;
1012df930be7Sderaadt 	    u_int32_t hval, disp;
1013df930be7Sderaadt 
1014df930be7Sderaadt 	    fcode = BSD_KEY(oldcode,finchar);
1015df930be7Sderaadt 	    hval = BSD_HASH(oldcode,finchar,db->hshift);
1016df930be7Sderaadt 	    dictp = &db->dict[hval];
1017df930be7Sderaadt 
1018df930be7Sderaadt 	    /* look for a free hash table entry */
1019df930be7Sderaadt 	    if (dictp->codem1 < max_ent) {
1020df930be7Sderaadt 		disp = (hval == 0) ? 1 : hval;
1021df930be7Sderaadt 		do {
1022df930be7Sderaadt 		    hval += disp;
1023df930be7Sderaadt 		    if (hval >= db->hsize)
1024df930be7Sderaadt 			hval -= db->hsize;
1025df930be7Sderaadt 		    dictp = &db->dict[hval];
1026df930be7Sderaadt 		} while (dictp->codem1 < max_ent);
1027df930be7Sderaadt 	    }
1028df930be7Sderaadt 
1029df930be7Sderaadt 	    /*
1030df930be7Sderaadt 	     * Invalidate previous hash table entry
1031df930be7Sderaadt 	     * assigned this code, and then take it over
1032df930be7Sderaadt 	     */
1033df930be7Sderaadt 	    dictp2 = &db->dict[max_ent+1];
1034df930be7Sderaadt 	    if (db->dict[dictp2->cptr].codem1 == max_ent) {
1035df930be7Sderaadt 		db->dict[dictp2->cptr].codem1 = BADCODEM1;
1036df930be7Sderaadt 	    }
1037df930be7Sderaadt 	    dictp2->cptr = hval;
1038df930be7Sderaadt 	    dictp->codem1 = max_ent;
1039df930be7Sderaadt 	    dictp->f.fcode = fcode;
1040df930be7Sderaadt 
1041df930be7Sderaadt 	    db->max_ent = ++max_ent;
1042df930be7Sderaadt 	    db->lens[max_ent] = db->lens[oldcode]+1;
1043df930be7Sderaadt 
1044df930be7Sderaadt 	    /* Expand code size if needed. */
1045df930be7Sderaadt 	    if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
1046df930be7Sderaadt 		db->n_bits = ++n_bits;
1047df930be7Sderaadt 		tgtbitno = 32-n_bits;
1048df930be7Sderaadt 	    }
1049df930be7Sderaadt 	}
1050df930be7Sderaadt 	oldcode = incode;
1051df930be7Sderaadt     }
1052df930be7Sderaadt     dmp->m_len = wptr - mtod(dmp, u_char *);
1053df930be7Sderaadt 
1054df930be7Sderaadt     /*
1055df930be7Sderaadt      * Keep the checkpoint right so that incompressible packets
1056df930be7Sderaadt      * clear the dictionary at the right times.
1057df930be7Sderaadt      */
1058df930be7Sderaadt     db->bytes_out += ilen;
1059df930be7Sderaadt     db->in_count += explen;
1060df930be7Sderaadt     if (bsd_check(db) && db->debug) {
1061df930be7Sderaadt 	printf("bsd_decomp%d: peer should have cleared dictionary\n",
1062df930be7Sderaadt 	       db->unit);
1063df930be7Sderaadt     }
1064df930be7Sderaadt 
1065df930be7Sderaadt     ++db->comp_count;
1066df930be7Sderaadt     db->comp_bytes += ilen + BSD_OVHD;
1067df930be7Sderaadt     ++db->uncomp_count;
1068df930be7Sderaadt     db->uncomp_bytes += explen;
1069df930be7Sderaadt 
1070df930be7Sderaadt     *dmpp = mret;
1071df930be7Sderaadt     return DECOMP_OK;
1072df930be7Sderaadt 
1073df930be7Sderaadt #ifdef DEBUG
1074df930be7Sderaadt  bad:
1075df930be7Sderaadt     if (codelen <= 0) {
1076df930be7Sderaadt 	printf("bsd_decomp%d: fell off end of chain ", db->unit);
1077df930be7Sderaadt 	printf("0x%x at 0x%x by 0x%x, max_ent=0x%x\n",
1078df930be7Sderaadt 	       incode, finchar, db->dict[finchar].cptr, max_ent);
1079df930be7Sderaadt     } else if (dictp->codem1 != finchar-1) {
1080df930be7Sderaadt 	printf("bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",
1081df930be7Sderaadt 	       db->unit, incode, finchar);
1082df930be7Sderaadt 	printf("oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode,
1083df930be7Sderaadt 	       db->dict[finchar].cptr, dictp->codem1);
1084df930be7Sderaadt     }
1085df930be7Sderaadt     m_freem(mret);
1086df930be7Sderaadt     return DECOMP_FATALERROR;
1087df930be7Sderaadt #endif /* DEBUG */
1088df930be7Sderaadt }
1089df930be7Sderaadt #endif /* DO_BSD_COMPRESS */
1090