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