xref: /freebsd/usr.sbin/ppp/pred.c (revision dd7e261079699ca066fd8acaaf3ed460e5de61a1)
11ae349f5Scvs2svn /*-
21ae349f5Scvs2svn  * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
31ae349f5Scvs2svn  *                    Ian Donaldson <iand@labtam.labtam.oz.au>
41ae349f5Scvs2svn  *                    Carsten Bormann <cabo@cs.tu-berlin.de>
51ae349f5Scvs2svn  *                    Dave Rand <dlr@bungi.com>/<dave_rand@novell.com>
61ae349f5Scvs2svn  * All rights reserved.
71ae349f5Scvs2svn  *
81ae349f5Scvs2svn  * Redistribution and use in source and binary forms, with or without
91ae349f5Scvs2svn  * modification, are permitted provided that the following conditions
101ae349f5Scvs2svn  * are met:
111ae349f5Scvs2svn  * 1. Redistributions of source code must retain the above copyright
121ae349f5Scvs2svn  *    notice, this list of conditions and the following disclaimer.
131ae349f5Scvs2svn  * 2. Redistributions in binary form must reproduce the above copyright
141ae349f5Scvs2svn  *    notice, this list of conditions and the following disclaimer in the
151ae349f5Scvs2svn  *    documentation and/or other materials provided with the distribution.
161ae349f5Scvs2svn  *
171ae349f5Scvs2svn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
181ae349f5Scvs2svn  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191ae349f5Scvs2svn  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201ae349f5Scvs2svn  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
211ae349f5Scvs2svn  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221ae349f5Scvs2svn  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231ae349f5Scvs2svn  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241ae349f5Scvs2svn  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251ae349f5Scvs2svn  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261ae349f5Scvs2svn  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271ae349f5Scvs2svn  * SUCH DAMAGE.
281ae349f5Scvs2svn  *
29dd7e2610SBrian Somers  *	$Id: pred.c,v 1.20.2.11 1998/04/25 00:09:14 brian Exp $
301ae349f5Scvs2svn  */
311ae349f5Scvs2svn 
322764b86aSBrian Somers #include <sys/types.h>
331ae349f5Scvs2svn 
341ae349f5Scvs2svn #include <stdlib.h>
351ae349f5Scvs2svn #include <string.h>
361ae349f5Scvs2svn 
371ae349f5Scvs2svn #include "mbuf.h"
381ae349f5Scvs2svn #include "log.h"
391ae349f5Scvs2svn #include "timer.h"
401ae349f5Scvs2svn #include "fsm.h"
41879ed6faSBrian Somers #include "lqr.h"
421ae349f5Scvs2svn #include "hdlc.h"
431ae349f5Scvs2svn #include "lcp.h"
441ae349f5Scvs2svn #include "ccp.h"
451ae349f5Scvs2svn #include "pred.h"
461ae349f5Scvs2svn 
471ae349f5Scvs2svn /* The following hash code is the heart of the algorithm:
481ae349f5Scvs2svn  * It builds a sliding hash sum of the previous 3-and-a-bit characters
491ae349f5Scvs2svn  * which will be used to index the guess table.
501ae349f5Scvs2svn  * A better hash function would result in additional compression,
511ae349f5Scvs2svn  * at the expense of time.
521ae349f5Scvs2svn  */
5303036ec7SBrian Somers #define HASH(state, x) state->hash = (state->hash << 4) ^ (x)
541ae349f5Scvs2svn #define GUESS_TABLE_SIZE 65536
551ae349f5Scvs2svn 
5603036ec7SBrian Somers struct pred1_state {
5703036ec7SBrian Somers   u_short hash;
5803036ec7SBrian Somers   u_char dict[GUESS_TABLE_SIZE];
5903036ec7SBrian Somers };
601ae349f5Scvs2svn 
611ae349f5Scvs2svn static int
6203036ec7SBrian Somers compress(struct pred1_state *state, u_char *source, u_char *dest, int len)
631ae349f5Scvs2svn {
641ae349f5Scvs2svn   int i, bitmask;
651ae349f5Scvs2svn   unsigned char *flagdest, flags, *orgdest;
661ae349f5Scvs2svn 
671ae349f5Scvs2svn   orgdest = dest;
681ae349f5Scvs2svn   while (len) {
691ae349f5Scvs2svn     flagdest = dest++;
701ae349f5Scvs2svn     flags = 0;			/* All guess wrong initially */
711ae349f5Scvs2svn     for (bitmask = 1, i = 0; i < 8 && len; i++, bitmask <<= 1) {
7203036ec7SBrian Somers       if (state->dict[state->hash] == *source) {
731ae349f5Scvs2svn 	flags |= bitmask;	/* Guess was right - don't output */
741ae349f5Scvs2svn       } else {
7503036ec7SBrian Somers 	state->dict[state->hash] = *source;
761ae349f5Scvs2svn 	*dest++ = *source;	/* Guess wrong, output char */
771ae349f5Scvs2svn       }
7803036ec7SBrian Somers       HASH(state, *source++);
791ae349f5Scvs2svn       len--;
801ae349f5Scvs2svn     }
811ae349f5Scvs2svn     *flagdest = flags;
821ae349f5Scvs2svn   }
831ae349f5Scvs2svn   return (dest - orgdest);
841ae349f5Scvs2svn }
851ae349f5Scvs2svn 
861ae349f5Scvs2svn static void
8703036ec7SBrian Somers SyncTable(struct pred1_state *state, u_char * source, u_char * dest, int len)
881ae349f5Scvs2svn {
891ae349f5Scvs2svn   while (len--) {
9003036ec7SBrian Somers     if (state->dict[state->hash] != *source)
9103036ec7SBrian Somers       state->dict[state->hash] = *source;
9203036ec7SBrian Somers     HASH(state, *dest++ = *source++);
931ae349f5Scvs2svn   }
941ae349f5Scvs2svn }
951ae349f5Scvs2svn 
961ae349f5Scvs2svn static int
9703036ec7SBrian Somers decompress(struct pred1_state *state, u_char * source, u_char * dest, int len)
981ae349f5Scvs2svn {
991ae349f5Scvs2svn   int i, bitmask;
1001ae349f5Scvs2svn   unsigned char flags, *orgdest;
1011ae349f5Scvs2svn 
1021ae349f5Scvs2svn   orgdest = dest;
1031ae349f5Scvs2svn   while (len) {
1041ae349f5Scvs2svn     flags = *source++;
1051ae349f5Scvs2svn     len--;
1061ae349f5Scvs2svn     for (i = 0, bitmask = 1; i < 8; i++, bitmask <<= 1) {
1071ae349f5Scvs2svn       if (flags & bitmask) {
10803036ec7SBrian Somers 	*dest = state->dict[state->hash];	/* Guess correct */
1091ae349f5Scvs2svn       } else {
1101ae349f5Scvs2svn 	if (!len)
1111ae349f5Scvs2svn 	  break;		/* we seem to be really done -- cabo */
11203036ec7SBrian Somers 	state->dict[state->hash] = *source;	/* Guess wrong */
1131ae349f5Scvs2svn 	*dest = *source++;	/* Read from source */
1141ae349f5Scvs2svn 	len--;
1151ae349f5Scvs2svn       }
11603036ec7SBrian Somers       HASH(state, *dest++);
1171ae349f5Scvs2svn     }
1181ae349f5Scvs2svn   }
1191ae349f5Scvs2svn   return (dest - orgdest);
1201ae349f5Scvs2svn }
1211ae349f5Scvs2svn 
1221ae349f5Scvs2svn static void
12303036ec7SBrian Somers Pred1Term(void *v)
1241ae349f5Scvs2svn {
12503036ec7SBrian Somers   struct pred1_state *state = (struct pred1_state *)v;
12603036ec7SBrian Somers   free(state);
1271ae349f5Scvs2svn }
1281ae349f5Scvs2svn 
1291ae349f5Scvs2svn static void
13003036ec7SBrian Somers Pred1ResetInput(void *v)
1311ae349f5Scvs2svn {
13203036ec7SBrian Somers   struct pred1_state *state = (struct pred1_state *)v;
13303036ec7SBrian Somers   state->hash = 0;
13403036ec7SBrian Somers   memset(state->dict, '\0', sizeof state->dict);
135dd7e2610SBrian Somers   log_Printf(LogCCP, "Predictor1: Input channel reset\n");
1361ae349f5Scvs2svn }
1371ae349f5Scvs2svn 
1381ae349f5Scvs2svn static void
13903036ec7SBrian Somers Pred1ResetOutput(void *v)
1401ae349f5Scvs2svn {
14103036ec7SBrian Somers   struct pred1_state *state = (struct pred1_state *)v;
14203036ec7SBrian Somers   state->hash = 0;
14303036ec7SBrian Somers   memset(state->dict, '\0', sizeof state->dict);
144dd7e2610SBrian Somers   log_Printf(LogCCP, "Predictor1: Output channel reset\n");
1451ae349f5Scvs2svn }
1461ae349f5Scvs2svn 
14703036ec7SBrian Somers static void *
14803036ec7SBrian Somers Pred1InitInput(struct lcp_opt *o)
1491ae349f5Scvs2svn {
15003036ec7SBrian Somers   struct pred1_state *state;
15103036ec7SBrian Somers   state = (struct pred1_state *)malloc(sizeof(struct pred1_state));
15203036ec7SBrian Somers   if (state != NULL)
15303036ec7SBrian Somers     Pred1ResetInput(state);
15403036ec7SBrian Somers   return state;
15503036ec7SBrian Somers }
15603036ec7SBrian Somers 
15703036ec7SBrian Somers static void *
15803036ec7SBrian Somers Pred1InitOutput(struct lcp_opt *o)
15903036ec7SBrian Somers {
16003036ec7SBrian Somers   struct pred1_state *state;
16103036ec7SBrian Somers   state = (struct pred1_state *)malloc(sizeof(struct pred1_state));
16203036ec7SBrian Somers   if (state != NULL)
16303036ec7SBrian Somers     Pred1ResetOutput(state);
16403036ec7SBrian Somers   return state;
1651ae349f5Scvs2svn }
1661ae349f5Scvs2svn 
1671ae349f5Scvs2svn static int
16803036ec7SBrian Somers Pred1Output(void *v, struct ccp *ccp, struct link *l, int pri, u_short proto,
169f4768038SBrian Somers             struct mbuf *bp)
1701ae349f5Scvs2svn {
17103036ec7SBrian Somers   struct pred1_state *state = (struct pred1_state *)v;
1721ae349f5Scvs2svn   struct mbuf *mwp;
1731ae349f5Scvs2svn   u_char *cp, *wp, *hp;
1741ae349f5Scvs2svn   int orglen, len;
1751ae349f5Scvs2svn   u_char bufp[MAX_MTU + 2];
1761ae349f5Scvs2svn   u_short fcs;
1771ae349f5Scvs2svn 
178dd7e2610SBrian Somers   orglen = mbuf_Length(bp) + 2;	/* add count of proto */
179dd7e2610SBrian Somers   mwp = mbuf_Alloc((orglen + 2) / 8 * 9 + 12, MB_HDLCOUT);
1801ae349f5Scvs2svn   hp = wp = MBUF_CTOP(mwp);
1811ae349f5Scvs2svn   cp = bufp;
1821ae349f5Scvs2svn   *wp++ = *cp++ = orglen >> 8;
1831ae349f5Scvs2svn   *wp++ = *cp++ = orglen & 0377;
1841ae349f5Scvs2svn   *cp++ = proto >> 8;
1851ae349f5Scvs2svn   *cp++ = proto & 0377;
186dd7e2610SBrian Somers   mbuf_Read(bp, cp, orglen - 2);
187dd7e2610SBrian Somers   fcs = hdlc_Fcs(INITFCS, bufp, 2 + orglen);
1881ae349f5Scvs2svn   fcs = ~fcs;
1891ae349f5Scvs2svn 
19003036ec7SBrian Somers   len = compress(state, bufp + 2, wp, orglen);
191dd7e2610SBrian Somers   log_Printf(LogDEBUG, "Pred1Output: orglen (%d) --> len (%d)\n", orglen, len);
192f4768038SBrian Somers   ccp->uncompout += orglen;
1931ae349f5Scvs2svn   if (len < orglen) {
1941ae349f5Scvs2svn     *hp |= 0x80;
1951ae349f5Scvs2svn     wp += len;
196f4768038SBrian Somers     ccp->compout += len;
1971ae349f5Scvs2svn   } else {
1981ae349f5Scvs2svn     memcpy(wp, bufp + 2, orglen);
1991ae349f5Scvs2svn     wp += orglen;
200f4768038SBrian Somers     ccp->compout += orglen;
2011ae349f5Scvs2svn   }
2021ae349f5Scvs2svn 
2031ae349f5Scvs2svn   *wp++ = fcs & 0377;
2041ae349f5Scvs2svn   *wp++ = fcs >> 8;
2051ae349f5Scvs2svn   mwp->cnt = wp - MBUF_CTOP(mwp);
206dd7e2610SBrian Somers   hdlc_Output(l, PRI_NORMAL, ccp_Proto(ccp), mwp);
2071ae349f5Scvs2svn   return 1;
2081ae349f5Scvs2svn }
2091ae349f5Scvs2svn 
2101ae349f5Scvs2svn static struct mbuf *
21103036ec7SBrian Somers Pred1Input(void *v, struct ccp *ccp, u_short *proto, struct mbuf *bp)
2121ae349f5Scvs2svn {
21303036ec7SBrian Somers   struct pred1_state *state = (struct pred1_state *)v;
2141ae349f5Scvs2svn   u_char *cp, *pp;
2151ae349f5Scvs2svn   int len, olen, len1;
2161ae349f5Scvs2svn   struct mbuf *wp;
2171ae349f5Scvs2svn   u_char *bufp;
2181ae349f5Scvs2svn   u_short fcs;
2191ae349f5Scvs2svn 
220dd7e2610SBrian Somers   wp = mbuf_Alloc(MAX_MTU + 2, MB_IPIN);
2211ae349f5Scvs2svn   cp = MBUF_CTOP(bp);
222dd7e2610SBrian Somers   olen = mbuf_Length(bp);
2231ae349f5Scvs2svn   pp = bufp = MBUF_CTOP(wp);
2241ae349f5Scvs2svn   *pp++ = *cp & 0177;
2251ae349f5Scvs2svn   len = *cp++ << 8;
2261ae349f5Scvs2svn   *pp++ = *cp;
2271ae349f5Scvs2svn   len += *cp++;
228f4768038SBrian Somers   ccp->uncompin += len & 0x7fff;
2291ae349f5Scvs2svn   if (len & 0x8000) {
23003036ec7SBrian Somers     len1 = decompress(state, cp, pp, olen - 4);
231f4768038SBrian Somers     ccp->compin += olen;
2321ae349f5Scvs2svn     len &= 0x7fff;
2331ae349f5Scvs2svn     if (len != len1) {		/* Error is detected. Send reset request */
234dd7e2610SBrian Somers       log_Printf(LogCCP, "Pred1: Length error\n");
235dd7e2610SBrian Somers       ccp_SendResetReq(&ccp->fsm);
236dd7e2610SBrian Somers       mbuf_Free(bp);
237dd7e2610SBrian Somers       mbuf_Free(wp);
2381ae349f5Scvs2svn       return NULL;
2391ae349f5Scvs2svn     }
2401ae349f5Scvs2svn     cp += olen - 4;
2411ae349f5Scvs2svn     pp += len1;
2421ae349f5Scvs2svn   } else {
243f4768038SBrian Somers     ccp->compin += len;
24403036ec7SBrian Somers     SyncTable(state, cp, pp, len);
2451ae349f5Scvs2svn     cp += len;
2461ae349f5Scvs2svn     pp += len;
2471ae349f5Scvs2svn   }
2481ae349f5Scvs2svn   *pp++ = *cp++;		/* CRC */
2491ae349f5Scvs2svn   *pp++ = *cp++;
250dd7e2610SBrian Somers   fcs = hdlc_Fcs(INITFCS, bufp, wp->cnt = pp - bufp);
2511ae349f5Scvs2svn   if (fcs != GOODFCS)
252dd7e2610SBrian Somers     log_Printf(LogDEBUG, "Pred1Input: fcs = 0x%04x (%s), len = 0x%x,"
2531ae349f5Scvs2svn 	      " olen = 0x%x\n", fcs, (fcs == GOODFCS) ? "good" : "bad",
2541ae349f5Scvs2svn 	      len, olen);
2551ae349f5Scvs2svn   if (fcs == GOODFCS) {
2561ae349f5Scvs2svn     wp->offset += 2;		/* skip length */
2571ae349f5Scvs2svn     wp->cnt -= 4;		/* skip length & CRC */
2581ae349f5Scvs2svn     pp = MBUF_CTOP(wp);
2591ae349f5Scvs2svn     *proto = *pp++;
2601ae349f5Scvs2svn     if (*proto & 1) {
2611ae349f5Scvs2svn       wp->offset++;
2621ae349f5Scvs2svn       wp->cnt--;
2631ae349f5Scvs2svn     } else {
2641ae349f5Scvs2svn       wp->offset += 2;
2651ae349f5Scvs2svn       wp->cnt -= 2;
2661ae349f5Scvs2svn       *proto = (*proto << 8) | *pp++;
2671ae349f5Scvs2svn     }
268dd7e2610SBrian Somers     mbuf_Free(bp);
2691ae349f5Scvs2svn     return wp;
2701ae349f5Scvs2svn   } else {
271dd7e2610SBrian Somers     log_DumpBp(LogHDLC, "Bad FCS", wp);
272dd7e2610SBrian Somers     ccp_SendResetReq(&ccp->fsm);
273dd7e2610SBrian Somers     mbuf_Free(wp);
2741ae349f5Scvs2svn   }
275dd7e2610SBrian Somers   mbuf_Free(bp);
2761ae349f5Scvs2svn   return NULL;
2771ae349f5Scvs2svn }
2781ae349f5Scvs2svn 
2791ae349f5Scvs2svn static void
28003036ec7SBrian Somers Pred1DictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf * bp)
2811ae349f5Scvs2svn {
2821ae349f5Scvs2svn }
2831ae349f5Scvs2svn 
2841ae349f5Scvs2svn static const char *
2851ae349f5Scvs2svn Pred1DispOpts(struct lcp_opt *o)
2861ae349f5Scvs2svn {
2871ae349f5Scvs2svn   return NULL;
2881ae349f5Scvs2svn }
2891ae349f5Scvs2svn 
2901ae349f5Scvs2svn static void
29103036ec7SBrian Somers Pred1InitOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
2921ae349f5Scvs2svn {
2931ae349f5Scvs2svn   o->len = 2;
2941ae349f5Scvs2svn }
2951ae349f5Scvs2svn 
2961ae349f5Scvs2svn static int
29703036ec7SBrian Somers Pred1SetOptsOutput(struct lcp_opt *o)
2981ae349f5Scvs2svn {
29903036ec7SBrian Somers   if (o->len != 2) {
30003036ec7SBrian Somers     o->len = 2;
3011ae349f5Scvs2svn     return MODE_NAK;
3021ae349f5Scvs2svn   }
3031ae349f5Scvs2svn   return MODE_ACK;
3041ae349f5Scvs2svn }
3051ae349f5Scvs2svn 
30603036ec7SBrian Somers static int
30703036ec7SBrian Somers Pred1SetOptsInput(struct lcp_opt *o, const struct ccp_config *cfg)
30803036ec7SBrian Somers {
30903036ec7SBrian Somers   return Pred1SetOptsOutput(o);
31003036ec7SBrian Somers }
31103036ec7SBrian Somers 
3121ae349f5Scvs2svn const struct ccp_algorithm Pred1Algorithm = {
3131ae349f5Scvs2svn   TY_PRED1,
3141342caedSBrian Somers   CCP_NEG_PRED1,
3151ae349f5Scvs2svn   Pred1DispOpts,
3161ae349f5Scvs2svn   {
31703036ec7SBrian Somers     Pred1SetOptsInput,
3181ae349f5Scvs2svn     Pred1InitInput,
31903036ec7SBrian Somers     Pred1Term,
3201ae349f5Scvs2svn     Pred1ResetInput,
3211ae349f5Scvs2svn     Pred1Input,
3221ae349f5Scvs2svn     Pred1DictSetup
3231ae349f5Scvs2svn   },
3241ae349f5Scvs2svn   {
32503036ec7SBrian Somers     Pred1InitOptsOutput,
32603036ec7SBrian Somers     Pred1SetOptsOutput,
3271ae349f5Scvs2svn     Pred1InitOutput,
32803036ec7SBrian Somers     Pred1Term,
3291ae349f5Scvs2svn     Pred1ResetOutput,
3301ae349f5Scvs2svn     Pred1Output
3311ae349f5Scvs2svn   },
3321ae349f5Scvs2svn };
333