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