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