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: fsm.c 241182 2011-02-17 21:50:03Z $" 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 f->state = STARTING; 154 if( f->callbacks->starting ) 155 (*f->callbacks->starting)(f); 156 break; 157 158 case CLOSED: 159 if( f->flags & OPT_SILENT ) 160 f->state = STOPPED; 161 else { 162 /* Send an initial configure-request */ 163 fsm_sconfreq(f, 0); 164 f->state = REQSENT; 165 } 166 break; 167 168 case CLOSING: 169 f->state = STOPPING; 170 /* fall through */ 171 case STOPPED: 172 case OPENED: 173 if( f->flags & OPT_RESTART ){ 174 fsm_lowerdown(f); 175 fsm_lowerup(f); 176 } 177 break; 178 } 179} 180 181 182/* 183 * fsm_close - Start closing connection. 184 * 185 * Cancel timeouts and either initiate close or possibly go directly to 186 * the CLOSED state. 187 */ 188void 189fsm_close(f, reason) 190 fsm *f; 191 char *reason; 192{ 193 f->term_reason = reason; 194 f->term_reason_len = (reason == NULL? 0: strlen(reason)); 195 switch( f->state ){ 196 case STARTING: 197 f->state = INITIAL; 198 break; 199 case STOPPED: 200 f->state = CLOSED; 201 break; 202 case STOPPING: 203 f->state = CLOSING; 204 break; 205 206 case REQSENT: 207 case ACKRCVD: 208 case ACKSENT: 209 case OPENED: 210 if( f->state != OPENED ) 211 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 212 else if( f->callbacks->down ) 213 (*f->callbacks->down)(f); /* Inform upper layers we're down */ 214 215 /* Init restart counter, send Terminate-Request */ 216 f->retransmits = f->maxtermtransmits; 217 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 218 (u_char *) f->term_reason, f->term_reason_len); 219 TIMEOUT(fsm_timeout, f, f->timeouttime); 220 --f->retransmits; 221 222 f->state = CLOSING; 223 break; 224 } 225} 226 227 228/* 229 * fsm_timeout - Timeout expired. 230 */ 231static void 232fsm_timeout(arg) 233 void *arg; 234{ 235 fsm *f = (fsm *) arg; 236 237 switch (f->state) { 238 case CLOSING: 239 case STOPPING: 240 if( f->retransmits <= 0 ){ 241 /* 242 * We've waited for an ack long enough. Peer probably heard us. 243 */ 244 f->state = (f->state == CLOSING)? CLOSED: STOPPED; 245 if( f->callbacks->finished ) 246 (*f->callbacks->finished)(f); 247 } else { 248 /* Send Terminate-Request */ 249 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 250 (u_char *) f->term_reason, f->term_reason_len); 251 TIMEOUT(fsm_timeout, f, f->timeouttime); 252 --f->retransmits; 253 } 254 break; 255 256 case REQSENT: 257 case ACKRCVD: 258 case ACKSENT: 259 if (f->retransmits <= 0) { 260 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f)); 261 f->state = STOPPED; 262 if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) 263 (*f->callbacks->finished)(f); 264 265 } else { 266 /* Retransmit the configure-request */ 267 if (f->callbacks->retransmit) 268 (*f->callbacks->retransmit)(f); 269 fsm_sconfreq(f, 1); /* Re-send Configure-Request */ 270 if( f->state == ACKRCVD ) 271 f->state = REQSENT; 272 } 273 break; 274 275 default: 276 FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state)); 277 } 278} 279 280 281/* 282 * fsm_input - Input packet. 283 */ 284void 285fsm_input(f, inpacket, l) 286 fsm *f; 287 u_char *inpacket; 288 int l; 289{ 290 u_char *inp; 291 u_char code, id; 292 int len; 293 294 /* 295 * Parse header (code, id and length). 296 * If packet too short, drop it. 297 */ 298 inp = inpacket; 299 if (l < HEADERLEN) { 300 FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol)); 301 return; 302 } 303 GETCHAR(code, inp); 304 GETCHAR(id, inp); 305 GETSHORT(len, inp); 306 if (len < HEADERLEN) { 307 FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol)); 308 return; 309 } 310 if (len > l) { 311 FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol)); 312 return; 313 } 314 len -= HEADERLEN; /* subtract header length */ 315 316 if( f->state == INITIAL || f->state == STARTING ){ 317 FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.", 318 f->protocol, f->state)); 319 return; 320 } 321 322 /* 323 * Action depends on code. 324 */ 325 switch (code) { 326 case CONFREQ: 327 fsm_rconfreq(f, id, inp, len); 328 break; 329 330 case CONFACK: 331 fsm_rconfack(f, id, inp, len); 332 break; 333 334 case CONFNAK: 335 case CONFREJ: 336 fsm_rconfnakrej(f, code, id, inp, len); 337 break; 338 339 case TERMREQ: 340 fsm_rtermreq(f, id, inp, len); 341 break; 342 343 case TERMACK: 344 fsm_rtermack(f); 345 break; 346 347 case CODEREJ: 348 fsm_rcoderej(f, inp, len); 349 break; 350 351 default: 352 if( !f->callbacks->extcode 353 || !(*f->callbacks->extcode)(f, code, id, inp, len) ) 354 fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); 355 break; 356 } 357} 358 359 360/* 361 * fsm_rconfreq - Receive Configure-Request. 362 */ 363static void 364fsm_rconfreq(f, id, inp, len) 365 fsm *f; 366 u_char id; 367 u_char *inp; 368 int len; 369{ 370 int code, reject_if_disagree; 371 372 switch( f->state ){ 373 case CLOSED: 374 /* Go away, we're closed */ 375 fsm_sdata(f, TERMACK, id, NULL, 0); 376 return; 377 case CLOSING: 378 case STOPPING: 379 return; 380 381 case OPENED: 382 /* Go down and restart negotiation */ 383 if( f->callbacks->down ) 384 (*f->callbacks->down)(f); /* Inform upper layers */ 385 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 386 break; 387 388 case STOPPED: 389 /* Negotiation started by our peer */ 390 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 391 f->state = REQSENT; 392 break; 393 } 394 395 /* 396 * Pass the requested configuration options 397 * to protocol-specific code for checking. 398 */ 399 if (f->callbacks->reqci){ /* Check CI */ 400 reject_if_disagree = (f->nakloops >= f->maxnakloops); 401 code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); 402 } else if (len) 403 code = CONFREJ; /* Reject all CI */ 404 else 405 code = CONFACK; 406 407 /* send the Ack, Nak or Rej to the peer */ 408 fsm_sdata(f, code, id, inp, len); 409 410 if (code == CONFACK) { 411 if (f->state == ACKRCVD) { 412 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 413 f->state = OPENED; 414 if (f->callbacks->up) 415 (*f->callbacks->up)(f); /* Inform upper layers */ 416 } else 417 f->state = ACKSENT; 418 f->nakloops = 0; 419 420 } else { 421 /* we sent CONFACK or CONFREJ */ 422 if (f->state != ACKRCVD) 423 f->state = REQSENT; 424 if( code == CONFNAK ) 425 ++f->nakloops; 426 } 427} 428 429 430/* 431 * fsm_rconfack - Receive Configure-Ack. 432 */ 433static void 434fsm_rconfack(f, id, inp, len) 435 fsm *f; 436 int id; 437 u_char *inp; 438 int len; 439{ 440 if (id != f->reqid || f->seen_ack) /* Expected id? */ 441 return; /* Nope, toss... */ 442 if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): 443 (len == 0)) ){ 444 /* Ack is bad - ignore it */ 445 error("Received bad configure-ack: %P", inp, len); 446 return; 447 } 448 f->seen_ack = 1; 449 450 switch (f->state) { 451 case CLOSED: 452 case STOPPED: 453 fsm_sdata(f, TERMACK, id, NULL, 0); 454 break; 455 456 case REQSENT: 457 f->state = ACKRCVD; 458 f->retransmits = f->maxconfreqtransmits; 459 break; 460 461 case ACKRCVD: 462 /* Huh? an extra valid Ack? oh well... */ 463 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 464 fsm_sconfreq(f, 0); 465 f->state = REQSENT; 466 break; 467 468 case ACKSENT: 469 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 470 f->state = OPENED; 471 f->retransmits = f->maxconfreqtransmits; 472 if (f->callbacks->up) 473 (*f->callbacks->up)(f); /* Inform upper layers */ 474 break; 475 476 case OPENED: 477 /* Go down and restart negotiation */ 478 if (f->callbacks->down) 479 (*f->callbacks->down)(f); /* Inform upper layers */ 480 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 481 f->state = REQSENT; 482 break; 483 } 484} 485 486 487/* 488 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. 489 */ 490static void 491fsm_rconfnakrej(f, code, id, inp, len) 492 fsm *f; 493 int code, id; 494 u_char *inp; 495 int len; 496{ 497 int (*proc) __P((fsm *, u_char *, int)); 498 int ret; 499 500 if (id != f->reqid || f->seen_ack) /* Expected id? */ 501 return; /* Nope, toss... */ 502 proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; 503 if (!proc || !(ret = proc(f, inp, len))) { 504 /* Nak/reject is bad - ignore it */ 505 error("Received bad configure-nak/rej: %P", inp, len); 506 return; 507 } 508 f->seen_ack = 1; 509 510 switch (f->state) { 511 case CLOSED: 512 case STOPPED: 513 fsm_sdata(f, TERMACK, id, NULL, 0); 514 break; 515 516 case REQSENT: 517 case ACKSENT: 518 /* They didn't agree to what we wanted - try another request */ 519 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 520 if (ret < 0) 521 f->state = STOPPED; /* kludge for stopping CCP */ 522 else 523 fsm_sconfreq(f, 0); /* Send Configure-Request */ 524 break; 525 526 case ACKRCVD: 527 /* Got a Nak/reject when we had already had an Ack?? oh well... */ 528 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 529 fsm_sconfreq(f, 0); 530 f->state = REQSENT; 531 break; 532 533 case OPENED: 534 /* Go down and restart negotiation */ 535 if (f->callbacks->down) 536 (*f->callbacks->down)(f); /* Inform upper layers */ 537 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 538 f->state = REQSENT; 539 break; 540 } 541} 542 543 544/* 545 * fsm_rtermreq - Receive Terminate-Req. 546 */ 547static void 548fsm_rtermreq(f, id, p, len) 549 fsm *f; 550 int id; 551 u_char *p; 552 int len; 553{ 554 switch (f->state) { 555 case ACKRCVD: 556 case ACKSENT: 557 f->state = REQSENT; /* Start over but keep trying */ 558 break; 559 560 case OPENED: 561 if (len > 0) { 562 info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p); 563 } else 564 info("%s terminated by peer", PROTO_NAME(f)); 565 if (f->callbacks->down) 566 (*f->callbacks->down)(f); /* Inform upper layers */ 567 f->retransmits = 0; 568 f->state = STOPPING; 569 TIMEOUT(fsm_timeout, f, f->timeouttime); 570 break; 571 } 572 573 fsm_sdata(f, TERMACK, id, NULL, 0); 574} 575 576 577/* 578 * fsm_rtermack - Receive Terminate-Ack. 579 */ 580static void 581fsm_rtermack(f) 582 fsm *f; 583{ 584 switch (f->state) { 585 case CLOSING: 586 UNTIMEOUT(fsm_timeout, f); 587 f->state = CLOSED; 588 if( f->callbacks->finished ) 589 (*f->callbacks->finished)(f); 590 break; 591 case STOPPING: 592 UNTIMEOUT(fsm_timeout, f); 593 f->state = STOPPED; 594 if( f->callbacks->finished ) 595 (*f->callbacks->finished)(f); 596 break; 597 598 case ACKRCVD: 599 f->state = REQSENT; 600 break; 601 602 case OPENED: 603 if (f->callbacks->down) 604 (*f->callbacks->down)(f); /* Inform upper layers */ 605 fsm_sconfreq(f, 0); 606 break; 607 } 608} 609 610 611/* 612 * fsm_rcoderej - Receive an Code-Reject. 613 */ 614static void 615fsm_rcoderej(f, inp, len) 616 fsm *f; 617 u_char *inp; 618 int len; 619{ 620 u_char code, id; 621 622 if (len < HEADERLEN) { 623 FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!")); 624 return; 625 } 626 GETCHAR(code, inp); 627 GETCHAR(id, inp); 628 warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id); 629 630 if( f->state == ACKRCVD ) 631 f->state = REQSENT; 632} 633 634 635/* 636 * fsm_protreject - Peer doesn't speak this protocol. 637 * 638 * Treat this as a catastrophic error (RXJ-). 639 */ 640void 641fsm_protreject(f) 642 fsm *f; 643{ 644 switch( f->state ){ 645 case CLOSING: 646 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 647 /* fall through */ 648 case CLOSED: 649 f->state = CLOSED; 650 if( f->callbacks->finished ) 651 (*f->callbacks->finished)(f); 652 break; 653 654 case STOPPING: 655 case REQSENT: 656 case ACKRCVD: 657 case ACKSENT: 658 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 659 /* fall through */ 660 case STOPPED: 661 f->state = STOPPED; 662 if( f->callbacks->finished ) 663 (*f->callbacks->finished)(f); 664 break; 665 666 case OPENED: 667 if( f->callbacks->down ) 668 (*f->callbacks->down)(f); 669 670 /* Init restart counter, send Terminate-Request */ 671 f->retransmits = f->maxtermtransmits; 672 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 673 (u_char *) f->term_reason, f->term_reason_len); 674 TIMEOUT(fsm_timeout, f, f->timeouttime); 675 --f->retransmits; 676 677 f->state = STOPPING; 678 break; 679 680 default: 681 FSMDEBUG(("%s: Protocol-reject event in state %d!", 682 PROTO_NAME(f), f->state)); 683 } 684} 685 686 687/* 688 * fsm_sconfreq - Send a Configure-Request. 689 */ 690static void 691fsm_sconfreq(f, retransmit) 692 fsm *f; 693 int retransmit; 694{ 695 u_char *outp; 696 int cilen; 697 698 if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){ 699 /* Not currently negotiating - reset options */ 700 if( f->callbacks->resetci ) 701 (*f->callbacks->resetci)(f); 702 f->nakloops = 0; 703 } 704 705 if( !retransmit ){ 706 /* New request - reset retransmission counter, use new ID */ 707 f->retransmits = f->maxconfreqtransmits; 708 f->reqid = ++f->id; 709 } 710 711 f->seen_ack = 0; 712 713 /* 714 * Make up the request packet 715 */ 716 outp = outpacket_buf + PPP_HDRLEN + HEADERLEN; 717 if( f->callbacks->cilen && f->callbacks->addci ){ 718 cilen = (*f->callbacks->cilen)(f); 719 if( cilen > peer_mru[f->unit] - HEADERLEN ) 720 cilen = peer_mru[f->unit] - HEADERLEN; 721 if (f->callbacks->addci) 722 (*f->callbacks->addci)(f, outp, &cilen); 723 } else 724 cilen = 0; 725 726 /* send the request to our peer */ 727 fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); 728 729 /* start the retransmit timer */ 730 --f->retransmits; 731 TIMEOUT(fsm_timeout, f, f->timeouttime); 732} 733 734 735/* 736 * fsm_sdata - Send some data. 737 * 738 * Used for all packets sent to our peer by this module. 739 */ 740void 741fsm_sdata(f, code, id, data, datalen) 742 fsm *f; 743 u_char code, id; 744 u_char *data; 745 int datalen; 746{ 747 u_char *outp; 748 int outlen; 749 750 /* Adjust length to be smaller than MTU */ 751 outp = outpacket_buf; 752 if (datalen > peer_mru[f->unit] - HEADERLEN) 753 datalen = peer_mru[f->unit] - HEADERLEN; 754 if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) 755 BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); 756 outlen = datalen + HEADERLEN; 757 MAKEHEADER(outp, f->protocol); 758 PUTCHAR(code, outp); 759 PUTCHAR(id, outp); 760 PUTSHORT(outlen, outp); 761 output(f->unit, outpacket_buf, outlen + PPP_HDRLEN); 762} 763