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
fsm_init(f)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
fsm_lowerup(f)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
fsm_lowerdown(f)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
fsm_open(f)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
terminate_layer(f,nextstate)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
fsm_close(f,reason)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
fsm_timeout(arg)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
fsm_input(f,inpacket,l)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
fsm_rconfreq(f,id,inp,len)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 CONFACK 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
fsm_rconfack(f,id,inp,len)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
fsm_rconfnakrej(f,code,id,inp,len)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
fsm_rtermreq(f,id,p,len)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
fsm_rtermack(f)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
fsm_rcoderej(f,inp,len)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
fsm_protreject(f)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
fsm_sconfreq(f,retransmit)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
fsm_sdata(f,code,id,data,datalen)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 }
818