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