/* * ccp_pred1.c * * Copyright (c) 1997-1999 Whistle Communications, Inc. * All rights reserved. */ /* * pred1.c * * Test program for Dave Rand's rendition of the predictor algorithm * * Updated by: archie@freebsd.org (Archie Cobbs) * Updated by: iand@labtam.labtam.oz.au (Ian Donaldson) * Updated by: Carsten Bormann * Original : Dave Rand / */ #include "ppp.h" #include "ccp.h" /* * DEFINITIONS */ #define PRED1_COMP_BUF_SIZE 2048 #define PRED1_DECOMP_BUF_SIZE 1600 #define PRED1_MAX_BLOWUP(n) ((n) * 9 / 8 + 24) /* * The following hash code is the heart of the algorithm: * It builds a sliding hash sum of the previous 3-and-a-bit characters * which will be used to index the guess table. * A better hash function would result in additional compression, * at the expense of time. */ #define IHASH(x) p->iHash = (p->iHash << 4) ^ (x) #define OHASH(x) p->oHash = (p->oHash << 4) ^ (x) /* * INTERNAL FUNCTIONS */ static void Pred1Init(int direction); static void Pred1Cleanup(int direction); static Mbuf Pred1Compress(int proto, Mbuf bp); static Mbuf Pred1Decompress(Mbuf bp, int *proto); static u_char *Pred1BuildConfigReq(CompInfo comp, u_char *cp); static Mbuf Pred1RecvResetReq(int id, Mbuf bp); static Mbuf Pred1SendResetReq(void); static int Compress(u_char *source, u_char *dest, int len); static int Decompress(u_char *source, u_char *dest, int slen, int dlen); static void SyncTable(u_char *source, u_char *dest, int len); /* * GLOBAL VARIABLES */ const struct compinfo gCompPred1Info = { TY_PRED1, Pred1Init, Pred1Cleanup, Pred1Compress, Pred1Decompress, NULL, Pred1BuildConfigReq, NULL, Pred1RecvResetReq, Pred1SendResetReq, NULL, }; /* * Pred1Init() */ void Pred1Init(int directions) { Pred1Info p = &bund->ccp.pred1; if (directions & CCP_DIR_INPUT) { p->iHash = 0; if (p->InputGuessTable == NULL) p->InputGuessTable = Malloc(MB_COMP, PRED1_TABLE_SIZE); memset(p->InputGuessTable, 0, PRED1_TABLE_SIZE); } if (directions & CCP_DIR_OUTPUT) { p->oHash = 0; if (p->OutputGuessTable == NULL) p->OutputGuessTable = Malloc(MB_COMP, PRED1_TABLE_SIZE); memset(p->OutputGuessTable, 0, PRED1_TABLE_SIZE); } } /* * Pred1Cleanup() */ void Pred1Cleanup(int direction) { Pred1Info p = &bund->ccp.pred1; if (direction & CCP_DIR_INPUT) { assert(p->InputGuessTable); Freee(p->InputGuessTable); p->InputGuessTable = NULL; } if (direction & CCP_DIR_OUTPUT) { assert(p->OutputGuessTable); Freee(p->OutputGuessTable); p->OutputGuessTable = NULL; } } /* * Pred1Compress() * * Compress a packet and return a compressed version. * The original is untouched. */ Mbuf Pred1Compress(int proto, Mbuf uncomp) { Mbuf comp; u_char *cp, *wp, *hp; int orglen, len; u_char bufp[PRED1_COMP_BUF_SIZE]; u_short fcs; orglen = plength(uncomp) + 2; /* add count of proto */ comp = mballoc(MB_FRAME_OUT, PRED1_MAX_BLOWUP(orglen + 2)); comp->prio = uncomp->prio; hp = wp = MBDATA(comp); /* Stick in original length and protocol */ cp = bufp; *wp++ = *cp++ = orglen >> 8; *wp++ = *cp++ = orglen & 0377; *cp++ = proto >> 8; *cp++ = proto & 0xff; /* Copy data into buffer and compute FCS */ mbcopy(uncomp, cp, orglen - 2); fcs = Crc16(INITFCS, bufp, orglen + 2); fcs = ~fcs; /* Compress data */ len = Compress(bufp + 2, wp, orglen); #ifdef DEBUG Log(LG_CCP2, ("orglen (%d) --> len (%d)", orglen, len)); #endif /* What happened? */ if (len < orglen) { *hp |= 0x80; wp += len; } else { memcpy(wp, bufp + 2, orglen); wp += orglen; } /* Add FCS */ *wp++ = fcs & 0377; *wp++ = fcs >> 8; comp->cnt = wp - MBDATA(comp); /* Done */ return(comp); } /* * Pred1Decompress() * * Decompress packet, set *protop accordingly. Returns NULL if * packet failed to decompress. In any case, it doesn't alter "comp". */ Mbuf Pred1Decompress(Mbuf comp, int *protop) { u_char *cp, *pp; int len, olen, len1; Mbuf uncomp; u_char *bufp; u_short fcs, proto; /* "comp" is compressed data */ olen = plength(comp); cp = MBDATA(comp); /* "uncomp" is uncompressed data */ uncomp = mballoc(MB_IPIN, PRED1_DECOMP_BUF_SIZE); uncomp->prio = comp->prio; pp = bufp = MBDATA(uncomp); /* Get initial length value */ *pp++ = *cp & 0x7F; len = *cp++ << 8; *pp++ = *cp; len += *cp++; /* Is data compressed or not really? */ if (len & 0x8000) { len &= 0x7fff; len1 = Decompress(cp, pp, olen - 4, PRED1_DECOMP_BUF_SIZE); if (len != len1) /* Error is detected. Send reset request */ { PFREE(uncomp); return(NULL); } cp += olen - 4; pp += len1; } else { SyncTable(cp, pp, len); cp += len; pp += len; } /* Copy CRC value */ *pp++ = *cp++; /* CRC */ *pp++ = *cp++; /* Check CRC */ fcs = Crc16(INITFCS, bufp, (uncomp->cnt = pp - bufp)); #ifdef DEBUG if (fcs != GOODFCS) Log(LG_CCP2, ("fcs = %04x (%s), len = %x, olen = %x", fcs, (fcs == GOODFCS)? "good" : "bad", len, olen)); #endif if (fcs != GOODFCS) { PFREE(uncomp); return(NULL); } /* Get protocol */ uncomp->offset += 2; /* skip length */ uncomp->cnt -= 4; /* skip length & CRC */ pp = MBDATA(uncomp); proto = *pp++; if (proto & 1) { uncomp->offset++; uncomp->cnt--; } else { uncomp->offset += 2; uncomp->cnt -= 2; proto = (proto << 8) | *pp++; } /* Return decompressed packet */ *protop = proto; return(uncomp); } /* * Pred1RecvResetReq() */ static Mbuf Pred1RecvResetReq(int id, Mbuf bp) { Pred1Init(CCP_DIR_OUTPUT); return(NULL); } /* * Pred1SendResetReq() */ static Mbuf Pred1SendResetReq(void) { Pred1Init(CCP_DIR_INPUT); return(NULL); } /* * Pred1BuildConfigReq() */ static u_char * Pred1BuildConfigReq(CompInfo comp, u_char *cp) { return(FsmConfValue(cp, comp->proto, 0, NULL)); } /* * Compress() */ static int Compress(u_char *source, u_char *dest, int len) { Pred1Info p = &bund->ccp.pred1; int i, bitmask; u_char flags; u_char *flagdest, *orgdest; orgdest = dest; while (len) { flagdest = dest++; flags = 0; /* All guess wrong initially */ for (bitmask=1, i=0; i < 8 && len; i++, bitmask <<= 1) { if (p->OutputGuessTable[p->oHash] == *source) flags |= bitmask; /* Guess was right - don't output */ else { p->OutputGuessTable[p->oHash] = *source; *dest++ = *source; /* Guess wrong, output char */ } OHASH(*source++); len--; } *flagdest = flags; } return(dest - orgdest); } /* * Decompress() * * Returns decompressed size, or -1 if we ran out of space */ static int Decompress(u_char *source, u_char *dest, int slen, int dlen) { Pred1Info p = &bund->ccp.pred1; int i, bitmask; u_char flags, *orgdest; orgdest = dest; while (slen) { flags = *source++; slen--; for (i=0, bitmask = 1; i < 8; i++, bitmask <<= 1) { if (dlen <= 0) return(-1); if (flags & bitmask) *dest = p->InputGuessTable[p->iHash]; /* Guess correct */ else { if (!slen) break; /* we seem to be really done -- cabo */ p->InputGuessTable[p->iHash] = *source; /* Guess wrong */ *dest = *source++; /* Read from source */ slen--; } IHASH(*dest++); dlen--; } } return(dest - orgdest); } /* * SyncTable() */ static void SyncTable(u_char *source, u_char *dest, int len) { Pred1Info p = &bund->ccp.pred1; while (len--) { if (p->InputGuessTable[p->iHash] != *source) p->InputGuessTable[p->iHash] = *source; IHASH(*dest++ = *source++); } }