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