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