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