424a69f4320ea3c9241400c25c982327e6e0f159
[ppp.git] / netbsd-1.1 / ppp-deflate.c
1 /*      $Id: ppp-deflate.c,v 1.7 1997/11/27 06:06:31 paulus Exp $       */
2
3 /*
4  * ppp_deflate.c - interface the zlib procedures for Deflate compression
5  * and decompression (as used by gzip) to the PPP code.
6  * This version is for use with mbufs on BSD-derived systems.
7  *
8  * Copyright (c) 1994 The Australian National University.
9  * All rights reserved.
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation is hereby granted, provided that the above copyright
13  * notice appears in all copies.  This software is provided without any
14  * warranty, express or implied. The Australian National University
15  * makes no representations about the suitability of this software for
16  * any purpose.
17  *
18  * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
19  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
20  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
21  * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
22  * OF SUCH DAMAGE.
23  *
24  * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
26  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
27  * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
28  * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
29  * OR MODIFICATIONS.
30  */
31
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/systm.h>
35 #include <sys/mbuf.h>
36 #include <net/ppp_defs.h>
37 #include <net/zlib.h>
38
39 #define PACKETPTR       struct mbuf *
40 #include <net/ppp-comp.h>
41
42 #if DO_DEFLATE
43
44 #define DEFLATE_DEBUG   1
45
46 /*
47  * State for a Deflate (de)compressor.
48  */
49 struct deflate_state {
50     int         seqno;
51     int         w_size;
52     int         unit;
53     int         hdrlen;
54     int         mru;
55     int         debug;
56     z_stream    strm;
57     struct compstat stats;
58 };
59
60 #define DEFLATE_OVHD    2               /* Deflate overhead/packet */
61
62 static void     *zalloc __P((void *, u_int items, u_int size));
63 static void     zfree __P((void *, void *ptr));
64 static void     *z_comp_alloc __P((u_char *options, int opt_len));
65 static void     *z_decomp_alloc __P((u_char *options, int opt_len));
66 static void     z_comp_free __P((void *state));
67 static void     z_decomp_free __P((void *state));
68 static int      z_comp_init __P((void *state, u_char *options, int opt_len,
69                                  int unit, int hdrlen, int debug));
70 static int      z_decomp_init __P((void *state, u_char *options, int opt_len,
71                                      int unit, int hdrlen, int mru, int debug));
72 static int      z_compress __P((void *state, struct mbuf **mret,
73                                   struct mbuf *mp, int slen, int maxolen));
74 static void     z_incomp __P((void *state, struct mbuf *dmsg));
75 static int      z_decompress __P((void *state, struct mbuf *cmp,
76                                     struct mbuf **dmpp));
77 static void     z_comp_reset __P((void *state));
78 static void     z_decomp_reset __P((void *state));
79 static void     z_comp_stats __P((void *state, struct compstat *stats));
80
81 /*
82  * Procedures exported to if_ppp.c.
83  */
84 struct compressor ppp_deflate = {
85     CI_DEFLATE,                 /* compress_proto */
86     z_comp_alloc,               /* comp_alloc */
87     z_comp_free,                /* comp_free */
88     z_comp_init,                /* comp_init */
89     z_comp_reset,               /* comp_reset */
90     z_compress,                 /* compress */
91     z_comp_stats,               /* comp_stat */
92     z_decomp_alloc,             /* decomp_alloc */
93     z_decomp_free,              /* decomp_free */
94     z_decomp_init,              /* decomp_init */
95     z_decomp_reset,             /* decomp_reset */
96     z_decompress,               /* decompress */
97     z_incomp,                   /* incomp */
98     z_comp_stats,               /* decomp_stat */
99 };
100
101 /*
102  * Space allocation and freeing routines for use by zlib routines.
103  */
104 void *
105 zalloc(notused, items, size)
106     void *notused;
107     u_int items, size;
108 {
109     void *ptr;
110
111     MALLOC(ptr, void *, items * size, M_DEVBUF, M_NOWAIT);
112     return ptr;
113 }
114
115 void
116 zfree(notused, ptr)
117     void *notused;
118     void *ptr;
119 {
120     FREE(ptr, M_DEVBUF);
121 }
122
123 /*
124  * Allocate space for a compressor.
125  */
126 static void *
127 z_comp_alloc(options, opt_len)
128     u_char *options;
129     int opt_len;
130 {
131     struct deflate_state *state;
132     int w_size;
133
134     if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE
135         || options[1] != CILEN_DEFLATE
136         || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
137         || options[3] != DEFLATE_CHK_SEQUENCE)
138         return NULL;
139     w_size = DEFLATE_SIZE(options[2]);
140     if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
141         return NULL;
142
143     MALLOC(state, struct deflate_state *, sizeof(struct deflate_state),
144            M_DEVBUF, M_NOWAIT);
145     if (state == NULL)
146         return NULL;
147
148     state->strm.next_in = NULL;
149     state->strm.zalloc = zalloc;
150     state->strm.zfree = zfree;
151     if (deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION, DEFLATE_METHOD_VAL,
152                      -w_size, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
153         FREE(state, M_DEVBUF);
154         return NULL;
155     }
156
157     state->w_size = w_size;
158     bzero(&state->stats, sizeof(state->stats));
159     return (void *) state;
160 }
161
162 static void
163 z_comp_free(arg)
164     void *arg;
165 {
166     struct deflate_state *state = (struct deflate_state *) arg;
167
168     deflateEnd(&state->strm);
169     FREE(state, M_DEVBUF);
170 }
171
172 static int
173 z_comp_init(arg, options, opt_len, unit, hdrlen, debug)
174     void *arg;
175     u_char *options;
176     int opt_len, unit, hdrlen, debug;
177 {
178     struct deflate_state *state = (struct deflate_state *) arg;
179
180     if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE
181         || options[1] != CILEN_DEFLATE
182         || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
183         || DEFLATE_SIZE(options[2]) != state->w_size
184         || options[3] != DEFLATE_CHK_SEQUENCE)
185         return 0;
186
187     state->seqno = 0;
188     state->unit = unit;
189     state->hdrlen = hdrlen;
190     state->debug = debug;
191
192     deflateReset(&state->strm);
193
194     return 1;
195 }
196
197 static void
198 z_comp_reset(arg)
199     void *arg;
200 {
201     struct deflate_state *state = (struct deflate_state *) arg;
202
203     state->seqno = 0;
204     deflateReset(&state->strm);
205 }
206
207 int
208 z_compress(arg, mret, mp, orig_len, maxolen)
209     void *arg;
210     struct mbuf **mret;         /* compressed packet (out) */
211     struct mbuf *mp;            /* uncompressed packet (in) */
212     int orig_len, maxolen;
213 {
214     struct deflate_state *state = (struct deflate_state *) arg;
215     u_char *rptr, *wptr;
216     int proto, olen, wspace, r, flush;
217     struct mbuf *m;
218
219     /*
220      * Check that the protocol is in the range we handle.
221      */
222     rptr = mtod(mp, u_char *);
223     proto = PPP_PROTOCOL(rptr);
224     if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) {
225         *mret = NULL;
226         return orig_len;
227     }
228
229     /* Allocate one mbuf initially. */
230     if (maxolen > orig_len)
231         maxolen = orig_len;
232     MGET(m, M_DONTWAIT, MT_DATA);
233     *mret = m;
234     if (m != NULL) {
235         m->m_len = 0;
236         if (maxolen + state->hdrlen > MLEN)
237             MCLGET(m, M_DONTWAIT);
238         wspace = M_TRAILINGSPACE(m);
239         if (state->hdrlen + PPP_HDRLEN + 2 < wspace) {
240             m->m_data += state->hdrlen;
241             wspace -= state->hdrlen;
242         }
243         wptr = mtod(m, u_char *);
244
245         /*
246          * Copy over the PPP header and store the 2-byte sequence number.
247          */
248         wptr[0] = PPP_ADDRESS(rptr);
249         wptr[1] = PPP_CONTROL(rptr);
250         wptr[2] = PPP_COMP >> 8;
251         wptr[3] = PPP_COMP;
252         wptr += PPP_HDRLEN;
253         wptr[0] = state->seqno >> 8;
254         wptr[1] = state->seqno;
255         wptr += 2;
256         state->strm.next_out = wptr;
257         state->strm.avail_out = wspace - (PPP_HDRLEN + 2);
258     } else {
259         state->strm.next_out = NULL;
260         state->strm.avail_out = 1000000;
261         wptr = NULL;
262         wspace = 0;
263     }
264     ++state->seqno;
265
266     rptr += (proto > 0xff)? 2: 3;       /* skip 1st proto byte if 0 */
267     state->strm.next_in = rptr;
268     state->strm.avail_in = mtod(mp, u_char *) + mp->m_len - rptr;
269     mp = mp->m_next;
270     flush = (mp == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH;
271     olen = 0;
272     for (;;) {
273         r = deflate(&state->strm, flush);
274         if (r != Z_OK) {
275             printf("z_compress: deflate returned %d (%s)\n",
276                    r, (state->strm.msg? state->strm.msg: ""));
277             break;
278         }
279         if (flush != Z_NO_FLUSH && state->strm.avail_out != 0)
280             break;              /* all done */
281         if (state->strm.avail_in == 0 && mp != NULL) {
282             state->strm.next_in = mtod(mp, u_char *);
283             state->strm.avail_in = mp->m_len;
284             mp = mp->m_next;
285             if (mp == NULL)
286                 flush = Z_PACKET_FLUSH;
287         }
288         if (state->strm.avail_out == 0) {
289             if (m != NULL) {
290                 m->m_len = wspace;
291                 olen += wspace;
292                 MGET(m->m_next, M_DONTWAIT, MT_DATA);
293                 m = m->m_next;
294                 if (m != NULL) {
295                     m->m_len = 0;
296                     if (maxolen - olen > MLEN)
297                         MCLGET(m, M_DONTWAIT);
298                     state->strm.next_out = mtod(m, u_char *);
299                     state->strm.avail_out = wspace = M_TRAILINGSPACE(m);
300                 }
301             }
302             if (m == NULL) {
303                 state->strm.next_out = NULL;
304                 state->strm.avail_out = 1000000;
305             }
306         }
307     }
308     if (m != NULL)
309         olen += (m->m_len = wspace - state->strm.avail_out);
310
311     /*
312      * See if we managed to reduce the size of the packet.
313      * If the compressor just gave us a single zero byte, it means
314      * the packet was incompressible.
315      */
316     if (m != NULL && olen < orig_len
317         && !(olen == PPP_HDRLEN + 3 && *wptr == 0)) {
318         state->stats.comp_bytes += olen;
319         state->stats.comp_packets++;
320     } else {
321         if (*mret != NULL) {
322             m_freem(*mret);
323             *mret = NULL;
324         }
325         state->stats.inc_bytes += orig_len;
326         state->stats.inc_packets++;
327         olen = orig_len;
328     }
329     state->stats.unc_bytes += orig_len;
330     state->stats.unc_packets++;
331
332     return olen;
333 }
334
335 static void
336 z_comp_stats(arg, stats)
337     void *arg;
338     struct compstat *stats;
339 {
340     struct deflate_state *state = (struct deflate_state *) arg;
341     u_int out;
342
343     *stats = state->stats;
344     stats->ratio = stats->unc_bytes;
345     out = stats->comp_bytes + stats->inc_bytes;
346     if (stats->ratio <= 0x7ffffff)
347         stats->ratio <<= 8;
348     else
349         out >>= 8;
350     if (out != 0)
351         stats->ratio /= out;
352 }
353
354 /*
355  * Allocate space for a decompressor.
356  */
357 static void *
358 z_decomp_alloc(options, opt_len)
359     u_char *options;
360     int opt_len;
361 {
362     struct deflate_state *state;
363     int w_size;
364
365     if (opt_len != CILEN_DEFLATE || options[0] != CI_DEFLATE
366         || options[1] != CILEN_DEFLATE
367         || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
368         || options[3] != DEFLATE_CHK_SEQUENCE)
369         return NULL;
370     w_size = DEFLATE_SIZE(options[2]);
371     if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
372         return NULL;
373
374     MALLOC(state, struct deflate_state *, sizeof(struct deflate_state),
375            M_DEVBUF, M_NOWAIT);
376     if (state == NULL)
377         return NULL;
378
379     state->strm.next_out = NULL;
380     state->strm.zalloc = zalloc;
381     state->strm.zfree = zfree;
382     if (inflateInit2(&state->strm, -w_size) != Z_OK) {
383         FREE(state, M_DEVBUF);
384         return NULL;
385     }
386
387     state->w_size = w_size;
388     bzero(&state->stats, sizeof(state->stats));
389     return (void *) state;
390 }
391
392 static void
393 z_decomp_free(arg)
394     void *arg;
395 {
396     struct deflate_state *state = (struct deflate_state *) arg;
397
398     inflateEnd(&state->strm);
399     FREE(state, M_DEVBUF);
400 }
401
402 static int
403 z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug)
404     void *arg;
405     u_char *options;
406     int opt_len, unit, hdrlen, mru, debug;
407 {
408     struct deflate_state *state = (struct deflate_state *) arg;
409
410     if (opt_len < CILEN_DEFLATE || options[0] != CI_DEFLATE
411         || options[1] != CILEN_DEFLATE
412         || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL
413         || DEFLATE_SIZE(options[2]) != state->w_size
414         || options[3] != DEFLATE_CHK_SEQUENCE)
415         return 0;
416
417     state->seqno = 0;
418     state->unit = unit;
419     state->hdrlen = hdrlen;
420     state->debug = debug;
421     state->mru = mru;
422
423     inflateReset(&state->strm);
424
425     return 1;
426 }
427
428 static void
429 z_decomp_reset(arg)
430     void *arg;
431 {
432     struct deflate_state *state = (struct deflate_state *) arg;
433
434     state->seqno = 0;
435     inflateReset(&state->strm);
436 }
437
438 /*
439  * Decompress a Deflate-compressed packet.
440  *
441  * Because of patent problems, we return DECOMP_ERROR for errors
442  * found by inspecting the input data and for system problems, but
443  * DECOMP_FATALERROR for any errors which could possibly be said to
444  * be being detected "after" decompression.  For DECOMP_ERROR,
445  * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
446  * infringing a patent of Motorola's if we do, so we take CCP down
447  * instead.
448  *
449  * Given that the frame has the correct sequence number and a good FCS,
450  * errors such as invalid codes in the input most likely indicate a
451  * bug, so we return DECOMP_FATALERROR for them in order to turn off
452  * compression, even though they are detected by inspecting the input.
453  */
454 int
455 z_decompress(arg, mi, mop)
456     void *arg;
457     struct mbuf *mi, **mop;
458 {
459     struct deflate_state *state = (struct deflate_state *) arg;
460     struct mbuf *mo, *mo_head;
461     u_char *rptr, *wptr;
462     int rlen, olen, ospace;
463     int seq, i, flush, r, decode_proto;
464     u_char hdr[PPP_HDRLEN + DEFLATE_OVHD];
465
466     *mop = NULL;
467     rptr = mtod(mi, u_char *);
468     rlen = mi->m_len;
469     for (i = 0; i < PPP_HDRLEN + DEFLATE_OVHD; ++i) {
470         while (rlen <= 0) {
471             mi = mi->m_next;
472             if (mi == NULL)
473                 return DECOMP_ERROR;
474             rptr = mtod(mi, u_char *);
475             rlen = mi->m_len;
476         }
477         hdr[i] = *rptr++;
478         --rlen;
479     }
480
481     /* Check the sequence number. */
482     seq = (hdr[PPP_HDRLEN] << 8) + hdr[PPP_HDRLEN+1];
483     if (seq != state->seqno) {
484         if (state->debug)
485             printf("z_decompress%d: bad seq # %d, expected %d\n",
486                    state->unit, seq, state->seqno);
487         return DECOMP_ERROR;
488     }
489     ++state->seqno;
490
491     /* Allocate an output mbuf. */
492     MGETHDR(mo, M_DONTWAIT, MT_DATA);
493     if (mo == NULL)
494         return DECOMP_ERROR;
495     mo_head = mo;
496     mo->m_len = 0;
497     mo->m_next = NULL;
498     MCLGET(mo, M_DONTWAIT);
499     ospace = M_TRAILINGSPACE(mo);
500     if (state->hdrlen + PPP_HDRLEN < ospace) {
501         mo->m_data += state->hdrlen;
502         ospace -= state->hdrlen;
503     }
504
505     /*
506      * Fill in the first part of the PPP header.  The protocol field
507      * comes from the decompressed data.
508      */
509     wptr = mtod(mo, u_char *);
510     wptr[0] = PPP_ADDRESS(hdr);
511     wptr[1] = PPP_CONTROL(hdr);
512     wptr[2] = 0;
513
514     /*
515      * Set up to call inflate.  We set avail_out to 1 initially so we can
516      * look at the first byte of the output and decide whether we have
517      * a 1-byte or 2-byte protocol field.
518      */
519     state->strm.next_in = rptr;
520     state->strm.avail_in = rlen;
521     mi = mi->m_next;
522     flush = (mi == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH;
523     rlen += PPP_HDRLEN + DEFLATE_OVHD;
524     state->strm.next_out = wptr + 3;
525     state->strm.avail_out = 1;
526     decode_proto = 1;
527     olen = PPP_HDRLEN;
528
529     /*
530      * Call inflate, supplying more input or output as needed.
531      */
532     for (;;) {
533         r = inflate(&state->strm, flush);
534         if (r != Z_OK) {
535 #if !DEFLATE_DEBUG
536             if (state->debug)
537 #endif
538                 printf("z_decompress%d: inflate returned %d (%s)\n",
539                        state->unit, r, (state->strm.msg? state->strm.msg: ""));
540             m_freem(mo_head);
541             return DECOMP_FATALERROR;
542         }
543         if (flush != Z_NO_FLUSH && state->strm.avail_out != 0)
544             break;              /* all done */
545         if (state->strm.avail_in == 0 && mi != NULL) {
546             state->strm.next_in = mtod(mi, u_char *);
547             state->strm.avail_in = mi->m_len;
548             rlen += mi->m_len;
549             mi = mi->m_next;
550             if (mi == NULL)
551                 flush = Z_PACKET_FLUSH;
552         }
553         if (state->strm.avail_out == 0) {
554             if (decode_proto) {
555                 state->strm.avail_out = ospace - PPP_HDRLEN;
556                 if ((wptr[3] & 1) == 0) {
557                     /* 2-byte protocol field */
558                     wptr[2] = wptr[3];
559                     --state->strm.next_out;
560                     ++state->strm.avail_out;
561                     --olen;
562                 }
563                 decode_proto = 0;
564             } else {
565                 mo->m_len = ospace;
566                 olen += ospace;
567                 MGET(mo->m_next, M_DONTWAIT, MT_DATA);
568                 mo = mo->m_next;
569                 if (mo == NULL) {
570                     m_freem(mo_head);
571                     return DECOMP_ERROR;
572                 }
573                 MCLGET(mo, M_DONTWAIT);
574                 state->strm.next_out = mtod(mo, u_char *);
575                 state->strm.avail_out = ospace = M_TRAILINGSPACE(mo);
576             }
577         }
578     }
579     if (decode_proto) {
580         m_freem(mo_head);
581         return DECOMP_ERROR;
582     }
583     olen += (mo->m_len = ospace - state->strm.avail_out);
584 #if DEFLATE_DEBUG
585     if (olen > state->mru + PPP_HDRLEN)
586         printf("ppp_deflate%d: exceeded mru (%d > %d)\n",
587                state->unit, olen, state->mru + PPP_HDRLEN);
588 #endif
589
590     state->stats.unc_bytes += olen;
591     state->stats.unc_packets++;
592     state->stats.comp_bytes += rlen;
593     state->stats.comp_packets++;
594
595     *mop = mo_head;
596     return DECOMP_OK;
597 }
598
599 /*
600  * Incompressible data has arrived - add it to the history.
601  */
602 static void
603 z_incomp(arg, mi)
604     void *arg;
605     struct mbuf *mi;
606 {
607     struct deflate_state *state = (struct deflate_state *) arg;
608     u_char *rptr;
609     int rlen, proto, r;
610
611     /*
612      * Check that the protocol is one we handle.
613      */
614     rptr = mtod(mi, u_char *);
615     proto = PPP_PROTOCOL(rptr);
616     if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
617         return;
618
619     ++state->seqno;
620
621     /*
622      * Iterate through the mbufs, adding the characters in them
623      * to the decompressor's history.  For the first mbuf, we start
624      * at the either the 1st or 2nd byte of the protocol field,
625      * depending on whether the protocol value is compressible.
626      */
627     rlen = mi->m_len;
628     state->strm.next_in = rptr + 3;
629     state->strm.avail_in = rlen - 3;
630     if (proto > 0xff) {
631         --state->strm.next_in;
632         ++state->strm.avail_in;
633     }
634     for (;;) {
635         r = inflateIncomp(&state->strm);
636         if (r != Z_OK) {
637             /* gak! */
638 #if !DEFLATE_DEBUG
639             if (state->debug)
640 #endif
641                 printf("z_incomp%d: inflateIncomp returned %d (%s)\n",
642                        state->unit, r, (state->strm.msg? state->strm.msg: ""));
643             return;
644         }
645         mi = mi->m_next;
646         if (mi == NULL)
647             break;
648         state->strm.next_in = mtod(mi, u_char *);
649         state->strm.avail_in = mi->m_len;
650         rlen += mi->m_len;
651     }
652
653     /*
654      * Update stats.
655      */
656     state->stats.inc_bytes += rlen;
657     state->stats.inc_packets++;
658     state->stats.unc_bytes += rlen;
659     state->stats.unc_packets++;
660 }
661
662 #endif /* DO_DEFLATE */