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