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