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