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