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