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