ccp.c revision 38557
1/*
2 *	   PPP Compression Control Protocol (CCP) Module
3 *
4 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5 *
6 *   Copyright (C) 1994, Internet Initiative Japan, Inc. All rights reserverd.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the Internet Initiative Japan, Inc.  The name of the
14 * IIJ may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * $Id: ccp.c,v 1.38 1998/08/07 18:42:47 brian Exp $
21 *
22 *	TODO:
23 *		o Support other compression protocols
24 */
25#include <sys/types.h>
26#include <netinet/in.h>
27#include <netinet/in_systm.h>
28#include <netinet/ip.h>
29#include <sys/un.h>
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <termios.h>
34
35#include "defs.h"
36#include "command.h"
37#include "mbuf.h"
38#include "log.h"
39#include "timer.h"
40#include "fsm.h"
41#include "lcpproto.h"
42#include "lcp.h"
43#include "ccp.h"
44#include "pred.h"
45#include "deflate.h"
46#include "throughput.h"
47#include "iplist.h"
48#include "slcompress.h"
49#include "lqr.h"
50#include "hdlc.h"
51#include "ipcp.h"
52#include "filter.h"
53#include "descriptor.h"
54#include "prompt.h"
55#include "link.h"
56#include "mp.h"
57#include "async.h"
58#include "physical.h"
59#include "bundle.h"
60
61static void CcpSendConfigReq(struct fsm *);
62static void CcpSentTerminateReq(struct fsm *);
63static void CcpSendTerminateAck(struct fsm *, u_char);
64static void CcpDecodeConfig(struct fsm *, u_char *, int, int,
65                            struct fsm_decode *);
66static void CcpLayerStart(struct fsm *);
67static void CcpLayerFinish(struct fsm *);
68static int CcpLayerUp(struct fsm *);
69static void CcpLayerDown(struct fsm *);
70static void CcpInitRestartCounter(struct fsm *);
71static void CcpRecvResetReq(struct fsm *);
72static void CcpRecvResetAck(struct fsm *, u_char);
73
74static struct fsm_callbacks ccp_Callbacks = {
75  CcpLayerUp,
76  CcpLayerDown,
77  CcpLayerStart,
78  CcpLayerFinish,
79  CcpInitRestartCounter,
80  CcpSendConfigReq,
81  CcpSentTerminateReq,
82  CcpSendTerminateAck,
83  CcpDecodeConfig,
84  CcpRecvResetReq,
85  CcpRecvResetAck
86};
87
88static const char *ccp_TimerNames[] =
89  {"CCP restart", "CCP openmode", "CCP stopped"};
90
91static char const *cftypes[] = {
92  /* Check out the latest ``Compression Control Protocol'' rfc (rfc1962.txt) */
93  "OUI",		/* 0: OUI */
94  "PRED1",		/* 1: Predictor type 1 */
95  "PRED2",		/* 2: Predictor type 2 */
96  "PUDDLE",		/* 3: Puddle Jumber */
97  "???", "???", "???", "???", "???", "???",
98  "???", "???", "???", "???", "???", "???",
99  "HWPPC",		/* 16: Hewlett-Packard PPC */
100  "STAC",		/* 17: Stac Electronics LZS (rfc1974) */
101  "MPPC",		/* 18: Microsoft PPC (rfc2118) */
102  "GAND",		/* 19: Gandalf FZA (rfc1993) */
103  "V42BIS",		/* 20: ARG->DATA.42bis compression */
104  "BSD",		/* 21: BSD LZW Compress */
105  "???",
106  "LZS-DCP",		/* 23: LZS-DCP Compression Protocol (rfc1967) */
107  "MAGNALINK/DEFLATE",	/* 24: Magnalink Variable Resource (rfc1975) */
108			/* 24: Deflate (according to pppd-2.3.*) */
109  "DCE",		/* 25: Data Circuit-Terminating Equip (rfc1976) */
110  "DEFLATE",		/* 26: Deflate (rfc1979) */
111};
112
113#define NCFTYPES (sizeof cftypes/sizeof cftypes[0])
114
115static const char *
116protoname(int proto)
117{
118  if (proto < 0 || proto > NCFTYPES)
119    return "none";
120  return cftypes[proto];
121}
122
123/* We support these algorithms, and Req them in the given order */
124static const struct ccp_algorithm *algorithm[] = {
125  &DeflateAlgorithm,
126  &Pred1Algorithm,
127  &PppdDeflateAlgorithm
128};
129
130#define NALGORITHMS (sizeof algorithm/sizeof algorithm[0])
131
132int
133ccp_ReportStatus(struct cmdargs const *arg)
134{
135  struct link *l;
136  struct ccp *ccp;
137
138  l = command_ChooseLink(arg);
139  ccp = &l->ccp;
140
141  prompt_Printf(arg->prompt, "%s: %s [%s]\n", l->name, ccp->fsm.name,
142                State2Nam(ccp->fsm.state));
143  prompt_Printf(arg->prompt, " My protocol = %s, His protocol = %s\n",
144                protoname(ccp->my_proto), protoname(ccp->his_proto));
145  prompt_Printf(arg->prompt, " Output: %ld --> %ld,  Input: %ld --> %ld\n",
146                ccp->uncompout, ccp->compout,
147                ccp->compin, ccp->uncompin);
148
149  prompt_Printf(arg->prompt, "\n Defaults: ");
150  prompt_Printf(arg->prompt, "FSM retry = %us\n", ccp->cfg.fsmretry);
151  prompt_Printf(arg->prompt, "           deflate windows: ");
152  prompt_Printf(arg->prompt, "incoming = %d, ", ccp->cfg.deflate.in.winsize);
153  prompt_Printf(arg->prompt, "outgoing = %d\n", ccp->cfg.deflate.out.winsize);
154  prompt_Printf(arg->prompt, "           DEFLATE:    %s\n",
155                command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE]));
156  prompt_Printf(arg->prompt, "           PREDICTOR1: %s\n",
157                command_ShowNegval(ccp->cfg.neg[CCP_NEG_PRED1]));
158  prompt_Printf(arg->prompt, "           DEFLATE24:  %s\n",
159                command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE24]));
160  return 0;
161}
162
163void
164ccp_SetupCallbacks(struct ccp *ccp)
165{
166  ccp->fsm.fn = &ccp_Callbacks;
167  ccp->fsm.FsmTimer.name = ccp_TimerNames[0];
168  ccp->fsm.OpenTimer.name = ccp_TimerNames[1];
169  ccp->fsm.StoppedTimer.name = ccp_TimerNames[2];
170}
171
172void
173ccp_Init(struct ccp *ccp, struct bundle *bundle, struct link *l,
174         const struct fsm_parent *parent)
175{
176  /* Initialise ourselves */
177
178  fsm_Init(&ccp->fsm, "CCP", PROTO_CCP, 1, CCP_MAXCODE, 10, LogCCP,
179           bundle, l, parent, &ccp_Callbacks, ccp_TimerNames);
180
181  ccp->cfg.deflate.in.winsize = 0;
182  ccp->cfg.deflate.out.winsize = 15;
183  ccp->cfg.fsmretry = DEF_FSMRETRY;
184  ccp->cfg.neg[CCP_NEG_DEFLATE] = NEG_ENABLED|NEG_ACCEPTED;
185  ccp->cfg.neg[CCP_NEG_PRED1] = NEG_ENABLED|NEG_ACCEPTED;
186  ccp->cfg.neg[CCP_NEG_DEFLATE24] = 0;
187
188  ccp_Setup(ccp);
189}
190
191void
192ccp_Setup(struct ccp *ccp)
193{
194  /* Set ourselves up for a startup */
195  ccp->fsm.open_mode = 0;
196  ccp->fsm.maxconfig = 10;
197  ccp->his_proto = ccp->my_proto = -1;
198  ccp->reset_sent = ccp->last_reset = -1;
199  ccp->in.algorithm = ccp->out.algorithm = -1;
200  ccp->in.state = ccp->out.state = NULL;
201  ccp->in.opt.id = -1;
202  ccp->out.opt = NULL;
203  ccp->his_reject = ccp->my_reject = 0;
204  ccp->uncompout = ccp->compout = 0;
205  ccp->uncompin = ccp->compin = 0;
206}
207
208static void
209CcpInitRestartCounter(struct fsm *fp)
210{
211  /* Set fsm timer load */
212  struct ccp *ccp = fsm2ccp(fp);
213
214  fp->FsmTimer.load = ccp->cfg.fsmretry * SECTICKS;
215  fp->restart = DEF_REQs;
216}
217
218static void
219CcpSendConfigReq(struct fsm *fp)
220{
221  /* Send config REQ please */
222  struct ccp *ccp = fsm2ccp(fp);
223  struct ccp_opt **o;
224  u_char *cp, buff[100];
225  int f, alloc;
226
227  cp = buff;
228  o = &ccp->out.opt;
229  alloc = ccp->his_reject == 0 && ccp->out.opt == NULL;
230  ccp->my_proto = -1;
231  ccp->out.algorithm = -1;
232  for (f = 0; f < NALGORITHMS; f++)
233    if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]) &&
234        !REJECTED(ccp, algorithm[f]->id)) {
235
236      if (!alloc)
237        for (o = &ccp->out.opt; *o != NULL; o = &(*o)->next)
238          if ((*o)->val.id == algorithm[f]->id && (*o)->algorithm == f)
239            break;
240
241      if (alloc || *o == NULL) {
242        *o = (struct ccp_opt *)malloc(sizeof(struct ccp_opt));
243        (*o)->val.id = algorithm[f]->id;
244        (*o)->val.len = 2;
245        (*o)->next = NULL;
246        (*o)->algorithm = f;
247        (*algorithm[f]->o.OptInit)(&(*o)->val, &ccp->cfg);
248      }
249
250      if (cp + (*o)->val.len > buff + sizeof buff) {
251        log_Printf(LogERROR, "%s: CCP REQ buffer overrun !\n", fp->link->name);
252        break;
253      }
254      memcpy(cp, &(*o)->val, (*o)->val.len);
255      cp += (*o)->val.len;
256
257      ccp->my_proto = (*o)->val.id;
258      ccp->out.algorithm = f;
259
260      if (alloc)
261        o = &(*o)->next;
262    }
263
264  fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, cp - buff);
265}
266
267void
268ccp_SendResetReq(struct fsm *fp)
269{
270  /* We can't read our input - ask peer to reset */
271  struct ccp *ccp = fsm2ccp(fp);
272
273  ccp->reset_sent = fp->reqid;
274  ccp->last_reset = -1;
275  fsm_Output(fp, CODE_RESETREQ, fp->reqid, NULL, 0);
276}
277
278static void
279CcpSentTerminateReq(struct fsm *fp)
280{
281  /* Term REQ just sent by FSM */
282}
283
284static void
285CcpSendTerminateAck(struct fsm *fp, u_char id)
286{
287  /* Send Term ACK please */
288  fsm_Output(fp, CODE_TERMACK, id, NULL, 0);
289}
290
291static void
292CcpRecvResetReq(struct fsm *fp)
293{
294  /* Got a reset REQ, reset outgoing dictionary */
295  struct ccp *ccp = fsm2ccp(fp);
296  if (ccp->out.state != NULL)
297    (*algorithm[ccp->out.algorithm]->o.Reset)(ccp->out.state);
298}
299
300static void
301CcpLayerStart(struct fsm *fp)
302{
303  /* We're about to start up ! */
304  log_Printf(LogCCP, "%s: LayerStart.\n", fp->link->name);
305}
306
307static void
308CcpLayerDown(struct fsm *fp)
309{
310  /* About to come down */
311  struct ccp *ccp = fsm2ccp(fp);
312  struct ccp_opt *next;
313
314  log_Printf(LogCCP, "%s: LayerDown.\n", fp->link->name);
315  if (ccp->in.state != NULL) {
316    (*algorithm[ccp->in.algorithm]->i.Term)(ccp->in.state);
317    ccp->in.state = NULL;
318    ccp->in.algorithm = -1;
319  }
320  if (ccp->out.state != NULL) {
321    (*algorithm[ccp->out.algorithm]->o.Term)(ccp->out.state);
322    ccp->out.state = NULL;
323    ccp->out.algorithm = -1;
324  }
325  ccp->his_reject = ccp->my_reject = 0;
326
327  while (ccp->out.opt) {
328    next = ccp->out.opt->next;
329    free(ccp->out.opt);
330    ccp->out.opt = next;
331  }
332  ccp_Setup(ccp);
333}
334
335static void
336CcpLayerFinish(struct fsm *fp)
337{
338  /* We're now down */
339  log_Printf(LogCCP, "%s: LayerFinish.\n", fp->link->name);
340}
341
342/*
343 *  Called when CCP has reached the OPEN state
344 */
345static int
346CcpLayerUp(struct fsm *fp)
347{
348  /* We're now up */
349  struct ccp *ccp = fsm2ccp(fp);
350  log_Printf(LogCCP, "%s: LayerUp.\n", fp->link->name);
351  if (ccp->in.state == NULL && ccp->in.algorithm >= 0 &&
352      ccp->in.algorithm < NALGORITHMS) {
353    ccp->in.state = (*algorithm[ccp->in.algorithm]->i.Init)(&ccp->in.opt);
354    if (ccp->in.state == NULL) {
355      log_Printf(LogERROR, "%s: %s (in) initialisation failure\n",
356                fp->link->name, protoname(ccp->his_proto));
357      ccp->his_proto = ccp->my_proto = -1;
358      fsm_Close(fp);
359    }
360  }
361
362  if (ccp->out.state == NULL && ccp->out.algorithm >= 0 &&
363      ccp->out.algorithm < NALGORITHMS) {
364    ccp->out.state = (*algorithm[ccp->out.algorithm]->o.Init)
365                       (&ccp->out.opt->val);
366    if (ccp->out.state == NULL) {
367      log_Printf(LogERROR, "%s: %s (out) initialisation failure\n",
368                fp->link->name, protoname(ccp->my_proto));
369      ccp->his_proto = ccp->my_proto = -1;
370      fsm_Close(fp);
371    }
372  }
373
374  log_Printf(LogCCP, "%s: Out = %s[%d], In = %s[%d]\n",
375            fp->link->name, protoname(ccp->my_proto), ccp->my_proto,
376            protoname(ccp->his_proto), ccp->his_proto);
377  return 1;
378}
379
380static void
381CcpDecodeConfig(struct fsm *fp, u_char *cp, int plen, int mode_type,
382                struct fsm_decode *dec)
383{
384  /* Deal with incoming data */
385  struct ccp *ccp = fsm2ccp(fp);
386  int type, length;
387  int f;
388  const char *end;
389
390  while (plen >= sizeof(struct fsmconfig)) {
391    type = *cp;
392    length = cp[1];
393
394    if (length == 0) {
395      log_Printf(LogCCP, "%s: CCP size zero\n", fp->link->name);
396      break;
397    }
398
399    if (length > sizeof(struct lcp_opt)) {
400      length = sizeof(struct lcp_opt);
401      log_Printf(LogCCP, "%s: Warning: Truncating length to %d\n",
402                fp->link->name, length);
403    }
404
405    for (f = NALGORITHMS-1; f > -1; f--)
406      if (algorithm[f]->id == type)
407        break;
408
409    end = f == -1 ? "" : (*algorithm[f]->Disp)((struct lcp_opt *)cp);
410    if (end == NULL)
411      end = "";
412
413    if (type < NCFTYPES)
414      log_Printf(LogCCP, " %s[%d] %s\n", cftypes[type], length, end);
415    else
416      log_Printf(LogCCP, " ???[%d] %s\n", length, end);
417
418    if (f == -1) {
419      /* Don't understand that :-( */
420      if (mode_type == MODE_REQ) {
421        ccp->my_reject |= (1 << type);
422        memcpy(dec->rejend, cp, length);
423        dec->rejend += length;
424      }
425    } else {
426      struct ccp_opt *o;
427
428      switch (mode_type) {
429      case MODE_REQ:
430	if (IsAccepted(ccp->cfg.neg[algorithm[f]->Neg]) &&
431            ccp->in.algorithm == -1) {
432	  memcpy(&ccp->in.opt, cp, length);
433          switch ((*algorithm[f]->i.Set)(&ccp->in.opt, &ccp->cfg)) {
434          case MODE_REJ:
435	    memcpy(dec->rejend, &ccp->in.opt, ccp->in.opt.len);
436	    dec->rejend += ccp->in.opt.len;
437            break;
438          case MODE_NAK:
439	    memcpy(dec->nakend, &ccp->in.opt, ccp->in.opt.len);
440	    dec->nakend += ccp->in.opt.len;
441            break;
442          case MODE_ACK:
443	    memcpy(dec->ackend, cp, length);
444	    dec->ackend += length;
445	    ccp->his_proto = type;
446            ccp->in.algorithm = f;		/* This one'll do :-) */
447            break;
448          }
449	} else {
450	  memcpy(dec->rejend, cp, length);
451	  dec->rejend += length;
452	}
453	break;
454      case MODE_NAK:
455        for (o = ccp->out.opt; o != NULL; o = o->next)
456          if (o->val.id == cp[0])
457            break;
458        if (o == NULL)
459          log_Printf(LogCCP, "%s: Warning: Ignoring peer NAK of unsent option\n",
460                    fp->link->name);
461        else {
462	  memcpy(&o->val, cp, length);
463          if ((*algorithm[f]->o.Set)(&o->val) == MODE_ACK)
464            ccp->my_proto = algorithm[f]->id;
465          else {
466	    ccp->his_reject |= (1 << type);
467	    ccp->my_proto = -1;
468          }
469        }
470        break;
471      case MODE_REJ:
472	ccp->his_reject |= (1 << type);
473	ccp->my_proto = -1;
474	break;
475      }
476    }
477
478    plen -= cp[1];
479    cp += cp[1];
480  }
481
482  if (mode_type != MODE_NOP) {
483    if (dec->rejend != dec->rej) {
484      /* rejects are preferred */
485      dec->ackend = dec->ack;
486      dec->nakend = dec->nak;
487      if (ccp->in.state == NULL) {
488        ccp->his_proto = -1;
489        ccp->in.algorithm = -1;
490      }
491    } else if (dec->nakend != dec->nak) {
492      /* then NAKs */
493      dec->ackend = dec->ack;
494      if (ccp->in.state == NULL) {
495        ccp->his_proto = -1;
496        ccp->in.algorithm = -1;
497      }
498    }
499  }
500}
501
502void
503ccp_Input(struct ccp *ccp, struct bundle *bundle, struct mbuf *bp)
504{
505  /* Got PROTO_CCP from link */
506  if (bundle_Phase(bundle) == PHASE_NETWORK)
507    fsm_Input(&ccp->fsm, bp);
508  else {
509    if (bundle_Phase(bundle) < PHASE_NETWORK)
510      log_Printf(LogCCP, "%s: Error: Unexpected CCP in phase %s (ignored)\n",
511                 ccp->fsm.link->name, bundle_PhaseName(bundle));
512    mbuf_Free(bp);
513  }
514}
515
516static void
517CcpRecvResetAck(struct fsm *fp, u_char id)
518{
519  /* Got a reset ACK, reset incoming dictionary */
520  struct ccp *ccp = fsm2ccp(fp);
521
522  if (ccp->reset_sent != -1) {
523    if (id != ccp->reset_sent) {
524      log_Printf(LogWARN, "CCP: %s: Incorrect ResetAck (id %d, not %d)"
525                " ignored\n", fp->link->name, id, ccp->reset_sent);
526      return;
527    }
528    /* Whaddaya know - a correct reset ack */
529  } else if (id == ccp->last_reset)
530    log_Printf(LogCCP, "%s: Duplicate ResetAck (resetting again)\n",
531              fp->link->name);
532  else {
533    log_Printf(LogWARN, "CCP: %s: Unexpected ResetAck (id %d) ignored\n",
534              fp->link->name, id);
535    return;
536  }
537
538  ccp->last_reset = ccp->reset_sent;
539  ccp->reset_sent = -1;
540  if (ccp->in.state != NULL)
541    (*algorithm[ccp->in.algorithm]->i.Reset)(ccp->in.state);
542}
543
544int
545ccp_Compress(struct ccp *ccp, struct link *l, int pri, u_short proto,
546             struct mbuf *m)
547{
548  /*
549   * Compress outgoing data.  It's already deemed to be suitable Network
550   * Layer data.
551   */
552  if (ccp->fsm.state == ST_OPENED && ccp->out.state != NULL)
553    return (*algorithm[ccp->out.algorithm]->o.Write)
554             (ccp->out.state, ccp, l, pri, proto, m);
555  return 0;
556}
557
558struct mbuf *
559ccp_Decompress(struct ccp *ccp, u_short *proto, struct mbuf *bp)
560{
561  /*
562   * If proto isn't PROTO_[I]COMPD, we still want to pass it to the
563   * decompression routines so that the dictionary's updated
564   */
565  if (ccp->fsm.state == ST_OPENED) {
566    if (*proto == PROTO_COMPD || *proto == PROTO_ICOMPD) {
567      /* Decompress incoming data */
568      if (ccp->reset_sent != -1)
569        /* Send another REQ and put the packet in the bit bucket */
570        fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->reset_sent, NULL, 0);
571      else if (ccp->in.state != NULL)
572        return (*algorithm[ccp->in.algorithm]->i.Read)
573                 (ccp->in.state, ccp, proto, bp);
574      mbuf_Free(bp);
575      bp = NULL;
576    } else if (PROTO_COMPRESSIBLE(*proto) && ccp->in.state != NULL)
577      /* Add incoming Network Layer traffic to our dictionary */
578      (*algorithm[ccp->in.algorithm]->i.DictSetup)
579        (ccp->in.state, ccp, *proto, bp);
580  }
581
582  return bp;
583}
584
585u_short
586ccp_Proto(struct ccp *ccp)
587{
588  return !link2physical(ccp->fsm.link) || !ccp->fsm.bundle->ncp.mp.active ?
589         PROTO_COMPD : PROTO_ICOMPD;
590}
591
592int
593ccp_SetOpenMode(struct ccp *ccp)
594{
595  int f;
596
597  for (f = 0; f < CCP_NEG_TOTAL; f++)
598    if (IsEnabled(ccp->cfg.neg[f])) {
599      ccp->fsm.open_mode = 0;
600      return 1;
601    }
602
603  ccp->fsm.open_mode = OPEN_PASSIVE;	/* Go straight to ST_STOPPED ? */
604
605  for (f = 0; f < CCP_NEG_TOTAL; f++)
606    if (IsAccepted(ccp->cfg.neg[f]))
607      return 1;
608
609  return 0;				/* No CCP at all */
610}
611