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