summaryrefslogtreecommitdiffstats
path: root/usr.sbin/ppp/deflate.c
diff options
context:
space:
mode:
authorbrian <brian@FreeBSD.org>1997-12-03 10:23:54 +0000
committerbrian <brian@FreeBSD.org>1997-12-03 10:23:54 +0000
commit4c9568e05af003a85ca40c896b5041fc8286221f (patch)
tree86301631e3a842883c7e61e9c2f1feab23a39e23 /usr.sbin/ppp/deflate.c
parent185fb48052f903a80b31786784cbb82fb059c35c (diff)
downloadFreeBSD-src-4c9568e05af003a85ca40c896b5041fc8286221f.zip
FreeBSD-src-4c9568e05af003a85ca40c896b5041fc8286221f.tar.gz
Abstract the CCP layer a level.
Add DEFLATE support.
Diffstat (limited to 'usr.sbin/ppp/deflate.c')
-rw-r--r--usr.sbin/ppp/deflate.c499
1 files changed, 499 insertions, 0 deletions
diff --git a/usr.sbin/ppp/deflate.c b/usr.sbin/ppp/deflate.c
new file mode 100644
index 0000000..a759288
--- /dev/null
+++ b/usr.sbin/ppp/deflate.c
@@ -0,0 +1,499 @@
+/*
+ * $Id$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "loadalias.h"
+#include "vars.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "lcpproto.h"
+#include "timer.h"
+#include "fsm.h"
+#include "deflate.h"
+
+/* Our state */
+struct deflate_state {
+ u_short seqno;
+ z_stream cx;
+};
+
+static int iWindowSize = 15;
+static int oWindowSize = 15;
+static struct deflate_state InputState, OutputState;
+static char garbage[10];
+static u_char EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff };
+
+#define DEFLATE_CHUNK_LEN 1024 /* Allocate mbufs this size */
+
+static void
+DeflateResetOutput(void)
+{
+ OutputState.seqno = 0;
+ deflateReset(&OutputState.cx);
+ LogPrintf(LogCCP, "Deflate: Output channel reset\n");
+}
+
+static int
+DeflateOutput(int pri, u_short proto, struct mbuf *mp)
+{
+ u_char *wp, *rp;
+ int olen, ilen, len, res, flush;
+ struct mbuf *mo_head, *mo, *mi_head, *mi;
+
+ ilen = plength(mp);
+ LogPrintf(LogDEBUG, "DeflateOutput: Proto %02x (%d bytes)\n", proto, ilen);
+ LogDumpBp(LogDEBUG, "DeflateOutput: Compress packet:", mp);
+
+ /* Stuff the protocol in front of the input */
+ mi_head = mi = mballoc(2, MB_HDLCOUT);
+ mi->next = mp;
+ rp = MBUF_CTOP(mi);
+ if (proto < 0x100) { /* Compress the protocol */
+ rp[0] = proto & 0377;
+ mi->cnt = 1;
+ } else { /* Don't compress the protocol */
+ rp[0] = proto >> 8;
+ rp[1] = proto & 0377;
+ mi->cnt = 2;
+ }
+
+ /* Allocate the initial output mbuf */
+ mo_head = mo = mballoc(DEFLATE_CHUNK_LEN, MB_HDLCOUT);
+ mo->cnt = 2;
+ wp = MBUF_CTOP(mo);
+ *wp++ = OutputState.seqno >> 8;
+ *wp++ = OutputState.seqno & 0377;
+ LogPrintf(LogDEBUG, "DeflateOutput: Seq %d\n", OutputState.seqno);
+ OutputState.seqno++;
+
+ /* Set up the deflation context */
+ OutputState.cx.next_out = wp;
+ OutputState.cx.avail_out = DEFLATE_CHUNK_LEN - 2;
+ OutputState.cx.next_in = MBUF_CTOP(mi);
+ OutputState.cx.avail_in = mi->cnt;
+ flush = Z_NO_FLUSH;
+
+ olen = 0;
+ while (1) {
+ if ((res = deflate(&OutputState.cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ LogPrintf(LogERROR, "DeflateOutput: deflate returned %d (%s)\n",
+ res, OutputState.cx.msg ? OutputState.cx.msg : "");
+ pfree(mo_head);
+ mbfree(mi_head);
+ OutputState.seqno--;
+ return 1; /* packet dropped */
+ }
+
+ if (flush == Z_SYNC_FLUSH && OutputState.cx.avail_out != 0)
+ break;
+
+ if (OutputState.cx.avail_in == 0 && mi->next != NULL) {
+ mi = mi->next;
+ OutputState.cx.next_in = MBUF_CTOP(mi);
+ OutputState.cx.avail_in = mi->cnt;
+ if (mi->next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (OutputState.cx.avail_out == 0) {
+ mo->next = mballoc(DEFLATE_CHUNK_LEN, MB_HDLCOUT);
+ olen += (mo->cnt = DEFLATE_CHUNK_LEN);
+ mo = mo->next;
+ mo->cnt = 0;
+ OutputState.cx.next_out = MBUF_CTOP(mo);
+ OutputState.cx.avail_out = DEFLATE_CHUNK_LEN;
+ }
+ }
+
+ olen += (mo->cnt = DEFLATE_CHUNK_LEN - OutputState.cx.avail_out);
+ olen -= 4; /* exclude the trailing EMPTY_BLOCK */
+
+ /*
+ * If the output packet (including seqno and excluding the EMPTY_BLOCK)
+ * got bigger, send the original - returning 0 to HdlcOutput() will
+ * continue to send ``mp''.
+ */
+ if (olen >= ilen) {
+ pfree(mo_head);
+ mbfree(mi_head);
+ LogPrintf(LogDEBUG, "DeflateOutput: %d => %d: Uncompressible (0x%04x)\n",
+ ilen, olen, proto);
+ CcpInfo.uncompout += ilen;
+ CcpInfo.compout += ilen; /* We measure this stuff too */
+ return 0;
+ }
+
+ pfree(mi_head);
+
+ /*
+ * Lose the last four bytes of our output.
+ * XXX: We should probably assert that these are the same as the
+ * contents of EMPTY_BLOCK.
+ */
+ for (mo = mo_head, len = mo->cnt; len < olen; mo = mo->next, len += mo->cnt)
+ ;
+ mo->cnt -= len - olen;
+ if (mo->next != NULL) {
+ pfree(mo->next);
+ mo->next = NULL;
+ }
+
+ CcpInfo.uncompout += ilen;
+ CcpInfo.compout += olen;
+
+ LogPrintf(LogDEBUG, "DeflateOutput: %d => %d bytes, proto 0x%04x\n",
+ ilen, olen, proto);
+
+ HdlcOutput(PRI_NORMAL, PROTO_COMPD, mo_head);
+ return 1;
+}
+
+static void
+DeflateResetInput(void)
+{
+ InputState.seqno = 0;
+ inflateReset(&InputState.cx);
+ LogPrintf(LogCCP, "Deflate: Input channel reset\n");
+}
+
+static struct mbuf *
+DeflateInput(u_short *proto, struct mbuf *mi)
+{
+ struct mbuf *mo, *mo_head, *mi_head;
+ u_char *wp;
+ int ilen, olen;
+ int seq, flush, res, first;
+ u_char hdr[2];
+
+ LogDumpBp(LogDEBUG, "DeflateInput: Decompress packet:", mi);
+ mi_head = mi = mbread(mi, hdr, 2);
+ ilen = 2;
+
+ /* Check the sequence number. */
+ seq = (hdr[0] << 8) + hdr[1];
+ LogPrintf(LogDEBUG, "DeflateInput: Seq %d\n", seq);
+ if (seq != InputState.seqno) {
+ LogPrintf(LogERROR, "DeflateInput: Seq error: Got %d, expected %d\n",
+ seq, InputState.seqno);
+ pfree(mi_head);
+ CcpSendResetReq(&CcpFsm);
+ return NULL;
+ }
+ InputState.seqno++;
+
+ /* Allocate an output mbuf */
+ mo_head = mo = mballoc(DEFLATE_CHUNK_LEN, MB_IPIN);
+
+ /* Our proto starts with 0 if it's compressed */
+ wp = MBUF_CTOP(mo);
+ wp[0] = '\0';
+
+ /*
+ * We set avail_out to 1 initially so we can look at the first
+ * byte of the output and decide whether we have a compressed
+ * proto field.
+ */
+ InputState.cx.next_in = MBUF_CTOP(mi);
+ InputState.cx.avail_in = mi->cnt;
+ InputState.cx.next_out = wp + 1;
+ InputState.cx.avail_out = 1;
+ ilen += mi->cnt;
+
+ flush = mi->next ? Z_NO_FLUSH : Z_SYNC_FLUSH;
+ first = 1;
+ olen = 0;
+
+ while (1) {
+ if ((res = inflate(&InputState.cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ LogPrintf(LogERROR, "DeflateInput: inflate returned %d (%s)\n",
+ res, InputState.cx.msg ? InputState.cx.msg : "");
+ pfree(mo_head);
+ pfree(mi);
+ CcpSendResetReq(&CcpFsm);
+ return NULL;
+ }
+
+ if (flush == Z_SYNC_FLUSH && InputState.cx.avail_out != 0)
+ break;
+
+ if (InputState.cx.avail_in == 0 && mi && (mi = mbfree(mi)) != NULL) {
+ /* underflow */
+ InputState.cx.next_in = MBUF_CTOP(mi);
+ ilen += (InputState.cx.avail_in = mi->cnt);
+ if (mi->next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (InputState.cx.avail_out == 0)
+ /* overflow */
+ if (first) {
+ if (!(wp[1] & 1)) {
+ /* 2 byte proto, shuffle it back in output */
+ wp[0] = wp[1];
+ InputState.cx.next_out--;
+ InputState.cx.avail_out = DEFLATE_CHUNK_LEN-1;
+ } else
+ InputState.cx.avail_out = DEFLATE_CHUNK_LEN-2;
+ first = 0;
+ } else {
+ olen += (mo->cnt = DEFLATE_CHUNK_LEN);
+ mo->next = mballoc(DEFLATE_CHUNK_LEN, MB_IPIN);
+ mo = mo->next;
+ InputState.cx.next_out = MBUF_CTOP(mo);
+ InputState.cx.avail_out = DEFLATE_CHUNK_LEN;
+ }
+ }
+
+ if (mi != NULL)
+ pfree(mi);
+
+ if (first) {
+ LogPrintf(LogERROR, "DeflateInput: Length error\n");
+ pfree(mo_head);
+ CcpSendResetReq(&CcpFsm);
+ return NULL;
+ }
+
+ olen += (mo->cnt = DEFLATE_CHUNK_LEN - InputState.cx.avail_out);
+
+ *proto = ((u_short)wp[0] << 8) | wp[1];
+ mo_head->offset += 2;
+ mo_head->cnt -= 2;
+ olen -= 2;
+
+ CcpInfo.compin += ilen;
+ CcpInfo.uncompin += olen;
+
+ LogPrintf(LogDEBUG, "DeflateInput: %d => %d bytes, proto 0x%04x\n",
+ ilen, olen, *proto);
+
+ /*
+ * Simulate an EMPTY_BLOCK so that our dictionary stays in sync.
+ * The peer will have silently removed this!
+ */
+ InputState.cx.next_out = garbage;
+ InputState.cx.avail_out = sizeof garbage;
+ InputState.cx.next_in = EMPTY_BLOCK;
+ InputState.cx.avail_in = sizeof EMPTY_BLOCK;
+ inflate(&InputState.cx, Z_SYNC_FLUSH);
+
+ return mo_head;
+}
+
+static void
+DeflateDictSetup(u_short proto, struct mbuf *mi)
+{
+ int res, flush;
+ u_char *rp;
+ struct mbuf *mi_head;
+ short len;
+
+ LogPrintf(LogDEBUG, "DeflateDictSetup: Got seq %d\n", InputState.seqno);
+
+ /*
+ * Stuff an ``uncompressed data'' block header followed by the
+ * protocol in front of the input
+ */
+ mi_head = mballoc(7, MB_HDLCOUT);
+ mi_head->next = mi;
+ len = plength(mi);
+ mi = mi_head;
+ rp = MBUF_CTOP(mi);
+ if (proto < 0x100) { /* Compress the protocol */
+ rp[5] = proto & 0377;
+ mi->cnt = 6;
+ len++;
+ } else { /* Don't compress the protocol */
+ rp[5] = proto >> 8;
+ rp[6] = proto & 0377;
+ mi->cnt = 7;
+ len += 2;
+ }
+ rp[0] = 0x80; /* BITS: 100xxxxx */
+ rp[1] = len & 0377; /* The length */
+ rp[2] = len >> 8;
+ rp[3] = (~len) & 0377; /* One's compliment of the length */
+ rp[4] = (~len) >> 8;
+
+ InputState.cx.next_in = rp;
+ InputState.cx.avail_in = mi->cnt;
+ InputState.cx.next_out = garbage;
+ InputState.cx.avail_out = sizeof garbage;
+ flush = Z_NO_FLUSH;
+
+ while (1) {
+ if ((res = inflate(&InputState.cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ LogPrintf(LogERROR, "DeflateDictSetup: inflate returned %d (%s)\n",
+ res, InputState.cx.msg ? InputState.cx.msg : "");
+ CcpSendResetReq(&CcpFsm);
+ mbfree(mi_head); /* lose our allocated ``head'' buf */
+ return;
+ }
+
+ if (flush == Z_SYNC_FLUSH && InputState.cx.avail_out != 0)
+ break;
+
+ if (InputState.cx.avail_in == 0 && mi && (mi = mi->next) != NULL) {
+ /* underflow */
+ InputState.cx.next_in = MBUF_CTOP(mi);
+ InputState.cx.avail_in = mi->cnt;
+ if (mi->next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (InputState.cx.avail_out == 0) {
+ /* overflow */
+ InputState.cx.next_out = garbage;
+ InputState.cx.avail_out = sizeof garbage;
+ }
+ }
+
+ CcpInfo.compin += len;
+ CcpInfo.uncompin += len;
+
+ InputState.seqno++;
+ mbfree(mi_head); /* lose our allocated ``head'' buf */
+}
+
+static const char *
+DeflateDispOpts(struct lcp_opt *o)
+{
+ static char disp[7];
+
+ sprintf(disp, "win %d", (o->data[0]>>4) + 8);
+ return disp;
+}
+
+static void
+DeflateGetOpts(struct lcp_opt *o)
+{
+ o->id = TY_DEFLATE;
+ o->len = 4;
+ o->data[1] = '\0';
+}
+
+static void
+DeflateGetInputOpts(struct lcp_opt *o)
+{
+ DeflateGetOpts(o);
+ o->data[0] = ((iWindowSize-8)<<4)+8;
+}
+
+static void
+DeflateGetOutputOpts(struct lcp_opt *o)
+{
+ DeflateGetOpts(o);
+ o->data[0] = ((oWindowSize-8)<<4)+8;
+}
+
+static int
+DeflateSetOpts(struct lcp_opt *o, int *sz)
+{
+ if (o->id != TY_DEFLATE || o->len != 4 ||
+ (o->data[0]&15) != 8 || o->data[1] != '\0') {
+ DeflateGetOpts(o);
+ return MODE_REJ;
+ }
+ *sz = (o->data[0] >> 4) + 8;
+ if (*sz > 15) {
+ *sz = 15;
+ DeflateGetOpts(o);
+ return MODE_NAK;
+ }
+
+ return MODE_ACK;
+}
+
+static int
+DeflateSetInputOpts(struct lcp_opt *o)
+{
+ return DeflateSetOpts(o, &iWindowSize);
+}
+
+static int
+DeflateSetOutputOpts(struct lcp_opt *o)
+{
+ return DeflateSetOpts(o, &oWindowSize);
+}
+
+static int
+DeflateInitInput(void)
+{
+ InputState.cx.zalloc = NULL;
+ InputState.cx.opaque = NULL;
+ InputState.cx.zfree = NULL;
+ InputState.cx.next_out = NULL;
+ if (inflateInit2(&InputState.cx, -iWindowSize) != Z_OK)
+ return 0;
+ DeflateResetInput();
+ return 1;
+}
+
+static int
+DeflateInitOutput(void)
+{
+ OutputState.cx.zalloc = NULL;
+ OutputState.cx.opaque = NULL;
+ OutputState.cx.zfree = NULL;
+ OutputState.cx.next_in = NULL;
+ if (deflateInit2(&OutputState.cx, Z_DEFAULT_COMPRESSION, 8,
+ -oWindowSize, 8, Z_DEFAULT_STRATEGY) != Z_OK)
+ return 0;
+ DeflateResetOutput();
+ return 1;
+}
+
+static void
+DeflateTermInput(void)
+{
+ iWindowSize = 15;
+ inflateEnd(&InputState.cx);
+}
+
+static void
+DeflateTermOutput(void)
+{
+ oWindowSize = 15;
+ deflateEnd(&OutputState.cx);
+}
+
+const struct ccp_algorithm DeflateAlgorithm = {
+ TY_DEFLATE,
+ ConfDeflate,
+ DeflateDispOpts,
+ {
+ DeflateGetInputOpts,
+ DeflateSetInputOpts,
+ DeflateInitInput,
+ DeflateTermInput,
+ DeflateResetInput,
+ DeflateInput,
+ DeflateDictSetup
+ },
+ {
+ DeflateGetOutputOpts,
+ DeflateSetOutputOpts,
+ DeflateInitOutput,
+ DeflateTermOutput,
+ DeflateResetOutput,
+ DeflateOutput
+ },
+};
OpenPOWER on IntegriCloud