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