2 * fsm.c - {Link, IP} Control Protocol Finite State Machine.
4 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5 * Use is subject to license terms.
7 * Copyright (c) 1989 Carnegie Mellon University.
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.
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 $"
28 * Randomize fsm id on link/init.
29 * Deal with variable outgoing MTU.
34 #include <sys/types.h>
37 #endif /* NO_DRAND48 */
42 #if !defined(lint) && !defined(_lint)
43 static const char rcsid
[] = RCSID
;
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));
55 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
57 static int peer_mru
[NUM_PPP
];
60 fsm_state(int statenum
)
62 static const char *fsm_states
[] = { FSM__STATES
};
65 if (statenum
< 0 || statenum
>= Dim(fsm_states
)) {
66 (void) slprintf(buf
, sizeof (buf
), "unknown#%d", statenum
);
69 return fsm_states
[statenum
];
73 * fsm_init - Initialize fsm.
75 * Initialize fsm state.
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;
93 * fsm_lowerup - The lower layer is up.
105 if( f
->flags
& OPT_SILENT
)
108 /* Send an initial configure-request */
115 error("%s: Up event in state %s", PROTO_NAME(f
), fsm_state(f
->state
));
121 * fsm_lowerdown - The lower layer is down.
123 * Cancel all timeouts and inform upper layers.
136 if (f
->callbacks
->starting
!= NULL
)
137 (*f
->callbacks
->starting
)(f
);
142 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
150 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
155 if (f
->callbacks
->down
!= NULL
)
156 (*f
->callbacks
->down
)(f
);
160 dbglog("%s: Down event in state %s", PROTO_NAME(f
),
161 fsm_state(f
->state
));
167 * fsm_open - Link is allowed to come up.
176 if (f
->callbacks
->starting
!= NULL
)
177 (*f
->callbacks
->starting
)(f
);
181 if( f
->flags
& OPT_SILENT
)
184 /* Send an initial configure-request */
196 if( f
->flags
& OPT_RESTART
){
206 /* explicitly do nothing here. */
213 * fsm_close - Start closing connection.
215 * Cancel timeouts and either initiate close or possibly go directly to
223 int prevstate
= f
->state
;
225 f
->term_reason
= reason
;
226 f
->term_reason_len
= (reason
== NULL
? 0: strlen(reason
));
230 if (f
->callbacks
->finished
!= NULL
)
231 (*f
->callbacks
->finished
)(f
);
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 */
252 * Note that this-layer-down means "stop transmitting."
253 * This-layer-finished means "stop everything."
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
);
267 /* explicitly do nothing here. */
274 * fsm_timeout - Timeout expired.
280 fsm
*f
= (fsm
*) arg
;
285 if( f
->retransmits
<= 0 ){
287 * We've waited for an ack long enough. Peer probably heard us.
289 f
->state
= (f
->state
== CLOSING
)? CLOSED
: STOPPED
;
290 if (f
->callbacks
->finished
!= NULL
)
291 (*f
->callbacks
->finished
)(f
);
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
);
304 if (f
->retransmits
<= 0) {
305 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f
));
307 if (!(f
->flags
& OPT_PASSIVE
) && f
->callbacks
->finished
!= NULL
)
308 (*f
->callbacks
->finished
)(f
);
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
)
321 fatal("%s: Timeout event in state %s!", PROTO_NAME(f
),
322 fsm_state(f
->state
));
328 * fsm_input - Input packet.
331 fsm_input(f
, inpacket
, l
)
341 * Parse header (code, id and length).
342 * If packet too short, drop it.
346 error("%s packet: discard; too small (%d < %d)", PROTO_NAME(f
), l
,
353 if (len
< HEADERLEN
) {
354 error("%s packet: discard; invalid length (%d < %d)", PROTO_NAME(f
),
359 error("%s packet: discard; truncated (%d > %d)", PROTO_NAME(f
), len
,
363 len
-= HEADERLEN
; /* subtract header length */
365 if (f
->state
== INITIAL
|| f
->state
== STARTING
) {
366 dbglog("%s: discarded packet in state %s", PROTO_NAME(f
),
367 fsm_state(f
->state
));
372 * Action depends on code.
376 fsm_rconfreq(f
, id
, inp
, len
);
380 fsm_rconfack(f
, id
, inp
, len
);
385 fsm_rconfnakrej(f
, code
, id
, inp
, len
);
389 fsm_rtermreq(f
, id
, inp
, len
);
397 fsm_rcoderej(f
, inp
, len
);
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
);
410 * fsm_rconfreq - Receive Configure-Request.
413 fsm_rconfreq(f
, id
, inp
, len
)
419 int code
, reject_if_disagree
;
423 /* Go away, we're closed */
424 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
429 dbglog("%s: discarded Configure-Request in state %s", PROTO_NAME(f
),
430 fsm_state(f
->state
));
434 /* Go down and restart negotiation */
435 if (f
->callbacks
->down
!= NULL
)
436 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
441 if (inp
>= outpacket_buf
&& inp
< outpacket_buf
+PPP_MRU
+PPP_HDRLEN
)
442 fatal("bad pointer");
446 * Pass the requested configuration options
447 * to protocol-specific code for checking.
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
);
453 code
= CODE_CONFREJ
; /* Reject all CI */
457 /* Allow NCP to do fancy footwork, such as reinitializing. */
461 if (f
->state
== OPENED
|| f
->state
== STOPPED
)
462 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
464 /* send the Ack, Nak or Rej to the peer */
465 fsm_sdata(f
, code
, id
, inp
, len
);
467 if (code
== CODE_CONFACK
) {
468 /* RFC 1661 event RCR+ */
469 if (f
->state
== ACKRCVD
) {
470 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
472 if (f
->callbacks
->up
!= NULL
)
473 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
479 /* RFC 1661 event RCR- */
480 /* (we sent CODE_CONFNAK or CODE_CONFREJ) */
481 if (f
->state
!= ACKRCVD
)
483 if( code
== CODE_CONFNAK
)
490 * fsm_rconfack - Receive Configure-Ack.
493 fsm_rconfack(f
, id
, inp
, len
)
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
):
503 /* Ack is bad - ignore it */
504 error("Received bad configure-ack: %P", inp
, len
);
512 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
517 f
->retransmits
= f
->maxconfreqtransmits
;
521 /* Huh? an extra valid Ack? oh well... */
522 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
528 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
530 f
->retransmits
= f
->maxconfreqtransmits
;
531 if (f
->callbacks
->up
!= NULL
)
532 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
536 /* Go down and restart negotiation */
537 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
539 if (f
->callbacks
->down
!= NULL
)
540 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
547 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
550 fsm_rconfnakrej(f
, code
, id
, inp
, len
)
556 int (*proc
) __P((fsm
*, u_char
*, int));
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
);
572 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
577 /* They didn't agree to what we wanted - try another request */
578 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
580 f
->state
= STOPPED
; /* kludge for stopping CCP */
582 fsm_sconfreq(f
, 0); /* Send Configure-Request */
586 /* Got a Nak/reject when we had already had an Ack?? oh well... */
587 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
593 /* Go down and restart negotiation */
594 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
596 if (f
->callbacks
->down
!= NULL
)
597 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
604 * fsm_rtermreq - Receive Terminate-Req.
607 fsm_rtermreq(f
, id
, p
, len
)
616 f
->state
= REQSENT
; /* Start over but keep trying */
621 info("%s terminated by peer (%0.*v)", PROTO_NAME(f
), len
, p
);
623 info("%s terminated by peer", PROTO_NAME(f
));
626 if (f
->callbacks
->down
!= NULL
)
627 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
629 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
633 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
638 * fsm_rtermack - Receive Terminate-Ack.
646 UNTIMEOUT(fsm_timeout
, f
);
648 if (f
->callbacks
->finished
!= NULL
)
649 (*f
->callbacks
->finished
)(f
);
652 UNTIMEOUT(fsm_timeout
, f
);
654 if (f
->callbacks
->finished
!= NULL
)
655 (*f
->callbacks
->finished
)(f
);
665 if (f
->callbacks
->down
!= NULL
)
666 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
673 * fsm_rcoderej - Receive a Code-Reject.
676 fsm_rcoderej(f
, inp
, len
)
684 if (len
< HEADERLEN
) {
685 error("%s: Code-Reject too short (%d < %d)", PROTO_NAME(f
), len
,
692 warn("%s: Rcvd Code-Reject for %s id %d", PROTO_NAME(f
),
693 code_name(code
,0), id
);
695 setbit(f
->codemask
, code
);
697 /* Let the protocol know what happened. */
698 if (f
->callbacks
->codereject
!= NULL
) {
699 seriouserr
= (*f
->callbacks
->codereject
)(f
,code
,id
,inp
,len
);
702 * By default, it's RXJ- for well-known codes and RXJ+ for
705 seriouserr
= (code
>= CODE_CONFREQ
&& code
<= CODE_CODEREJ
);
709 /* RXJ- -- shut down the protocol. */
712 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
716 if (f
->callbacks
->finished
!= NULL
)
717 (*f
->callbacks
->finished
)(f
);
724 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
728 if (f
->callbacks
->finished
!= NULL
)
729 (*f
->callbacks
->finished
)(f
);
734 if (f
->callbacks
->down
!= NULL
)
735 (*f
->callbacks
->down
)(f
);
737 if (f
->term_reason
== NULL
) {
738 f
->term_reason
= "unacceptable Code-Reject received";
739 f
->term_reason_len
= strlen(f
->term_reason
);
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
);
751 fatal("state error");
754 /* RXJ+ -- just back up from Ack-Rcvd to Req-Sent. */
755 if (f
->state
== ACKRCVD
)
762 * fsm_protreject - Peer doesn't speak this protocol.
764 * Treat this as a catastrophic error (RXJ-).
772 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
776 if (f
->callbacks
->finished
!= NULL
)
777 (*f
->callbacks
->finished
)(f
);
784 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
788 if (f
->callbacks
->finished
!= NULL
)
789 (*f
->callbacks
->finished
)(f
);
794 if (f
->callbacks
->down
!= NULL
)
795 (*f
->callbacks
->down
)(f
);
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
);
806 dbglog("%s: Protocol-Reject in state %s", PROTO_NAME(f
),
807 fsm_state(f
->state
));
813 * fsm_sconfreq - Send a Configure-Request.
816 fsm_sconfreq(f
, retransmit
)
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
);
831 /* New request - reset retransmission counter, use new ID */
832 f
->retransmits
= f
->maxconfreqtransmits
;
839 * Make up the request packet
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
;
847 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
850 if (f
->callbacks
->addci
!= NULL
)
851 (*f
->callbacks
->addci
)(f
, outp
, &cilen
);
855 /* send the request to our peer */
856 fsm_sdata(f
, CODE_CONFREQ
, f
->reqid
, outp
, cilen
);
858 /* start the retransmit timer */
860 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
865 * fsm_sdata - Send some data.
867 * Used for all packets sent to our peer by this module.
870 fsm_sdata(f
, code
, id
, data
, datalen
)
879 if (isset(f
->codemask
,code
)) {
880 dbglog("%s: Peer has rejected %s; not sending another",
881 PROTO_NAME(f
), code_name(code
,0));
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
);
895 PUTSHORT(outlen
, outp
);
896 output(f
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
900 * fsm_setpeermru - Set our idea of the peer's mru
902 * Used by routines in lcp.c which negotiate this value.
905 fsm_setpeermru(unit
, mru
)
909 if (unit
>= NUM_PPP
) {
910 dbglog("fsm_setpeermru: unit out of bounds");
912 peer_mru
[unit
] = mru
;