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