1 /* 2 * PPP Link Control Protocol (LCP) Module 3 * 4 * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 5 * 6 * Copyright (C) 1993, 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: lcp.c,v 1.12 1997/02/22 16:10:23 peter Exp $ 21 * 22 * TODO: 23 * o Validate magic number received from peer. 24 * o Limit data field length by MRU 25 */ 26 #include <sys/time.h> 27 #include "fsm.h" 28 #include "lcp.h" 29 #include "ipcp.h" 30 #include "lcpproto.h" 31 #include "os.h" 32 #include "hdlc.h" 33 #include "ccp.h" 34 #include "lqr.h" 35 #include "phase.h" 36 #include "vars.h" 37 #include "auth.h" 38 #include <arpa/inet.h> 39 40 extern void IpcpUp(); 41 extern void IpcpOpen(); 42 extern void StartPapChallenge(); 43 extern void StartChapChallenge(); 44 extern void SetLinkParams(struct lcpstate *); 45 extern void Prompt(); 46 extern void StopIdleTimer(); 47 extern void OsLinkdown(); 48 extern void Cleanup(); 49 extern struct pppTimer IpcpReportTimer; 50 51 struct lcpstate LcpInfo; 52 53 static void LcpSendConfigReq __P((struct fsm *)); 54 static void LcpSendTerminateReq __P((struct fsm *fp)); 55 static void LcpSendTerminateAck __P((struct fsm *fp)); 56 static void LcpDecodeConfig __P((u_char *cp, int flen,int mode)); 57 static void LcpInitRestartCounter __P((struct fsm *)); 58 static void LcpLayerUp __P((struct fsm *)); 59 static void LcpLayerDown __P((struct fsm *)); 60 static void LcpLayerStart __P((struct fsm *)); 61 static void LcpLayerFinish __P((struct fsm *)); 62 63 extern int ModemSpeed(); 64 65 #define REJECTED(p, x) (p->his_reject & (1<<x)) 66 67 static char *cftypes[] = { 68 "???", "MRU", "ACCMAP", "AUTHPROTO", "QUALPROTO", "MAGICNUM", 69 "RESERVED", "PROTOCOMP", "ACFCOMP", "FCSALT", "SDP", 70 }; 71 72 struct fsm LcpFsm = { 73 "LCP", /* Name of protocol */ 74 PROTO_LCP, /* Protocol Number */ 75 LCP_MAXCODE, 76 OPEN_ACTIVE, 77 ST_INITIAL, /* State of machine */ 78 0, 0, 0, 79 80 0, 81 { 0, 0, 0, NULL, NULL, NULL }, 82 83 LcpLayerUp, 84 LcpLayerDown, 85 LcpLayerStart, 86 LcpLayerFinish, 87 LcpInitRestartCounter, 88 LcpSendConfigReq, 89 LcpSendTerminateReq, 90 LcpSendTerminateAck, 91 LcpDecodeConfig, 92 }; 93 94 static struct pppTimer LcpReportTimer; 95 96 char *PhaseNames[] = { 97 "Dead", "Establish", "Authenticate", "Network", "Terminate" 98 }; 99 100 void 101 NewPhase(new) 102 int new; 103 { 104 struct lcpstate *lcp = &LcpInfo; 105 106 phase = new; 107 LogPrintf(LOG_PHASE_BIT, "Phase: %s\n", PhaseNames[phase]); 108 switch (phase) { 109 case PHASE_AUTHENTICATE: 110 lcp->auth_ineed = lcp->want_auth; 111 lcp->auth_iwait = lcp->his_auth; 112 if (lcp->his_auth || lcp->want_auth) { 113 LogPrintf(LOG_PHASE_BIT, " his = %x, mine = %x\n", lcp->his_auth, lcp->want_auth); 114 if (lcp->his_auth == PROTO_PAP) 115 StartAuthChallenge(&AuthPapInfo); 116 if (lcp->want_auth == PROTO_CHAP) 117 StartAuthChallenge(&AuthChapInfo); 118 } else 119 NewPhase(PHASE_NETWORK); 120 break; 121 case PHASE_NETWORK: 122 IpcpUp(); 123 IpcpOpen(); 124 CcpUp(); 125 CcpOpen(); 126 if (mode & MODE_BACKGROUND && BGFiledes[1] != -1) { 127 char c = EX_NORMAL; 128 if (write(BGFiledes[1],&c,1) == 1) 129 LogPrintf(LOG_PHASE_BIT,"Parent notified of success.\n"); 130 else 131 LogPrintf(LOG_PHASE_BIT,"Failed to notify parent of success.\n"); 132 close(BGFiledes[1]); 133 BGFiledes[1] = -1; 134 } 135 break; 136 case PHASE_DEAD: 137 if (mode & MODE_DIRECT) 138 Cleanup(EX_DEAD); 139 break; 140 } 141 } 142 143 static void 144 LcpReportTime() 145 { 146 #ifdef VERBOSE 147 time_t t; 148 149 time(&t); 150 logprintf("%s", ctime(&t)); 151 #endif 152 StopTimer(&LcpReportTimer); 153 LcpReportTimer.state = TIMER_STOPPED; 154 StartTimer(&LcpReportTimer); 155 HdlcErrorCheck(); 156 } 157 158 int 159 ReportLcpStatus() 160 { 161 struct lcpstate *lcp = &LcpInfo; 162 struct fsm *fp = &LcpFsm; 163 164 printf("%s [%s]\n", fp->name, StateNames[fp->state]); 165 printf( 166 " his side: MRU %ld, ACCMAP %08lx, PROTOCOMP %d, ACFCOMP %d MAGIC %08lx\n", 167 lcp->his_mru, lcp->his_accmap, lcp->his_protocomp, lcp->his_acfcomp, lcp->his_magic); 168 printf( 169 " my side: MRU %ld, ACCMAP %08lx, PROTOCOMP %d, ACFCOMP %d MAGIC %08lx\n", 170 lcp->want_mru, lcp->want_accmap, lcp->want_protocomp, lcp->want_acfcomp, lcp->want_magic); 171 printf("\nDefaults: MRU = %ld, ACCMAP = %08x\t", VarMRU, VarAccmap); 172 printf("Open Mode: %s\n", (VarOpenMode == OPEN_ACTIVE)? "active" : "passive"); 173 return(1); 174 } 175 176 /* 177 * Generate random number which will be used as magic number. 178 */ 179 u_long 180 GenerateMagic() 181 { 182 time_t tl; 183 struct timeval tval; 184 185 time(&tl); 186 gettimeofday(&tval, NULL); 187 tl += (tval.tv_sec ^ tval.tv_usec) + getppid(); 188 tl *= getpid(); 189 return(tl); 190 } 191 192 void 193 LcpInit() 194 { 195 struct lcpstate *lcp = &LcpInfo; 196 197 FsmInit(&LcpFsm); 198 HdlcInit(); 199 200 bzero(lcp, sizeof(struct lcpstate)); 201 lcp->want_mru = VarMRU; 202 lcp->his_mru = DEF_MRU; 203 lcp->his_accmap = 0xffffffff; 204 lcp->want_accmap = VarAccmap; 205 lcp->want_magic = GenerateMagic(); 206 lcp->want_auth = lcp->his_auth = 0; 207 if (Enabled(ConfChap)) 208 lcp->want_auth = PROTO_CHAP; 209 else if (Enabled(ConfPap)) 210 lcp->want_auth = PROTO_PAP; 211 if (Enabled(ConfLqr)) lcp->want_lqrperiod = VarLqrTimeout * 100; 212 if (Enabled(ConfAcfcomp)) lcp->want_acfcomp = 1; 213 if (Enabled(ConfProtocomp)) lcp->want_protocomp = 1; 214 LcpFsm.maxconfig = 10; 215 } 216 217 static void 218 LcpInitRestartCounter(fp) 219 struct fsm *fp; 220 { 221 fp->FsmTimer.load = VarRetryTimeout * SECTICKS; 222 fp->restart = 5; 223 } 224 225 void 226 PutConfValue(cpp, types, type, len, val) 227 u_char **cpp; 228 char **types; 229 u_char type; 230 int len; 231 u_long val; 232 { 233 u_char *cp; 234 struct in_addr ina; 235 236 cp = *cpp; 237 *cp++ = type; *cp++ = len; 238 if (len == 6) { 239 if (type == TY_IPADDR) { 240 ina.s_addr = htonl(val); 241 LogPrintf(LOG_LCP_BIT, " %s [%d] %s\n", 242 types[type], len, inet_ntoa(ina)); 243 } else { 244 LogPrintf(LOG_LCP_BIT, " %s [%d] %08x\n", types[type], len, val); 245 } 246 *cp++ = (val >> 24) & 0377; 247 *cp++ = (val >> 16) & 0377; 248 } else 249 LogPrintf(LOG_LCP_BIT, " %s [%d] %d\n", types[type], len, val); 250 *cp++ = (val >> 8) & 0377; 251 *cp++ = val & 0377; 252 *cpp = cp; 253 } 254 255 static void 256 LcpSendConfigReq(fp) 257 struct fsm *fp; 258 { 259 u_char *cp; 260 struct lcpstate *lcp = &LcpInfo; 261 struct lqrreq *req; 262 263 LogPrintf(LOG_LCP_BIT, "%s: SendConfigReq\n", fp->name); 264 cp = ReqBuff; 265 if (!DEV_IS_SYNC) { 266 if (lcp->want_acfcomp && !REJECTED(lcp, TY_ACFCOMP)) { 267 *cp++ = TY_ACFCOMP; *cp++ = 2; 268 LogPrintf(LOG_LCP_BIT, " %s\n", cftypes[TY_ACFCOMP]); 269 } 270 if (lcp->want_protocomp && !REJECTED(lcp, TY_PROTOCOMP)) { 271 *cp++ = TY_PROTOCOMP; *cp++ = 2; 272 LogPrintf(LOG_LCP_BIT, " %s\n", cftypes[TY_PROTOCOMP]); 273 } 274 if (!REJECTED(lcp, TY_ACCMAP)) 275 PutConfValue(&cp, cftypes, TY_ACCMAP, 6, lcp->want_accmap); 276 } 277 if (!REJECTED(lcp, TY_MRU)) 278 PutConfValue(&cp, cftypes, TY_MRU, 4, lcp->want_mru); 279 if (lcp->want_magic && !REJECTED(lcp, TY_MAGICNUM)) 280 PutConfValue(&cp, cftypes, TY_MAGICNUM, 6, lcp->want_magic); 281 if (lcp->want_lqrperiod && !REJECTED(lcp, TY_QUALPROTO)) { 282 req = (struct lqrreq *)cp; 283 req->type = TY_QUALPROTO; req->length = sizeof(struct lqrreq); 284 req->proto = htons(PROTO_LQR); 285 req->period = htonl(lcp->want_lqrperiod); 286 cp += sizeof(struct lqrreq); 287 LogPrintf(LOG_LCP_BIT, " %s (%d)\n", cftypes[TY_QUALPROTO], lcp->want_lqrperiod); 288 } 289 switch (lcp->want_auth) { 290 case PROTO_PAP: 291 PutConfValue(&cp, cftypes, TY_AUTHPROTO, 4, lcp->want_auth); 292 break; 293 case PROTO_CHAP: 294 PutConfValue(&cp, cftypes, TY_AUTHPROTO, 5, lcp->want_auth); 295 *cp++ = 5; /* Use MD5 */ 296 break; 297 } 298 FsmOutput(fp, CODE_CONFIGREQ, fp->reqid++, ReqBuff, cp - ReqBuff); 299 } 300 301 void 302 LcpSendProtoRej(option, count) 303 u_char *option; 304 int count; 305 { 306 struct fsm *fp = &LcpFsm; 307 308 LogPrintf(LOG_LCP_BIT, "%s: SendProtoRej\n", fp->name); 309 FsmOutput(fp, CODE_PROTOREJ, fp->reqid, option, count); 310 } 311 312 static void 313 LcpSendTerminateReq(fp) 314 struct fsm *fp; 315 { 316 /* Most thins are done in fsm layer. Nothing to to. */ 317 } 318 319 static void 320 LcpSendTerminateAck(fp) 321 struct fsm *fp; 322 { 323 LogPrintf(LOG_LCP_BIT, "%s: SendTerminateAck.\n", fp->name); 324 FsmOutput(fp, CODE_TERMACK, fp->reqid++, NULL, 0); 325 } 326 327 static void 328 LcpLayerStart(fp) 329 struct fsm *fp; 330 { 331 LogPrintf(LOG_LCP_BIT, "%s: LayerStart\n", fp->name); 332 NewPhase(PHASE_ESTABLISH); 333 } 334 335 static void 336 StopAllTimers() 337 { 338 StopTimer(&LcpReportTimer); 339 StopTimer(&IpcpReportTimer); 340 StopIdleTimer(); 341 StopTimer(&AuthPapInfo.authtimer); 342 StopTimer(&AuthChapInfo.authtimer); 343 StopLqrTimer(); 344 } 345 346 static void 347 LcpLayerFinish(fp) 348 struct fsm *fp; 349 { 350 #ifdef VERBOSE 351 fprintf(stderr, "%s: LayerFinish\r\n", fp->name); 352 #endif 353 Prompt(1); 354 LogPrintf(LOG_LCP_BIT, "%s: LayerFinish\n", fp->name); 355 #ifdef notdef 356 OsCloseLink(0); 357 #else 358 OsCloseLink(1); 359 #endif 360 NewPhase(PHASE_DEAD); 361 StopAllTimers(); 362 OsInterfaceDown(0); 363 } 364 365 static void 366 LcpLayerUp(fp) 367 struct fsm *fp; 368 { 369 LogPrintf(LOG_LCP_BIT, "%s: LayerUp\n", fp->name); 370 OsSetInterfaceParams(23, LcpInfo.his_mru, ModemSpeed()); 371 SetLinkParams(&LcpInfo); 372 373 NewPhase(PHASE_AUTHENTICATE); 374 375 StartLqm(); 376 StopTimer(&LcpReportTimer); 377 LcpReportTimer.state = TIMER_STOPPED; 378 LcpReportTimer.load = 60 * SECTICKS; 379 LcpReportTimer.func = LcpReportTime; 380 StartTimer(&LcpReportTimer); 381 } 382 383 static void 384 LcpLayerDown(fp) 385 struct fsm *fp; 386 { 387 LogPrintf(LOG_LCP_BIT, "%s: LayerDown\n", fp->name); 388 StopAllTimers(); 389 OsLinkdown(); 390 NewPhase(PHASE_TERMINATE); 391 Prompt(1); 392 } 393 394 void 395 LcpUp() 396 { 397 FsmUp(&LcpFsm); 398 } 399 400 void 401 LcpDown() /* Sudden death */ 402 { 403 NewPhase(PHASE_DEAD); 404 StopAllTimers(); 405 FsmDown(&LcpFsm); 406 } 407 408 void 409 LcpOpen(mode) 410 int mode; 411 { 412 LcpFsm.open_mode = mode; 413 FsmOpen(&LcpFsm); 414 } 415 416 void 417 LcpClose() 418 { 419 FsmClose(&LcpFsm); 420 } 421 422 /* 423 * XXX: Should validate option length 424 */ 425 static void 426 LcpDecodeConfig(cp, plen, mode) 427 u_char *cp; 428 int plen; 429 int mode; 430 { 431 char *request; 432 int type, length, mru; 433 u_long *lp, magic, accmap; 434 u_short *sp, proto; 435 struct lqrreq *req; 436 437 ackp = AckBuff; 438 nakp = NakBuff; 439 rejp = RejBuff; 440 441 while (plen >= sizeof(struct fsmconfig)) { 442 type = *cp; 443 length = cp[1]; 444 if (type <= TY_ACFCOMP) 445 request = cftypes[type]; 446 else 447 request = "???"; 448 449 switch (type) { 450 case TY_MRU: 451 sp = (u_short *)(cp + 2); 452 mru = htons(*sp); 453 LogPrintf(LOG_LCP_BIT, " %s %d\n", request, mru); 454 455 switch (mode) { 456 case MODE_REQ: 457 if (mru > MAX_MRU) { 458 *sp = htons(MAX_MRU); 459 bcopy(cp, nakp, 4); nakp += 4; 460 } else if (mru < MIN_MRU) { 461 *sp = htons(MIN_MRU); 462 bcopy(cp, nakp, 4); nakp += 4; 463 } else { 464 LcpInfo.his_mru = mru; 465 bcopy(cp, ackp, 4); ackp += 4; 466 } 467 break; 468 case MODE_NAK: 469 if (mru >= MIN_MRU || mru <= MAX_MRU) 470 LcpInfo.want_mru = mru; 471 break; 472 case MODE_REJ: 473 LcpInfo.his_reject |= (1 << type); 474 break; 475 } 476 break; 477 case TY_ACCMAP: 478 lp = (u_long *)(cp + 2); 479 accmap = htonl(*lp); 480 LogPrintf(LOG_LCP_BIT, " %s %08x\n", request, accmap); 481 482 switch (mode) { 483 case MODE_REQ: 484 LcpInfo.his_accmap = accmap; 485 bcopy(cp, ackp, 6); ackp += 6; 486 break; 487 case MODE_NAK: 488 LcpInfo.want_accmap = accmap; 489 break; 490 case MODE_REJ: 491 LcpInfo.his_reject |= (1 << type); 492 break; 493 } 494 break; 495 case TY_AUTHPROTO: 496 sp = (u_short *)(cp + 2); 497 proto = ntohs(*sp); 498 LogPrintf(LOG_LCP_BIT, " %s proto = %04x\n", request, proto); 499 500 switch (mode) { 501 case MODE_REQ: 502 switch (proto) { 503 case PROTO_PAP: 504 if (length != 4) { 505 LogPrintf(LOG_LCP_BIT, " %s bad length (%d)\n", request, length); 506 goto reqreject; 507 } 508 if (Acceptable(ConfPap)) { 509 LcpInfo.his_auth = proto; 510 bcopy(cp, ackp, length); ackp += length; 511 } else if (Acceptable(ConfChap)) { 512 *nakp++ = *cp; *nakp++ = 5; 513 *nakp++ = (unsigned char)(PROTO_CHAP >> 8); 514 *nakp++ = (unsigned char)PROTO_CHAP; 515 *nakp++ = 5; 516 } else 517 goto reqreject; 518 break; 519 case PROTO_CHAP: 520 if (length < 5) { 521 LogPrintf(LOG_LCP_BIT, " %s bad length (%d)\n", request, length); 522 goto reqreject; 523 } 524 if (Acceptable(ConfChap) && cp[4] == 5) { 525 LcpInfo.his_auth = proto; 526 bcopy(cp, ackp, length); ackp += length; 527 } else if (Acceptable(ConfPap)) { 528 *nakp++ = *cp; *nakp++ = 4; 529 *nakp++ = (unsigned char)(PROTO_PAP >> 8); 530 *nakp++ = (unsigned char)PROTO_PAP; 531 } else 532 goto reqreject; 533 break; 534 default: 535 LogPrintf(LOG_LCP_BIT, " %s not implemented, NAK.\n", request); 536 bcopy(cp, nakp, length); 537 nakp += length; 538 break; 539 } 540 break; 541 case MODE_NAK: 542 break; 543 case MODE_REJ: 544 LcpInfo.his_reject |= (1 << type); 545 break; 546 } 547 break; 548 case TY_QUALPROTO: 549 req = (struct lqrreq *)cp; 550 LogPrintf(LOG_LCP_BIT, " %s proto: %x, interval: %dms\n", 551 request, ntohs(req->proto), ntohl(req->period)*10); 552 switch (mode) { 553 case MODE_REQ: 554 if (ntohs(req->proto) != PROTO_LQR || !Acceptable(ConfLqr)) 555 goto reqreject; 556 else { 557 LcpInfo.his_lqrperiod = ntohl(req->period); 558 if (LcpInfo.his_lqrperiod < 500) 559 LcpInfo.his_lqrperiod = 500; 560 req->period = htonl(LcpInfo.his_lqrperiod); 561 bcopy(cp, ackp, length); ackp += length; 562 } 563 break; 564 case MODE_NAK: 565 break; 566 case MODE_REJ: 567 LcpInfo.his_reject |= (1 << type); 568 break; 569 } 570 break; 571 case TY_MAGICNUM: 572 lp = (u_long *)(cp + 2); 573 magic = ntohl(*lp); 574 LogPrintf(LOG_LCP_BIT, " %s %08x\n", request, magic); 575 576 switch (mode) { 577 case MODE_REQ: 578 if (LcpInfo.want_magic) { 579 /* XXX: Shoud validate magic number */ 580 if (magic == LcpInfo.want_magic) 581 logprintf("magic is same!! %x, %x, %x\n", 582 magic, LcpInfo.want_magic, LcpInfo.his_magic); 583 LcpInfo.his_magic = magic; 584 bcopy(cp, ackp, length); ackp += length; 585 } else { 586 LcpInfo.my_reject |= (1 << type); 587 goto reqreject; 588 } 589 break; 590 case MODE_NAK: 591 LogPrintf(LOG_LCP_BIT, " %s magic %08x has NAKed\n", request, magic); 592 LcpInfo.want_magic = GenerateMagic(); 593 break; 594 case MODE_REJ: 595 LogPrintf(LOG_LCP_BIT, " %s magic has REJected\n", request); 596 LcpInfo.want_magic = 0; 597 LcpInfo.his_reject |= (1 << type); 598 break; 599 } 600 break; 601 case TY_PROTOCOMP: 602 LogPrintf(LOG_LCP_BIT, " %s\n", request); 603 604 switch (mode) { 605 case MODE_REQ: 606 if (Acceptable(ConfProtocomp)) { 607 LcpInfo.his_protocomp = 1; 608 bcopy(cp, ackp, 2); ackp += 2; 609 } else { 610 #ifdef OLDMST 611 /* 612 * MorningStar before v1.3 needs NAK 613 */ 614 bcopy(cp, nakp, 2); nakp += 2; 615 #else 616 bcopy(cp, rejp, 2); rejp += 2; 617 LcpInfo.my_reject |= (1 << type); 618 #endif 619 } 620 break; 621 case MODE_NAK: 622 case MODE_REJ: 623 LcpInfo.want_protocomp = 0; 624 LcpInfo.his_reject |= (1 << type); 625 break; 626 } 627 break; 628 case TY_ACFCOMP: 629 LogPrintf(LOG_LCP_BIT, " %s\n", request); 630 switch (mode) { 631 case MODE_REQ: 632 if (Acceptable(ConfAcfcomp)) { 633 LcpInfo.his_acfcomp = 1; 634 bcopy(cp, ackp, 2); 635 ackp += 2; 636 } else { 637 #ifdef OLDMST 638 /* 639 * MorningStar before v1.3 needs NAK 640 */ 641 bcopy(cp, nakp, 2); 642 nakp += 2; 643 #else 644 bcopy(cp, rejp, 2); 645 rejp += 2; 646 LcpInfo.my_reject |= (1 << type); 647 #endif 648 } 649 break; 650 case MODE_NAK: 651 case MODE_REJ: 652 LcpInfo.want_acfcomp = 0; 653 LcpInfo.his_reject |= (1 << type); 654 break; 655 } 656 break; 657 case TY_SDP: 658 LogPrintf(LOG_LCP_BIT, " %s\n", request); 659 switch (mode) { 660 case MODE_REQ: 661 case MODE_NAK: 662 case MODE_REJ: 663 break; 664 } 665 break; 666 default: 667 LogPrintf(LOG_LCP_BIT, " ???[%02x]\n", type); 668 if (mode == MODE_REQ) { 669 reqreject: 670 bcopy(cp, rejp, length); 671 rejp += length; 672 LcpInfo.my_reject |= (1 << type); 673 } 674 break; 675 } 676 /* to avoid inf. loop */ 677 if (length == 0) { 678 LogPrintf(LOG_LCP, "LCP size zero\n"); 679 break; 680 } 681 682 plen -= length; 683 cp += length; 684 } 685 } 686 687 void 688 LcpInput(struct mbuf *bp) 689 { 690 FsmInput(&LcpFsm, bp); 691 } 692