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