1/***************************************************************************** 2* fsm.c - Network Control Protocol Finite State Machine program file. 3* 4* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. 5* portions Copyright (c) 1997 by Global Election Systems Inc. 6* 7* The authors hereby grant permission to use, copy, modify, distribute, 8* and license this software and its documentation for any purpose, provided 9* that existing copyright notices are retained in all copies and that this 10* notice and the following disclaimer are included verbatim in any 11* distributions. No written agreement, license, or royalty fee is required 12* for any of the authorized uses. 13* 14* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR 15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24* 25****************************************************************************** 26* REVISION HISTORY 27* 28* 03-01-01 Marc Boucher <marc@mbsi.ca> 29* Ported to lwIP. 30* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc. 31* Original based on BSD fsm.c. 32*****************************************************************************/ 33/* 34 * fsm.c - {Link, IP} Control Protocol Finite State Machine. 35 * 36 * Copyright (c) 1989 Carnegie Mellon University. 37 * All rights reserved. 38 * 39 * Redistribution and use in source and binary forms are permitted 40 * provided that the above copyright notice and this paragraph are 41 * duplicated in all such forms and that any documentation, 42 * advertising materials, and other materials related to such 43 * distribution and use acknowledge that the software was developed 44 * by Carnegie Mellon University. The name of the 45 * University may not be used to endorse or promote products derived 46 * from this software without specific prior written permission. 47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 50 */ 51 52/* 53 * TODO: 54 * Randomize fsm id on link/init. 55 * Deal with variable outgoing MTU. 56 */ 57 58#include "lwip/opt.h" 59 60#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ 61 62#include "ppp.h" 63#include "pppdebug.h" 64 65#include "fsm.h" 66 67#include <string.h> 68 69#if PPP_DEBUG 70static const char *ppperr_strerr[] = { 71 "LS_INITIAL", /* LS_INITIAL 0 */ 72 "LS_STARTING", /* LS_STARTING 1 */ 73 "LS_CLOSED", /* LS_CLOSED 2 */ 74 "LS_STOPPED", /* LS_STOPPED 3 */ 75 "LS_CLOSING", /* LS_CLOSING 4 */ 76 "LS_STOPPING", /* LS_STOPPING 5 */ 77 "LS_REQSENT", /* LS_REQSENT 6 */ 78 "LS_ACKRCVD", /* LS_ACKRCVD 7 */ 79 "LS_ACKSENT", /* LS_ACKSENT 8 */ 80 "LS_OPENED" /* LS_OPENED 9 */ 81}; 82#endif /* PPP_DEBUG */ 83 84static void fsm_timeout (void *); 85static void fsm_rconfreq (fsm *, u_char, u_char *, int); 86static void fsm_rconfack (fsm *, int, u_char *, int); 87static void fsm_rconfnakrej (fsm *, int, int, u_char *, int); 88static void fsm_rtermreq (fsm *, int, u_char *, int); 89static void fsm_rtermack (fsm *); 90static void fsm_rcoderej (fsm *, u_char *, int); 91static void fsm_sconfreq (fsm *, int); 92 93#define PROTO_NAME(f) ((f)->callbacks->proto_name) 94 95int peer_mru[NUM_PPP]; 96 97 98/* 99 * fsm_init - Initialize fsm. 100 * 101 * Initialize fsm state. 102 */ 103void 104fsm_init(fsm *f) 105{ 106 f->state = LS_INITIAL; 107 f->flags = 0; 108 f->id = 0; /* XXX Start with random id? */ 109 f->timeouttime = FSM_DEFTIMEOUT; 110 f->maxconfreqtransmits = FSM_DEFMAXCONFREQS; 111 f->maxtermtransmits = FSM_DEFMAXTERMREQS; 112 f->maxnakloops = FSM_DEFMAXNAKLOOPS; 113 f->term_reason_len = 0; 114} 115 116 117/* 118 * fsm_lowerup - The lower layer is up. 119 */ 120void 121fsm_lowerup(fsm *f) 122{ 123 int oldState = f->state; 124 125 LWIP_UNUSED_ARG(oldState); 126 127 switch( f->state ) { 128 case LS_INITIAL: 129 f->state = LS_CLOSED; 130 break; 131 132 case LS_STARTING: 133 if( f->flags & OPT_SILENT ) { 134 f->state = LS_STOPPED; 135 } else { 136 /* Send an initial configure-request */ 137 fsm_sconfreq(f, 0); 138 f->state = LS_REQSENT; 139 } 140 break; 141 142 default: 143 FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n", 144 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 145 } 146 147 FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n", 148 PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); 149} 150 151 152/* 153 * fsm_lowerdown - The lower layer is down. 154 * 155 * Cancel all timeouts and inform upper layers. 156 */ 157void 158fsm_lowerdown(fsm *f) 159{ 160 int oldState = f->state; 161 162 LWIP_UNUSED_ARG(oldState); 163 164 switch( f->state ) { 165 case LS_CLOSED: 166 f->state = LS_INITIAL; 167 break; 168 169 case LS_STOPPED: 170 f->state = LS_STARTING; 171 if( f->callbacks->starting ) { 172 (*f->callbacks->starting)(f); 173 } 174 break; 175 176 case LS_CLOSING: 177 f->state = LS_INITIAL; 178 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 179 break; 180 181 case LS_STOPPING: 182 case LS_REQSENT: 183 case LS_ACKRCVD: 184 case LS_ACKSENT: 185 f->state = LS_STARTING; 186 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 187 break; 188 189 case LS_OPENED: 190 if( f->callbacks->down ) { 191 (*f->callbacks->down)(f); 192 } 193 f->state = LS_STARTING; 194 break; 195 196 default: 197 FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n", 198 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 199 } 200 201 FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n", 202 PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); 203} 204 205 206/* 207 * fsm_open - Link is allowed to come up. 208 */ 209void 210fsm_open(fsm *f) 211{ 212 int oldState = f->state; 213 214 LWIP_UNUSED_ARG(oldState); 215 216 switch( f->state ) { 217 case LS_INITIAL: 218 f->state = LS_STARTING; 219 if( f->callbacks->starting ) { 220 (*f->callbacks->starting)(f); 221 } 222 break; 223 224 case LS_CLOSED: 225 if( f->flags & OPT_SILENT ) { 226 f->state = LS_STOPPED; 227 } else { 228 /* Send an initial configure-request */ 229 fsm_sconfreq(f, 0); 230 f->state = LS_REQSENT; 231 } 232 break; 233 234 case LS_CLOSING: 235 f->state = LS_STOPPING; 236 /* fall through */ 237 case LS_STOPPED: 238 case LS_OPENED: 239 if( f->flags & OPT_RESTART ) { 240 fsm_lowerdown(f); 241 fsm_lowerup(f); 242 } 243 break; 244 } 245 246 FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n", 247 PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); 248} 249 250#if 0 /* backport pppd 2.4.4b1; */ 251/* 252 * terminate_layer - Start process of shutting down the FSM 253 * 254 * Cancel any timeout running, notify upper layers we're done, and 255 * send a terminate-request message as configured. 256 */ 257static void 258terminate_layer(fsm *f, int nextstate) 259{ 260 /* @todo */ 261} 262#endif 263 264/* 265 * fsm_close - Start closing connection. 266 * 267 * Cancel timeouts and either initiate close or possibly go directly to 268 * the LS_CLOSED state. 269 */ 270void 271fsm_close(fsm *f, char *reason) 272{ 273 int oldState = f->state; 274 275 LWIP_UNUSED_ARG(oldState); 276 277 f->term_reason = reason; 278 f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason)); 279 switch( f->state ) { 280 case LS_STARTING: 281 f->state = LS_INITIAL; 282 break; 283 case LS_STOPPED: 284 f->state = LS_CLOSED; 285 break; 286 case LS_STOPPING: 287 f->state = LS_CLOSING; 288 break; 289 290 case LS_REQSENT: 291 case LS_ACKRCVD: 292 case LS_ACKSENT: 293 case LS_OPENED: 294 if( f->state != LS_OPENED ) { 295 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 296 } else if( f->callbacks->down ) { 297 (*f->callbacks->down)(f); /* Inform upper layers we're down */ 298 } 299 /* Init restart counter, send Terminate-Request */ 300 f->retransmits = f->maxtermtransmits; 301 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 302 (u_char *) f->term_reason, f->term_reason_len); 303 TIMEOUT(fsm_timeout, f, f->timeouttime); 304 --f->retransmits; 305 306 f->state = LS_CLOSING; 307 break; 308 } 309 310 FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n", 311 PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); 312} 313 314 315/* 316 * fsm_timeout - Timeout expired. 317 */ 318static void 319fsm_timeout(void *arg) 320{ 321 fsm *f = (fsm *) arg; 322 323 switch (f->state) { 324 case LS_CLOSING: 325 case LS_STOPPING: 326 if( f->retransmits <= 0 ) { 327 FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n", 328 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 329 /* 330 * We've waited for an ack long enough. Peer probably heard us. 331 */ 332 f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED; 333 if( f->callbacks->finished ) { 334 (*f->callbacks->finished)(f); 335 } 336 } else { 337 FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n", 338 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 339 /* Send Terminate-Request */ 340 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 341 (u_char *) f->term_reason, f->term_reason_len); 342 TIMEOUT(fsm_timeout, f, f->timeouttime); 343 --f->retransmits; 344 } 345 break; 346 347 case LS_REQSENT: 348 case LS_ACKRCVD: 349 case LS_ACKSENT: 350 if (f->retransmits <= 0) { 351 FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n", 352 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 353 f->state = LS_STOPPED; 354 if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) { 355 (*f->callbacks->finished)(f); 356 } 357 } else { 358 FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n", 359 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 360 /* Retransmit the configure-request */ 361 if (f->callbacks->retransmit) { 362 (*f->callbacks->retransmit)(f); 363 } 364 fsm_sconfreq(f, 1); /* Re-send Configure-Request */ 365 if( f->state == LS_ACKRCVD ) { 366 f->state = LS_REQSENT; 367 } 368 } 369 break; 370 371 default: 372 FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n", 373 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 374 } 375} 376 377 378/* 379 * fsm_input - Input packet. 380 */ 381void 382fsm_input(fsm *f, u_char *inpacket, int l) 383{ 384 u_char *inp = inpacket; 385 u_char code, id; 386 int len; 387 388 /* 389 * Parse header (code, id and length). 390 * If packet too short, drop it. 391 */ 392 if (l < HEADERLEN) { 393 FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n", 394 f->protocol)); 395 return; 396 } 397 GETCHAR(code, inp); 398 GETCHAR(id, inp); 399 GETSHORT(len, inp); 400 if (len < HEADERLEN) { 401 FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n", 402 f->protocol)); 403 return; 404 } 405 if (len > l) { 406 FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n", 407 f->protocol)); 408 return; 409 } 410 len -= HEADERLEN; /* subtract header length */ 411 412 if( f->state == LS_INITIAL || f->state == LS_STARTING ) { 413 FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n", 414 f->protocol, f->state, ppperr_strerr[f->state])); 415 return; 416 } 417 FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l)); 418 /* 419 * Action depends on code. 420 */ 421 switch (code) { 422 case CONFREQ: 423 fsm_rconfreq(f, id, inp, len); 424 break; 425 426 case CONFACK: 427 fsm_rconfack(f, id, inp, len); 428 break; 429 430 case CONFNAK: 431 case CONFREJ: 432 fsm_rconfnakrej(f, code, id, inp, len); 433 break; 434 435 case TERMREQ: 436 fsm_rtermreq(f, id, inp, len); 437 break; 438 439 case TERMACK: 440 fsm_rtermack(f); 441 break; 442 443 case CODEREJ: 444 fsm_rcoderej(f, inp, len); 445 break; 446 447 default: 448 FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f))); 449 if( !f->callbacks->extcode || 450 !(*f->callbacks->extcode)(f, code, id, inp, len) ) { 451 fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); 452 } 453 break; 454 } 455} 456 457 458/* 459 * fsm_rconfreq - Receive Configure-Request. 460 */ 461static void 462fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) 463{ 464 int code, reject_if_disagree; 465 466 FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n", 467 PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); 468 switch( f->state ) { 469 case LS_CLOSED: 470 /* Go away, we're closed */ 471 fsm_sdata(f, TERMACK, id, NULL, 0); 472 return; 473 case LS_CLOSING: 474 case LS_STOPPING: 475 return; 476 477 case LS_OPENED: 478 /* Go down and restart negotiation */ 479 if( f->callbacks->down ) { 480 (*f->callbacks->down)(f); /* Inform upper layers */ 481 } 482 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 483 break; 484 485 case LS_STOPPED: 486 /* Negotiation started by our peer */ 487 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 488 f->state = LS_REQSENT; 489 break; 490 } 491 492 /* 493 * Pass the requested configuration options 494 * to protocol-specific code for checking. 495 */ 496 if (f->callbacks->reqci) { /* Check CI */ 497 reject_if_disagree = (f->nakloops >= f->maxnakloops); 498 code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); 499 } else if (len) { 500 code = CONFREJ; /* Reject all CI */ 501 } else { 502 code = CONFACK; 503 } 504 505 /* send the Ack, Nak or Rej to the peer */ 506 fsm_sdata(f, (u_char)code, id, inp, len); 507 508 if (code == CONFACK) { 509 if (f->state == LS_ACKRCVD) { 510 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 511 f->state = LS_OPENED; 512 if (f->callbacks->up) { 513 (*f->callbacks->up)(f); /* Inform upper layers */ 514 } 515 } else { 516 f->state = LS_ACKSENT; 517 } 518 f->nakloops = 0; 519 } else { 520 /* we sent CONFACK or CONFREJ */ 521 if (f->state != LS_ACKRCVD) { 522 f->state = LS_REQSENT; 523 } 524 if( code == CONFNAK ) { 525 ++f->nakloops; 526 } 527 } 528} 529 530 531/* 532 * fsm_rconfack - Receive Configure-Ack. 533 */ 534static void 535fsm_rconfack(fsm *f, int id, u_char *inp, int len) 536{ 537 FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n", 538 PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); 539 540 if (id != f->reqid || f->seen_ack) { /* Expected id? */ 541 return; /* Nope, toss... */ 542 } 543 if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) { 544 /* Ack is bad - ignore it */ 545 FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n", 546 PROTO_NAME(f), len)); 547 return; 548 } 549 f->seen_ack = 1; 550 551 switch (f->state) { 552 case LS_CLOSED: 553 case LS_STOPPED: 554 fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); 555 break; 556 557 case LS_REQSENT: 558 f->state = LS_ACKRCVD; 559 f->retransmits = f->maxconfreqtransmits; 560 break; 561 562 case LS_ACKRCVD: 563 /* Huh? an extra valid Ack? oh well... */ 564 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 565 fsm_sconfreq(f, 0); 566 f->state = LS_REQSENT; 567 break; 568 569 case LS_ACKSENT: 570 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 571 f->state = LS_OPENED; 572 f->retransmits = f->maxconfreqtransmits; 573 if (f->callbacks->up) { 574 (*f->callbacks->up)(f); /* Inform upper layers */ 575 } 576 break; 577 578 case LS_OPENED: 579 /* Go down and restart negotiation */ 580 if (f->callbacks->down) { 581 (*f->callbacks->down)(f); /* Inform upper layers */ 582 } 583 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 584 f->state = LS_REQSENT; 585 break; 586 } 587} 588 589 590/* 591 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. 592 */ 593static void 594fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) 595{ 596 int (*proc) (fsm *, u_char *, int); 597 int ret; 598 599 FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n", 600 PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); 601 602 if (id != f->reqid || f->seen_ack) { /* Expected id? */ 603 return; /* Nope, toss... */ 604 } 605 proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; 606 if (!proc || !((ret = proc(f, inp, len)))) { 607 /* Nak/reject is bad - ignore it */ 608 FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n", 609 PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); 610 return; 611 } 612 f->seen_ack = 1; 613 614 switch (f->state) { 615 case LS_CLOSED: 616 case LS_STOPPED: 617 fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); 618 break; 619 620 case LS_REQSENT: 621 case LS_ACKSENT: 622 /* They didn't agree to what we wanted - try another request */ 623 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 624 if (ret < 0) { 625 f->state = LS_STOPPED; /* kludge for stopping CCP */ 626 } else { 627 fsm_sconfreq(f, 0); /* Send Configure-Request */ 628 } 629 break; 630 631 case LS_ACKRCVD: 632 /* Got a Nak/reject when we had already had an Ack?? oh well... */ 633 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 634 fsm_sconfreq(f, 0); 635 f->state = LS_REQSENT; 636 break; 637 638 case LS_OPENED: 639 /* Go down and restart negotiation */ 640 if (f->callbacks->down) { 641 (*f->callbacks->down)(f); /* Inform upper layers */ 642 } 643 fsm_sconfreq(f, 0); /* Send initial Configure-Request */ 644 f->state = LS_REQSENT; 645 break; 646 } 647} 648 649 650/* 651 * fsm_rtermreq - Receive Terminate-Req. 652 */ 653static void 654fsm_rtermreq(fsm *f, int id, u_char *p, int len) 655{ 656 LWIP_UNUSED_ARG(p); 657 658 FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n", 659 PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); 660 661 switch (f->state) { 662 case LS_ACKRCVD: 663 case LS_ACKSENT: 664 f->state = LS_REQSENT; /* Start over but keep trying */ 665 break; 666 667 case LS_OPENED: 668 if (len > 0) { 669 FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p)); 670 } else { 671 FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f))); 672 } 673 if (f->callbacks->down) { 674 (*f->callbacks->down)(f); /* Inform upper layers */ 675 } 676 f->retransmits = 0; 677 f->state = LS_STOPPING; 678 TIMEOUT(fsm_timeout, f, f->timeouttime); 679 break; 680 } 681 682 fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); 683} 684 685 686/* 687 * fsm_rtermack - Receive Terminate-Ack. 688 */ 689static void 690fsm_rtermack(fsm *f) 691{ 692 FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n", 693 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 694 695 switch (f->state) { 696 case LS_CLOSING: 697 UNTIMEOUT(fsm_timeout, f); 698 f->state = LS_CLOSED; 699 if( f->callbacks->finished ) { 700 (*f->callbacks->finished)(f); 701 } 702 break; 703 704 case LS_STOPPING: 705 UNTIMEOUT(fsm_timeout, f); 706 f->state = LS_STOPPED; 707 if( f->callbacks->finished ) { 708 (*f->callbacks->finished)(f); 709 } 710 break; 711 712 case LS_ACKRCVD: 713 f->state = LS_REQSENT; 714 break; 715 716 case LS_OPENED: 717 if (f->callbacks->down) { 718 (*f->callbacks->down)(f); /* Inform upper layers */ 719 } 720 fsm_sconfreq(f, 0); 721 break; 722 default: 723 FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n", 724 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 725 } 726} 727 728 729/* 730 * fsm_rcoderej - Receive an Code-Reject. 731 */ 732static void 733fsm_rcoderej(fsm *f, u_char *inp, int len) 734{ 735 u_char code, id; 736 737 FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n", 738 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 739 740 if (len < HEADERLEN) { 741 FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n")); 742 return; 743 } 744 GETCHAR(code, inp); 745 GETCHAR(id, inp); 746 FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n", 747 PROTO_NAME(f), code, id)); 748 749 if( f->state == LS_ACKRCVD ) { 750 f->state = LS_REQSENT; 751 } 752} 753 754 755/* 756 * fsm_protreject - Peer doesn't speak this protocol. 757 * 758 * Treat this as a catastrophic error (RXJ-). 759 */ 760void 761fsm_protreject(fsm *f) 762{ 763 switch( f->state ) { 764 case LS_CLOSING: 765 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 766 /* fall through */ 767 case LS_CLOSED: 768 f->state = LS_CLOSED; 769 if( f->callbacks->finished ) { 770 (*f->callbacks->finished)(f); 771 } 772 break; 773 774 case LS_STOPPING: 775 case LS_REQSENT: 776 case LS_ACKRCVD: 777 case LS_ACKSENT: 778 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ 779 /* fall through */ 780 case LS_STOPPED: 781 f->state = LS_STOPPED; 782 if( f->callbacks->finished ) { 783 (*f->callbacks->finished)(f); 784 } 785 break; 786 787 case LS_OPENED: 788 if( f->callbacks->down ) { 789 (*f->callbacks->down)(f); 790 } 791 /* Init restart counter, send Terminate-Request */ 792 f->retransmits = f->maxtermtransmits; 793 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, 794 (u_char *) f->term_reason, f->term_reason_len); 795 TIMEOUT(fsm_timeout, f, f->timeouttime); 796 --f->retransmits; 797 798 f->state = LS_STOPPING; 799 break; 800 801 default: 802 FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n", 803 PROTO_NAME(f), f->state, ppperr_strerr[f->state])); 804 } 805} 806 807 808/* 809 * fsm_sconfreq - Send a Configure-Request. 810 */ 811static void 812fsm_sconfreq(fsm *f, int retransmit) 813{ 814 u_char *outp; 815 int cilen; 816 817 if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) { 818 /* Not currently negotiating - reset options */ 819 if( f->callbacks->resetci ) { 820 (*f->callbacks->resetci)(f); 821 } 822 f->nakloops = 0; 823 } 824 825 if( !retransmit ) { 826 /* New request - reset retransmission counter, use new ID */ 827 f->retransmits = f->maxconfreqtransmits; 828 f->reqid = ++f->id; 829 } 830 831 f->seen_ack = 0; 832 833 /* 834 * Make up the request packet 835 */ 836 outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN; 837 if( f->callbacks->cilen && f->callbacks->addci ) { 838 cilen = (*f->callbacks->cilen)(f); 839 if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) { 840 cilen = peer_mru[f->unit] - HEADERLEN; 841 } 842 if (f->callbacks->addci) { 843 (*f->callbacks->addci)(f, outp, &cilen); 844 } 845 } else { 846 cilen = 0; 847 } 848 849 /* send the request to our peer */ 850 fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); 851 852 /* start the retransmit timer */ 853 --f->retransmits; 854 TIMEOUT(fsm_timeout, f, f->timeouttime); 855 856 FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n", 857 PROTO_NAME(f), f->reqid)); 858} 859 860 861/* 862 * fsm_sdata - Send some data. 863 * 864 * Used for all packets sent to our peer by this module. 865 */ 866void 867fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen) 868{ 869 u_char *outp; 870 int outlen; 871 872 /* Adjust length to be smaller than MTU */ 873 outp = outpacket_buf[f->unit]; 874 if (datalen > peer_mru[f->unit] - (int)HEADERLEN) { 875 datalen = peer_mru[f->unit] - HEADERLEN; 876 } 877 if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) { 878 BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); 879 } 880 outlen = datalen + HEADERLEN; 881 MAKEHEADER(outp, f->protocol); 882 PUTCHAR(code, outp); 883 PUTCHAR(id, outp); 884 PUTSHORT(outlen, outp); 885 pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN); 886 FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n", 887 PROTO_NAME(f), code, id, outlen)); 888} 889 890#endif /* PPP_SUPPORT */ 891