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