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