Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / net / ppp / ppp_deflate.c
diff --git a/kernel/drivers/net/ppp/ppp_deflate.c b/kernel/drivers/net/ppp/ppp_deflate.c
new file mode 100644 (file)
index 0000000..b5edc7f
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * ppp_deflate.c - interface the zlib procedures for Deflate compression
+ * and decompression (as used by gzip) to the PPP code.
+ *
+ * Copyright 1994-1998 Paul Mackerras.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <linux/ppp_defs.h>
+#include <linux/ppp-comp.h>
+
+#include <linux/zlib.h>
+#include <asm/unaligned.h>
+
+/*
+ * State for a Deflate (de)compressor.
+ */
+struct ppp_deflate_state {
+    int                seqno;
+    int                w_size;
+    int                unit;
+    int                mru;
+    int                debug;
+    z_stream   strm;
+    struct compstat stats;
+};
+
+#define DEFLATE_OVHD   2               /* Deflate overhead/packet */
+
+static void    *z_comp_alloc(unsigned char *options, int opt_len);
+static void    *z_decomp_alloc(unsigned char *options, int opt_len);
+static void    z_comp_free(void *state);
+static void    z_decomp_free(void *state);
+static int     z_comp_init(void *state, unsigned char *options,
+                                int opt_len,
+                                int unit, int hdrlen, int debug);
+static int     z_decomp_init(void *state, unsigned char *options,
+                                  int opt_len,
+                                  int unit, int hdrlen, int mru, int debug);
+static int     z_compress(void *state, unsigned char *rptr,
+                               unsigned char *obuf,
+                               int isize, int osize);
+static void    z_incomp(void *state, unsigned char *ibuf, int icnt);
+static int     z_decompress(void *state, unsigned char *ibuf,
+                               int isize, unsigned char *obuf, int osize);
+static void    z_comp_reset(void *state);
+static void    z_decomp_reset(void *state);
+static void    z_comp_stats(void *state, struct compstat *stats);
+
+/**
+ *     z_comp_free - free the memory used by a compressor
+ *     @arg:   pointer to the private state for the compressor.
+ */
+static void z_comp_free(void *arg)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       if (state) {
+               zlib_deflateEnd(&state->strm);
+               vfree(state->strm.workspace);
+               kfree(state);
+       }
+}
+
+/**
+ *     z_comp_alloc - allocate space for a compressor.
+ *     @options: pointer to CCP option data
+ *     @opt_len: length of the CCP option at @options.
+ *
+ *     The @options pointer points to the a buffer containing the
+ *     CCP option data for the compression being negotiated.  It is
+ *     formatted according to RFC1979, and describes the window
+ *     size that the peer is requesting that we use in compressing
+ *     data to be sent to it.
+ *
+ *     Returns the pointer to the private state for the compressor,
+ *     or NULL if we could not allocate enough memory.
+ */
+static void *z_comp_alloc(unsigned char *options, int opt_len)
+{
+       struct ppp_deflate_state *state;
+       int w_size;
+
+       if (opt_len != CILEN_DEFLATE ||
+           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+           options[1] != CILEN_DEFLATE ||
+           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+           options[3] != DEFLATE_CHK_SEQUENCE)
+               return NULL;
+       w_size = DEFLATE_SIZE(options[2]);
+       if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
+               return NULL;
+
+       state = kzalloc(sizeof(*state),
+                                                    GFP_KERNEL);
+       if (state == NULL)
+               return NULL;
+
+       state->strm.next_in   = NULL;
+       state->w_size         = w_size;
+       state->strm.workspace = vmalloc(zlib_deflate_workspacesize(-w_size, 8));
+       if (state->strm.workspace == NULL)
+               goto out_free;
+
+       if (zlib_deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION,
+                        DEFLATE_METHOD_VAL, -w_size, 8, Z_DEFAULT_STRATEGY)
+           != Z_OK)
+               goto out_free;
+       return (void *) state;
+
+out_free:
+       z_comp_free(state);
+       return NULL;
+}
+
+/**
+ *     z_comp_init - initialize a previously-allocated compressor.
+ *     @arg:   pointer to the private state for the compressor
+ *     @options: pointer to the CCP option data describing the
+ *             compression that was negotiated with the peer
+ *     @opt_len: length of the CCP option data at @options
+ *     @unit:  PPP unit number for diagnostic messages
+ *     @hdrlen: ignored (present for backwards compatibility)
+ *     @debug: debug flag; if non-zero, debug messages are printed.
+ *
+ *     The CCP options described by @options must match the options
+ *     specified when the compressor was allocated.  The compressor
+ *     history is reset.  Returns 0 for failure (CCP options don't
+ *     match) or 1 for success.
+ */
+static int z_comp_init(void *arg, unsigned char *options, int opt_len,
+                      int unit, int hdrlen, int debug)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       if (opt_len < CILEN_DEFLATE ||
+           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+           options[1] != CILEN_DEFLATE ||
+           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+           DEFLATE_SIZE(options[2]) != state->w_size ||
+           options[3] != DEFLATE_CHK_SEQUENCE)
+               return 0;
+
+       state->seqno = 0;
+       state->unit  = unit;
+       state->debug = debug;
+
+       zlib_deflateReset(&state->strm);
+
+       return 1;
+}
+
+/**
+ *     z_comp_reset - reset a previously-allocated compressor.
+ *     @arg:   pointer to private state for the compressor.
+ *
+ *     This clears the history for the compressor and makes it
+ *     ready to start emitting a new compressed stream.
+ */
+static void z_comp_reset(void *arg)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       state->seqno = 0;
+       zlib_deflateReset(&state->strm);
+}
+
+/**
+ *     z_compress - compress a PPP packet with Deflate compression.
+ *     @arg:   pointer to private state for the compressor
+ *     @rptr:  uncompressed packet (input)
+ *     @obuf:  compressed packet (output)
+ *     @isize: size of uncompressed packet
+ *     @osize: space available at @obuf
+ *
+ *     Returns the length of the compressed packet, or 0 if the
+ *     packet is incompressible.
+ */
+static int z_compress(void *arg, unsigned char *rptr, unsigned char *obuf,
+              int isize, int osize)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+       int r, proto, off, olen, oavail;
+       unsigned char *wptr;
+
+       /*
+        * Check that the protocol is in the range we handle.
+        */
+       proto = PPP_PROTOCOL(rptr);
+       if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+               return 0;
+
+       /* Don't generate compressed packets which are larger than
+          the uncompressed packet. */
+       if (osize > isize)
+               osize = isize;
+
+       wptr = obuf;
+
+       /*
+        * Copy over the PPP header and store the 2-byte sequence number.
+        */
+       wptr[0] = PPP_ADDRESS(rptr);
+       wptr[1] = PPP_CONTROL(rptr);
+       put_unaligned_be16(PPP_COMP, wptr + 2);
+       wptr += PPP_HDRLEN;
+       put_unaligned_be16(state->seqno, wptr);
+       wptr += DEFLATE_OVHD;
+       olen = PPP_HDRLEN + DEFLATE_OVHD;
+       state->strm.next_out = wptr;
+       state->strm.avail_out = oavail = osize - olen;
+       ++state->seqno;
+
+       off = (proto > 0xff) ? 2 : 3;   /* skip 1st proto byte if 0 */
+       rptr += off;
+       state->strm.next_in = rptr;
+       state->strm.avail_in = (isize - off);
+
+       for (;;) {
+               r = zlib_deflate(&state->strm, Z_PACKET_FLUSH);
+               if (r != Z_OK) {
+                       if (state->debug)
+                               printk(KERN_ERR
+                                      "z_compress: deflate returned %d\n", r);
+                       break;
+               }
+               if (state->strm.avail_out == 0) {
+                       olen += oavail;
+                       state->strm.next_out = NULL;
+                       state->strm.avail_out = oavail = 1000000;
+               } else {
+                       break;          /* all done */
+               }
+       }
+       olen += oavail - state->strm.avail_out;
+
+       /*
+        * See if we managed to reduce the size of the packet.
+        */
+       if (olen < isize && olen <= osize) {
+               state->stats.comp_bytes += olen;
+               state->stats.comp_packets++;
+       } else {
+               state->stats.inc_bytes += isize;
+               state->stats.inc_packets++;
+               olen = 0;
+       }
+       state->stats.unc_bytes += isize;
+       state->stats.unc_packets++;
+
+       return olen;
+}
+
+/**
+ *     z_comp_stats - return compression statistics for a compressor
+ *             or decompressor.
+ *     @arg:   pointer to private space for the (de)compressor
+ *     @stats: pointer to a struct compstat to receive the result.
+ */
+static void z_comp_stats(void *arg, struct compstat *stats)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       *stats = state->stats;
+}
+
+/**
+ *     z_decomp_free - Free the memory used by a decompressor.
+ *     @arg:   pointer to private space for the decompressor.
+ */
+static void z_decomp_free(void *arg)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       if (state) {
+               zlib_inflateEnd(&state->strm);
+               vfree(state->strm.workspace);
+               kfree(state);
+       }
+}
+
+/**
+ *     z_decomp_alloc - allocate space for a decompressor.
+ *     @options: pointer to CCP option data
+ *     @opt_len: length of the CCP option at @options.
+ *
+ *     The @options pointer points to the a buffer containing the
+ *     CCP option data for the compression being negotiated.  It is
+ *     formatted according to RFC1979, and describes the window
+ *     size that we are requesting the peer to use in compressing
+ *     data to be sent to us.
+ *
+ *     Returns the pointer to the private state for the decompressor,
+ *     or NULL if we could not allocate enough memory.
+ */
+static void *z_decomp_alloc(unsigned char *options, int opt_len)
+{
+       struct ppp_deflate_state *state;
+       int w_size;
+
+       if (opt_len != CILEN_DEFLATE ||
+           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+           options[1] != CILEN_DEFLATE ||
+           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+           options[3] != DEFLATE_CHK_SEQUENCE)
+               return NULL;
+       w_size = DEFLATE_SIZE(options[2]);
+       if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE)
+               return NULL;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (state == NULL)
+               return NULL;
+
+       state->w_size         = w_size;
+       state->strm.next_out  = NULL;
+       state->strm.workspace = vmalloc(zlib_inflate_workspacesize());
+       if (state->strm.workspace == NULL)
+               goto out_free;
+
+       if (zlib_inflateInit2(&state->strm, -w_size) != Z_OK)
+               goto out_free;
+       return (void *) state;
+
+out_free:
+       z_decomp_free(state);
+       return NULL;
+}
+
+/**
+ *     z_decomp_init - initialize a previously-allocated decompressor.
+ *     @arg:   pointer to the private state for the decompressor
+ *     @options: pointer to the CCP option data describing the
+ *             compression that was negotiated with the peer
+ *     @opt_len: length of the CCP option data at @options
+ *     @unit:  PPP unit number for diagnostic messages
+ *     @hdrlen: ignored (present for backwards compatibility)
+ *     @mru:   maximum length of decompressed packets
+ *     @debug: debug flag; if non-zero, debug messages are printed.
+ *
+ *     The CCP options described by @options must match the options
+ *     specified when the decompressor was allocated.  The decompressor
+ *     history is reset.  Returns 0 for failure (CCP options don't
+ *     match) or 1 for success.
+ */
+static int z_decomp_init(void *arg, unsigned char *options, int opt_len,
+                        int unit, int hdrlen, int mru, int debug)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       if (opt_len < CILEN_DEFLATE ||
+           (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) ||
+           options[1] != CILEN_DEFLATE ||
+           DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL ||
+           DEFLATE_SIZE(options[2]) != state->w_size ||
+           options[3] != DEFLATE_CHK_SEQUENCE)
+               return 0;
+
+       state->seqno = 0;
+       state->unit  = unit;
+       state->debug = debug;
+       state->mru   = mru;
+
+       zlib_inflateReset(&state->strm);
+
+       return 1;
+}
+
+/**
+ *     z_decomp_reset - reset a previously-allocated decompressor.
+ *     @arg:   pointer to private state for the decompressor.
+ *
+ *     This clears the history for the decompressor and makes it
+ *     ready to receive a new compressed stream.
+ */
+static void z_decomp_reset(void *arg)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+
+       state->seqno = 0;
+       zlib_inflateReset(&state->strm);
+}
+
+/**
+ *     z_decompress - decompress a Deflate-compressed packet.
+ *     @arg:   pointer to private state for the decompressor
+ *     @ibuf:  pointer to input (compressed) packet data
+ *     @isize: length of input packet
+ *     @obuf:  pointer to space for output (decompressed) packet
+ *     @osize: amount of space available at @obuf
+ *
+ * Because of patent problems, we return DECOMP_ERROR for errors
+ * found by inspecting the input data and for system problems, but
+ * DECOMP_FATALERROR for any errors which could possibly be said to
+ * be being detected "after" decompression.  For DECOMP_ERROR,
+ * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be
+ * infringing a patent of Motorola's if we do, so we take CCP down
+ * instead.
+ *
+ * Given that the frame has the correct sequence number and a good FCS,
+ * errors such as invalid codes in the input most likely indicate a
+ * bug, so we return DECOMP_FATALERROR for them in order to turn off
+ * compression, even though they are detected by inspecting the input.
+ */
+static int z_decompress(void *arg, unsigned char *ibuf, int isize,
+                unsigned char *obuf, int osize)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+       int olen, seq, r;
+       int decode_proto, overflow;
+       unsigned char overflow_buf[1];
+
+       if (isize <= PPP_HDRLEN + DEFLATE_OVHD) {
+               if (state->debug)
+                       printk(KERN_DEBUG "z_decompress%d: short pkt (%d)\n",
+                              state->unit, isize);
+               return DECOMP_ERROR;
+       }
+
+       /* Check the sequence number. */
+       seq = get_unaligned_be16(ibuf + PPP_HDRLEN);
+       if (seq != (state->seqno & 0xffff)) {
+               if (state->debug)
+                       printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n",
+                              state->unit, seq, state->seqno & 0xffff);
+               return DECOMP_ERROR;
+       }
+       ++state->seqno;
+
+       /*
+        * Fill in the first part of the PPP header.  The protocol field
+        * comes from the decompressed data.
+        */
+       obuf[0] = PPP_ADDRESS(ibuf);
+       obuf[1] = PPP_CONTROL(ibuf);
+       obuf[2] = 0;
+
+       /*
+        * Set up to call inflate.  We set avail_out to 1 initially so we can
+        * look at the first byte of the output and decide whether we have
+        * a 1-byte or 2-byte protocol field.
+        */
+       state->strm.next_in = ibuf + PPP_HDRLEN + DEFLATE_OVHD;
+       state->strm.avail_in = isize - (PPP_HDRLEN + DEFLATE_OVHD);
+       state->strm.next_out = obuf + 3;
+       state->strm.avail_out = 1;
+       decode_proto = 1;
+       overflow = 0;
+
+       /*
+        * Call inflate, supplying more input or output as needed.
+        */
+       for (;;) {
+               r = zlib_inflate(&state->strm, Z_PACKET_FLUSH);
+               if (r != Z_OK) {
+                       if (state->debug)
+                               printk(KERN_DEBUG "z_decompress%d: inflate returned %d (%s)\n",
+                                      state->unit, r, (state->strm.msg? state->strm.msg: ""));
+                       return DECOMP_FATALERROR;
+               }
+               if (state->strm.avail_out != 0)
+                       break;          /* all done */
+               if (decode_proto) {
+                       state->strm.avail_out = osize - PPP_HDRLEN;
+                       if ((obuf[3] & 1) == 0) {
+                               /* 2-byte protocol field */
+                               obuf[2] = obuf[3];
+                               --state->strm.next_out;
+                               ++state->strm.avail_out;
+                       }
+                       decode_proto = 0;
+               } else if (!overflow) {
+                       /*
+                        * We've filled up the output buffer; the only way to
+                        * find out whether inflate has any more characters
+                        * left is to give it another byte of output space.
+                        */
+                       state->strm.next_out = overflow_buf;
+                       state->strm.avail_out = 1;
+                       overflow = 1;
+               } else {
+                       if (state->debug)
+                               printk(KERN_DEBUG "z_decompress%d: ran out of mru\n",
+                                      state->unit);
+                       return DECOMP_FATALERROR;
+               }
+       }
+
+       if (decode_proto) {
+               if (state->debug)
+                       printk(KERN_DEBUG "z_decompress%d: didn't get proto\n",
+                              state->unit);
+               return DECOMP_ERROR;
+       }
+
+       olen = osize + overflow - state->strm.avail_out;
+       state->stats.unc_bytes += olen;
+       state->stats.unc_packets++;
+       state->stats.comp_bytes += isize;
+       state->stats.comp_packets++;
+
+       return olen;
+}
+
+/**
+ *     z_incomp - add incompressible input data to the history.
+ *     @arg:   pointer to private state for the decompressor
+ *     @ibuf:  pointer to input packet data
+ *     @icnt:  length of input data.
+ */
+static void z_incomp(void *arg, unsigned char *ibuf, int icnt)
+{
+       struct ppp_deflate_state *state = (struct ppp_deflate_state *) arg;
+       int proto, r;
+
+       /*
+        * Check that the protocol is one we handle.
+        */
+       proto = PPP_PROTOCOL(ibuf);
+       if (proto > 0x3fff || proto == 0xfd || proto == 0xfb)
+               return;
+
+       ++state->seqno;
+
+       /*
+        * We start at the either the 1st or 2nd byte of the protocol field,
+        * depending on whether the protocol value is compressible.
+        */
+       state->strm.next_in = ibuf + 3;
+       state->strm.avail_in = icnt - 3;
+       if (proto > 0xff) {
+               --state->strm.next_in;
+               ++state->strm.avail_in;
+       }
+
+       r = zlib_inflateIncomp(&state->strm);
+       if (r != Z_OK) {
+               /* gak! */
+               if (state->debug) {
+                       printk(KERN_DEBUG "z_incomp%d: inflateIncomp returned %d (%s)\n",
+                              state->unit, r, (state->strm.msg? state->strm.msg: ""));
+               }
+               return;
+       }
+
+       /*
+        * Update stats.
+        */
+       state->stats.inc_bytes += icnt;
+       state->stats.inc_packets++;
+       state->stats.unc_bytes += icnt;
+       state->stats.unc_packets++;
+}
+
+/*************************************************************
+ * Module interface table
+ *************************************************************/
+
+/* These are in ppp_generic.c */
+extern int  ppp_register_compressor   (struct compressor *cp);
+extern void ppp_unregister_compressor (struct compressor *cp);
+
+/*
+ * Procedures exported to if_ppp.c.
+ */
+static struct compressor ppp_deflate = {
+       .compress_proto =       CI_DEFLATE,
+       .comp_alloc =           z_comp_alloc,
+       .comp_free =            z_comp_free,
+       .comp_init =            z_comp_init,
+       .comp_reset =           z_comp_reset,
+       .compress =             z_compress,
+       .comp_stat =            z_comp_stats,
+       .decomp_alloc =         z_decomp_alloc,
+       .decomp_free =          z_decomp_free,
+       .decomp_init =          z_decomp_init,
+       .decomp_reset =         z_decomp_reset,
+       .decompress =           z_decompress,
+       .incomp =               z_incomp,
+       .decomp_stat =          z_comp_stats,
+       .owner =                THIS_MODULE
+};
+
+static struct compressor ppp_deflate_draft = {
+       .compress_proto =       CI_DEFLATE_DRAFT,
+       .comp_alloc =           z_comp_alloc,
+       .comp_free =            z_comp_free,
+       .comp_init =            z_comp_init,
+       .comp_reset =           z_comp_reset,
+       .compress =             z_compress,
+       .comp_stat =            z_comp_stats,
+       .decomp_alloc =         z_decomp_alloc,
+       .decomp_free =          z_decomp_free,
+       .decomp_init =          z_decomp_init,
+       .decomp_reset =         z_decomp_reset,
+       .decompress =           z_decompress,
+       .incomp =               z_incomp,
+       .decomp_stat =          z_comp_stats,
+       .owner =                THIS_MODULE
+};
+
+static int __init deflate_init(void)
+{
+        int answer = ppp_register_compressor(&ppp_deflate);
+        if (answer == 0)
+                printk(KERN_INFO
+                      "PPP Deflate Compression module registered\n");
+       ppp_register_compressor(&ppp_deflate_draft);
+        return answer;
+}
+
+static void __exit deflate_cleanup(void)
+{
+       ppp_unregister_compressor(&ppp_deflate);
+       ppp_unregister_compressor(&ppp_deflate_draft);
+}
+
+module_init(deflate_init);
+module_exit(deflate_cleanup);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE));
+MODULE_ALIAS("ppp-compress-" __stringify(CI_DEFLATE_DRAFT));