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