1/* 2 * fsm.c - {Link, IP} Control Protocol Finite State Machine. 3 * 4 * Copyright (c) 1989 Carnegie Mellon University. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms are permitted 8 * provided that the above copyright notice and this paragraph are 9 * duplicated in all such forms and that any documentation, 10 * advertising materials, and other materials related to such 11 * distribution and use acknowledge that the software was developed 12 * by Carnegie Mellon University. The name of the 13 * University may not be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18 */ 19 20#define RCSID "$Id$" 21 22/* 23 * TODO: 24 * Randomize fsm id on link/init. 25 * Deal with variable outgoing MTU. 26 */ 27 28#include <stdio.h> 29#include <string.h> 30#include <sys/types.h> 31 32#include <pppd.h> 33#include "fsm.h" 34 35static const char rcsid[] = RCSID; 36 37static void fsm_timeout __P((void *)); 38static void fsm_rconfreq __P((fsm *, int, u_char *, int)); 39static void fsm_rconfack __P((fsm *, int, u_char *, int)); 40static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int)); 41static void fsm_rtermreq __P((fsm *, int, u_char *, int)); 42static void fsm_rtermack __P((fsm *)); 43static void fsm_rcoderej __P((fsm *, u_char *, int)); 44static void fsm_sconfreq __P((fsm *, int)); 45 46#define PROTO_NAME(f) ((f)->callbacks->proto_name) 47 48int peer_mru[NUM_PPP]; 49 50 51/* 52 * fsm_init - Initialize fsm. 53 * 54 * Initialize fsm state. 55 */ 56void 57fsm_init(f) 58 fsm *f; 59{ 60 f->state = INITIAL; 61 f->flags = 0; 62 f->id = 0; 63 f->timeouttime = DEFTIMEOUT; 64 f->maxconfreqtransmits = DEFMAXCONFREQS; 65 f->maxtermtransmits = DEFMAXTERMREQS; 66 f->maxnakloops = DEFMAXNAKLOOPS; 67 f->term_reason_len = 0; 68} 69 70 71/* 72 * fsm_lowerup - The lower layer is up. 73 */ 74void 75fsm_lowerup(f) 76 fsm *f; 77{ 78 switch( f->state ){ 79 case INITIAL: 80 f->state = CLOSED; 81 break; 82 83 case STARTING: 84 if( f->flags & OPT_SILENT ) 85 f->state = STOPPED; 86 else { 87 /* Send an initial configure-request */ 88 fsm_sconfreq(f, 0); 89 f->state = REQSENT; 90 } 91 break; 92 93 default: 94 FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state)); 95 } 96} 97 98 99/* 100 * fsm_lowerdown - The lower layer is down. 101 * 102 * Cancel all timeouts and inform upper layers. 103 */ 104void 105fsm_lowerdown(f) 106 fsm *f; 107{ 108 switch( f->state ){ 109 case CLOSED: 110 f->state = INITIAL; 111 break; 112 113 case STOPPED: 114 f->state = STARTING; 115 if( f->callbacks->starting ) 116 (*f->callbacks->starting)(f); 117 break; 118 119 case CLOSING: 120 f->state = INITIAL; 121 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 122 break; 123 124 case STOPPING: 125 case REQSENT: 126 case ACKRCVD: 127 case ACKSENT: 128 f->state = STARTING; 129 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 130 break; 131 132 case OPENED: 133 if( f->callbacks->down ) 134 (*f->callbacks->down)(f); 135 f->state = STARTING; 136 break; 137 138 default: 139 FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state)); 140 } 141} 142 143 144/* 145 * fsm_open - Link is allowed to come up. 146 */ 147void 148fsm_open(f) 149 fsm *f; 150{ 151 switch( f->state ){ 152 case INITIAL: 153 case STARTING: /* foxconn wklin added */ 154 f->state = STARTING; 155 if( f->callbacks->starting ) 156 (*f->callbacks->starting)(f); 157 break; 158 159 case CLOSED: 160 if( f->flags & OPT_SILENT ) 161 f->state = STOPPED; 162 else { 163 /* Send an initial configure-request */ 164 fsm_sconfreq(f, 0); 165 f->state = REQSENT; 166 } 167 break; 168 169 case CLOSING: 170 f->state = STOPPING; 171 /* fall through */ 172 case STOPPED: 173 case OPENED: 174 if( f->flags & OPT_RESTART ){ 175 fsm_lowerdown(f); 176 fsm_lowerup(f); 177 } 178 break; 179 } 180} 181 182 183/* 184 * fsm_close - Start closing connection. 185 * 186 * Cancel timeouts and either initiate close or possibly go directly to 187 * the CLOSED state. 188 */ 189void 190fsm_close(f, reason) 191 fsm *f; 192 char *reason; 193{ 194 f->term_reason = reason; 195 f->term_reason_len = (reason == NULL? 0: strlen(reason)); 196 switch( f->state ){ 197 case STARTING: 198 f->state = INITIAL; 199 break; 200 case STOPPED: 201 f->state = CLOSED; 202 break; 203 case STOPPING: 204 f->state = CLOSING; 205 break; 206 207 case REQSENT: 208 case ACKRCVD: 209 case ACKSENT: 210 case OPENED: 211 if( f->state != OPENED ) 212 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 213 else if( f->callbacks->down ) 214 (*f->callbacks->down)(f); /* Inform upper layers we're down */ 215 216 /* Init restart counter, send Terminate-Request */ 217 f->retransmits = f->maxtermtransmits; 218 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 219 (u_char *) f->term_reason, f->term_reason_len); 220 TIMEOUT(fsm_timeout, f, f->timeouttime); 221 --f->retransmits; 222 223 f->state = CLOSING; 224 break; 225 } 226} 227 228 229/* 230 * fsm_timeout - Timeout expired. 231 */ 232static void 233fsm_timeout(arg) 234 void *arg; 235{ 236 fsm *f = (fsm *) arg; 237 238 switch (f->state) { 239 case CLOSING: 240 case STOPPING: 241 if( f->retransmits <= 0 ){ 242 /* 243 * We've waited for an ack long enough. Peer probably heard us. 244 */ 245 f->state = (f->state == CLOSING)? CLOSED: STOPPED; 246 if( f->callbacks->finished ) 247 (*f->callbacks->finished)(f); 248 } else { 249 /* Send Terminate-Request */ 250 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 251 (u_char *) f->term_reason, f->term_reason_len); 252 TIMEOUT(fsm_timeout, f, f->timeouttime); 253 --f->retransmits; 254 } 255 break; 256 257 case REQSENT: 258 case ACKRCVD: 259 case ACKSENT: 260 if (f->retransmits <= 0) { 261 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f)); 262 f->state = STOPPED; 263 if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) 264 (*f->callbacks->finished)(f); 265 266 } else { 267 /* Retransmit the configure-request */ 268 if (f->callbacks->retransmit) 269 (*f->callbacks->retransmit)(f); 270 fsm_sconfreq(f, 1); /* Re-send Configure-Request */ 271 if( f->state == ACKRCVD ) 272 f->state = REQSENT; 273 } 274 break; 275 276 default: 277 FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state)); 278 } 279} 280 281 282/* 283 * fsm_input - Input packet. 284 */ 285void 286fsm_input(f, inpacket, l) 287 fsm *f; 288 u_char *inpacket; 289 int l; 290{ 291 u_char *inp; 292 u_char code, id; 293 int len; 294 295 /* 296 * Parse header (code, id and length). 297 * If packet too short, drop it. 298 */ 299 inp = inpacket; 300 if (l < HEADERLEN) { 301 FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol)); 302 return; 303 } 304 GETCHAR(code, inp); 305 GETCHAR(id, inp); 306 GETSHORT(len, inp); 307 if (len < HEADERLEN) { 308 FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol)); 309 return; 310 } 311 if (len > l) { 312 FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol)); 313 return; 314 } 315 len -= HEADERLEN; /* subtract header length */ 316 317 if( f->state == INITIAL || f->state == STARTING ){ 318 FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.", 319 f->protocol, f->state)); 320 return; 321 } 322 323 /* 324 * Action depends on code. 325 */ 326 switch (code) { 327 case CONFREQ: 328 fsm_rconfreq(f, id, inp, len); 329 break; 330 331 case CONFACK: 332 fsm_rconfack(f, id, inp, len); 333 break; 334 335 case CONFNAK: 336 case CONFREJ: 337 fsm_rconfnakrej(f, code, id, inp, len); 338 break; 339 340 case TERMREQ: 341 fsm_rtermreq(f, id, inp, len); 342 break; 343 344 case TERMACK: 345 fsm_rtermack(f); 346 break; 347 348 case CODEREJ: 349 fsm_rcoderej(f, inp, len); 350 break; 351 352 default: 353 if( !f->callbacks->extcode 354 || !(*f->callbacks->extcode)(f, code, id, inp, len) ) 355 fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); 356 break; 357 } 358} 359 360 361/* 362 * fsm_rconfreq - Receive Configure-Request. 363 */ 364static void 365fsm_rconfreq(f, id, inp, len) 366 fsm *f; 367 u_char id; 368 u_char *inp; 369 int len; 370{ 371 int code, reject_if_disagree; 372 373 switch( f->state ){ 374 case CLOSED: 375 /* Go away, we're closed */ 376 fsm_sdata(f, TERMACK, id, NULL, 0); 377 return; 378 case CLOSING: 379 case STOPPING: 380 return; 381 382 case OPENED: 383 /* Go down and restart negotiation */ 384 if( f->callbacks->down ) 385 (*f->callbacks->down)(f); /* Inform upper layers */ 386 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 387 break; 388 389 case STOPPED: 390 /* Negotiation started by our peer */ 391 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 392 f->state = REQSENT; 393 break; 394 } 395 396 /* 397 * Pass the requested configuration options 398 * to protocol-specific code for checking. 399 */ 400 if (f->callbacks->reqci){ /* Check CI */ 401 reject_if_disagree = (f->nakloops >= f->maxnakloops); 402 code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); 403 } else if (len) 404 code = CONFREJ; /* Reject all CI */ 405 else 406 code = CONFACK; 407 408 /* send the Ack, Nak or Rej to the peer */ 409 fsm_sdata(f, code, id, inp, len); 410 411 if (code == CONFACK) { 412 if (f->state == ACKRCVD) { 413 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 414 f->state = OPENED; 415 if (f->callbacks->up) 416 (*f->callbacks->up)(f); /* Inform upper layers */ 417 } else 418 f->state = ACKSENT; 419 f->nakloops = 0; 420 421 } else { 422 /* we sent CONFACK or CONFREJ */ 423 if (f->state != ACKRCVD) 424 f->state = REQSENT; 425 if( code == CONFNAK ) 426 ++f->nakloops; 427 } 428} 429 430 431/* 432 * fsm_rconfack - Receive Configure-Ack. 433 */ 434static void 435fsm_rconfack(f, id, inp, len) 436 fsm *f; 437 int id; 438 u_char *inp; 439 int len; 440{ 441 if (id != f->reqid || f->seen_ack) /* Expected id? */ 442 return; /* Nope, toss... */ 443 if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): 444 (len == 0)) ){ 445 /* Ack is bad - ignore it */ 446 error("Received bad configure-ack: %P", inp, len); 447 return; 448 } 449 f->seen_ack = 1; 450 451 switch (f->state) { 452 case CLOSED: 453 case STOPPED: 454 fsm_sdata(f, TERMACK, id, NULL, 0); 455 break; 456 457 case REQSENT: 458 f->state = ACKRCVD; 459 f->retransmits = f->maxconfreqtransmits; 460 break; 461 462 case ACKRCVD: 463 /* Huh? an extra valid Ack? oh well... */ 464 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 465 fsm_sconfreq(f, 0); 466 f->state = REQSENT; 467 break; 468 469 case ACKSENT: 470 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 471 f->state = OPENED; 472 f->retransmits = f->maxconfreqtransmits; 473 if (f->callbacks->up) 474 (*f->callbacks->up)(f); /* Inform upper layers */ 475 break; 476 477 case OPENED: 478 /* Go down and restart negotiation */ 479 if (f->callbacks->down) 480 (*f->callbacks->down)(f); /* Inform upper layers */ 481 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 482 f->state = REQSENT; 483 break; 484 } 485} 486 487 488/* 489 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. 490 */ 491static void 492fsm_rconfnakrej(f, code, id, inp, len) 493 fsm *f; 494 int code, id; 495 u_char *inp; 496 int len; 497{ 498 int (*proc) __P((fsm *, u_char *, int)); 499 int ret; 500 501 if (id != f->reqid || f->seen_ack) /* Expected id? */ 502 return; /* Nope, toss... */ 503 proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; 504 if (!proc || !(ret = proc(f, inp, len))) { 505 /* Nak/reject is bad - ignore it */ 506 error("Received bad configure-nak/rej: %P", inp, len); 507 return; 508 } 509 f->seen_ack = 1; 510 511 switch (f->state) { 512 case CLOSED: 513 case STOPPED: 514 fsm_sdata(f, TERMACK, id, NULL, 0); 515 break; 516 517 case REQSENT: 518 case ACKSENT: 519 /* They didn't agree to what we wanted - try another request */ 520 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 521 if (ret < 0) 522 f->state = STOPPED; /* kludge for stopping CCP */ 523 else 524 fsm_sconfreq(f, 0); /* Send Configure-Request */ 525 break; 526 527 case ACKRCVD: 528 /* Got a Nak/reject when we had already had an Ack?? oh well... */ 529 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 530 fsm_sconfreq(f, 0); 531 f->state = REQSENT; 532 break; 533 534 case OPENED: 535 /* Go down and restart negotiation */ 536 if (f->callbacks->down) 537 (*f->callbacks->down)(f); /* Inform upper layers */ 538 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 539 f->state = REQSENT; 540 break; 541 } 542} 543 544 545/* 546 * fsm_rtermreq - Receive Terminate-Req. 547 */ 548static void 549fsm_rtermreq(f, id, p, len) 550 fsm *f; 551 int id; 552 u_char *p; 553 int len; 554{ 555 switch (f->state) { 556 case ACKRCVD: 557 case ACKSENT: 558 f->state = REQSENT; /* Start over but keep trying */ 559 break; 560 561 case OPENED: 562 if (len > 0) { 563 info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p); 564 } else 565 info("%s terminated by peer", PROTO_NAME(f)); 566 if (f->callbacks->down) 567 (*f->callbacks->down)(f); /* Inform upper layers */ 568 f->retransmits = 0; 569 f->state = STOPPING; 570 TIMEOUT(fsm_timeout, f, f->timeouttime); 571 break; 572 } 573 574 fsm_sdata(f, TERMACK, id, NULL, 0); 575} 576 577 578/* 579 * fsm_rtermack - Receive Terminate-Ack. 580 */ 581static void 582fsm_rtermack(f) 583 fsm *f; 584{ 585 switch (f->state) { 586 case CLOSING: 587 UNTIMEOUT(fsm_timeout, f); 588 f->state = CLOSED; 589 if( f->callbacks->finished ) 590 (*f->callbacks->finished)(f); 591 break; 592 case STOPPING: 593 UNTIMEOUT(fsm_timeout, f); 594 f->state = STOPPED; 595 if( f->callbacks->finished ) 596 (*f->callbacks->finished)(f); 597 break; 598 599 case ACKRCVD: 600 f->state = REQSENT; 601 break; 602 603 case OPENED: 604 if (f->callbacks->down) 605 (*f->callbacks->down)(f); /* Inform upper layers */ 606 fsm_sconfreq(f, 0); 607 break; 608 } 609} 610 611 612/* 613 * fsm_rcoderej - Receive an Code-Reject. 614 */ 615static void 616fsm_rcoderej(f, inp, len) 617 fsm *f; 618 u_char *inp; 619 int len; 620{ 621 u_char code, id; 622 623 if (len < HEADERLEN) { 624 FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!")); 625 return; 626 } 627 GETCHAR(code, inp); 628 GETCHAR(id, inp); 629 warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id); 630 631 if( f->state == ACKRCVD ) 632 f->state = REQSENT; 633} 634 635 636/* 637 * fsm_protreject - Peer doesn't speak this protocol. 638 * 639 * Treat this as a catastrophic error (RXJ-). 640 */ 641void 642fsm_protreject(f) 643 fsm *f; 644{ 645 switch( f->state ){ 646 case CLOSING: 647 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 648 /* fall through */ 649 case CLOSED: 650 f->state = CLOSED; 651 if( f->callbacks->finished ) 652 (*f->callbacks->finished)(f); 653 break; 654 655 case STOPPING: 656 case REQSENT: 657 case ACKRCVD: 658 case ACKSENT: 659 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 660 /* fall through */ 661 case STOPPED: 662 f->state = STOPPED; 663 if( f->callbacks->finished ) 664 (*f->callbacks->finished)(f); 665 break; 666 667 case OPENED: 668 if( f->callbacks->down ) 669 (*f->callbacks->down)(f); 670 671 /* Init restart counter, send Terminate-Request */ 672 f->retransmits = f->maxtermtransmits; 673 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 674 (u_char *) f->term_reason, f->term_reason_len); 675 TIMEOUT(fsm_timeout, f, f->timeouttime); 676 --f->retransmits; 677 678 f->state = STOPPING; 679 break; 680 681 default: 682 FSMDEBUG(("%s: Protocol-reject event in state %d!", 683 PROTO_NAME(f), f->state)); 684 } 685} 686 687 688/* 689 * fsm_sconfreq - Send a Configure-Request. 690 */ 691static void 692fsm_sconfreq(f, retransmit) 693 fsm *f; 694 int retransmit; 695{ 696 u_char *outp; 697 int cilen; 698 699 if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){ 700 /* Not currently negotiating - reset options */ 701 if( f->callbacks->resetci ) 702 (*f->callbacks->resetci)(f); 703 f->nakloops = 0; 704 } 705 706 if( !retransmit ){ 707 /* New request - reset retransmission counter, use new ID */ 708 f->retransmits = f->maxconfreqtransmits; 709 f->reqid = ++f->id; 710 } 711 712 f->seen_ack = 0; 713 714 /* 715 * Make up the request packet 716 */ 717 outp = outpacket_buf + PPP_HDRLEN + HEADERLEN; 718 if( f->callbacks->cilen && f->callbacks->addci ){ 719 cilen = (*f->callbacks->cilen)(f); 720 if( cilen > peer_mru[f->unit] - HEADERLEN ) 721 cilen = peer_mru[f->unit] - HEADERLEN; 722 if (f->callbacks->addci) 723 (*f->callbacks->addci)(f, outp, &cilen); 724 } else 725 cilen = 0; 726 727 /* send the request to our peer */ 728 fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); 729 730 /* start the retransmit timer */ 731 --f->retransmits; 732 TIMEOUT(fsm_timeout, f, f->timeouttime); 733} 734 735 736/* 737 * fsm_sdata - Send some data. 738 * 739 * Used for all packets sent to our peer by this module. 740 */ 741void 742fsm_sdata(f, code, id, data, datalen) 743 fsm *f; 744 u_char code, id; 745 u_char *data; 746 int datalen; 747{ 748 u_char *outp; 749 int outlen; 750 751 /* Adjust length to be smaller than MTU */ 752 outp = outpacket_buf; 753 if (datalen > peer_mru[f->unit] - HEADERLEN) 754 datalen = peer_mru[f->unit] - HEADERLEN; 755 if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) 756 BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); 757 outlen = datalen + HEADERLEN; 758 MAKEHEADER(outp, f->protocol); 759 PUTCHAR(code, outp); 760 PUTCHAR(id, outp); 761 PUTSHORT(outlen, outp); 762 output(f->unit, outpacket_buf, outlen + PPP_HDRLEN); 763} 764