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 static const char rcsid
[] = RCSID
;
44 static void fsm_timeout
__P((void *));
45 static void fsm_rconfreq
__P((fsm
*, int, u_char
*, int));
46 static void fsm_rconfack
__P((fsm
*, int, u_char
*, int));
47 static void fsm_rconfnakrej
__P((fsm
*, int, int, u_char
*, int));
48 static void fsm_rtermreq
__P((fsm
*, int, u_char
*, int));
49 static void fsm_rtermack
__P((fsm
*));
50 static void fsm_rcoderej
__P((fsm
*, u_char
*, int));
51 static void fsm_sconfreq
__P((fsm
*, int));
53 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
55 static int peer_mru
[NUM_PPP
];
58 fsm_state(int statenum
)
60 static const char *fsm_states
[] = { FSM__STATES
};
63 if (statenum
< 0 || statenum
>= Dim(fsm_states
)) {
64 (void) slprintf(buf
, sizeof (buf
), "unknown#%d", statenum
);
67 return fsm_states
[statenum
];
71 * fsm_init - Initialize fsm.
73 * Initialize fsm state.
81 f
->id
= (uchar_t
)(drand48() * 0xFF); /* Start with random id */
82 f
->timeouttime
= DEFTIMEOUT
;
83 f
->maxconfreqtransmits
= DEFMAXCONFREQS
;
84 f
->maxtermtransmits
= DEFMAXTERMREQS
;
85 f
->maxnakloops
= DEFMAXNAKLOOPS
;
86 f
->term_reason_len
= 0;
91 * fsm_lowerup - The lower layer is up.
103 if( f
->flags
& OPT_SILENT
)
106 /* Send an initial configure-request */
113 error("%s: Up event in state %s", PROTO_NAME(f
), fsm_state(f
->state
));
119 * fsm_lowerdown - The lower layer is down.
121 * Cancel all timeouts and inform upper layers.
134 if (f
->callbacks
->starting
!= NULL
)
135 (*f
->callbacks
->starting
)(f
);
140 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
148 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
153 if (f
->callbacks
->down
!= NULL
)
154 (*f
->callbacks
->down
)(f
);
158 dbglog("%s: Down event in state %s", PROTO_NAME(f
),
159 fsm_state(f
->state
));
165 * fsm_open - Link is allowed to come up.
174 if (f
->callbacks
->starting
!= NULL
)
175 (*f
->callbacks
->starting
)(f
);
179 if( f
->flags
& OPT_SILENT
)
182 /* Send an initial configure-request */
194 if( f
->flags
& OPT_RESTART
){
204 /* explicitly do nothing here. */
211 * fsm_close - Start closing connection.
213 * Cancel timeouts and either initiate close or possibly go directly to
221 int prevstate
= f
->state
;
223 f
->term_reason
= reason
;
224 f
->term_reason_len
= (reason
== NULL
? 0: strlen(reason
));
228 if (f
->callbacks
->finished
!= NULL
)
229 (*f
->callbacks
->finished
)(f
);
245 if (prevstate
!= OPENED
)
246 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
247 else if (f
->callbacks
->down
!= NULL
)
248 (*f
->callbacks
->down
)(f
); /* Inform upper layers we're down */
250 * Note that this-layer-down means "stop transmitting."
251 * This-layer-finished means "stop everything."
254 /* Init restart counter, send Terminate-Request */
255 f
->retransmits
= f
->maxtermtransmits
;
256 fsm_sdata(f
, CODE_TERMREQ
, f
->reqid
= ++f
->id
,
257 (u_char
*) f
->term_reason
, f
->term_reason_len
);
258 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
265 /* explicitly do nothing here. */
272 * fsm_timeout - Timeout expired.
278 fsm
*f
= (fsm
*) arg
;
283 if( f
->retransmits
<= 0 ){
285 * We've waited for an ack long enough. Peer probably heard us.
287 f
->state
= (f
->state
== CLOSING
)? CLOSED
: STOPPED
;
288 if (f
->callbacks
->finished
!= NULL
)
289 (*f
->callbacks
->finished
)(f
);
291 /* Send Terminate-Request */
292 fsm_sdata(f
, CODE_TERMREQ
, f
->reqid
= ++f
->id
,
293 (u_char
*) f
->term_reason
, f
->term_reason_len
);
294 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
302 if (f
->retransmits
<= 0) {
303 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f
));
305 if (!(f
->flags
& OPT_PASSIVE
) && f
->callbacks
->finished
!= NULL
)
306 (*f
->callbacks
->finished
)(f
);
309 /* Retransmit the configure-request */
310 if (f
->callbacks
->retransmit
!= NULL
)
311 (*f
->callbacks
->retransmit
)(f
);
312 fsm_sconfreq(f
, 1); /* Re-send Configure-Request */
313 if( f
->state
== ACKRCVD
)
319 fatal("%s: Timeout event in state %s!", PROTO_NAME(f
),
320 fsm_state(f
->state
));
326 * fsm_input - Input packet.
329 fsm_input(f
, inpacket
, l
)
339 * Parse header (code, id and length).
340 * If packet too short, drop it.
344 error("%s packet: discard; too small (%d < %d)", PROTO_NAME(f
), l
,
351 if (len
< HEADERLEN
) {
352 error("%s packet: discard; invalid length (%d < %d)", PROTO_NAME(f
),
357 error("%s packet: discard; truncated (%d > %d)", PROTO_NAME(f
), len
,
361 len
-= HEADERLEN
; /* subtract header length */
363 if (f
->state
== INITIAL
|| f
->state
== STARTING
) {
364 dbglog("%s: discarded packet in state %s", PROTO_NAME(f
),
365 fsm_state(f
->state
));
370 * Action depends on code.
374 fsm_rconfreq(f
, id
, inp
, len
);
378 fsm_rconfack(f
, id
, inp
, len
);
383 fsm_rconfnakrej(f
, code
, id
, inp
, len
);
387 fsm_rtermreq(f
, id
, inp
, len
);
395 fsm_rcoderej(f
, inp
, len
);
399 if (f
->callbacks
->extcode
== NULL
||
400 !(*f
->callbacks
->extcode
)(f
, code
, id
, inp
, len
))
401 fsm_sdata(f
, CODE_CODEREJ
, ++f
->id
, inpacket
, len
+ HEADERLEN
);
408 * fsm_rconfreq - Receive Configure-Request.
411 fsm_rconfreq(f
, id
, inp
, len
)
417 int code
, reject_if_disagree
;
421 /* Go away, we're closed */
422 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
427 dbglog("%s: discarded Configure-Request in state %s", PROTO_NAME(f
),
428 fsm_state(f
->state
));
432 /* Go down and restart negotiation */
433 if (f
->callbacks
->down
!= NULL
)
434 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
439 if (inp
>= outpacket_buf
&& inp
< outpacket_buf
+PPP_MRU
+PPP_HDRLEN
)
440 fatal("bad pointer");
444 * Pass the requested configuration options
445 * to protocol-specific code for checking.
447 if (f
->callbacks
->reqci
!= NULL
) { /* Check CI */
448 reject_if_disagree
= (f
->nakloops
>= f
->maxnakloops
);
449 code
= (*f
->callbacks
->reqci
)(f
, inp
, &len
, reject_if_disagree
);
451 code
= CODE_CONFREJ
; /* Reject all CI */
455 /* Allow NCP to do fancy footwork, such as reinitializing. */
459 if (f
->state
== OPENED
|| f
->state
== STOPPED
)
460 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
462 /* send the Ack, Nak or Rej to the peer */
463 fsm_sdata(f
, code
, id
, inp
, len
);
465 if (code
== CODE_CONFACK
) {
466 /* RFC 1661 event RCR+ */
467 if (f
->state
== ACKRCVD
) {
468 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
470 if (f
->callbacks
->up
!= NULL
)
471 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
477 /* RFC 1661 event RCR- */
478 /* (we sent CODE_CONFNAK or CODE_CONFREJ) */
479 if (f
->state
!= ACKRCVD
)
481 if( code
== CODE_CONFNAK
)
488 * fsm_rconfack - Receive Configure-Ack.
491 fsm_rconfack(f
, id
, inp
, len
)
497 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
498 return; /* Nope, toss... */
499 if( !(f
->callbacks
->ackci
!= NULL
? (*f
->callbacks
->ackci
)(f
, inp
, len
):
501 /* Ack is bad - ignore it */
502 error("Received bad configure-ack: %P", inp
, len
);
510 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
515 f
->retransmits
= f
->maxconfreqtransmits
;
519 /* Huh? an extra valid Ack? oh well... */
520 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
526 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
528 f
->retransmits
= f
->maxconfreqtransmits
;
529 if (f
->callbacks
->up
!= NULL
)
530 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
534 /* Go down and restart negotiation */
535 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
537 if (f
->callbacks
->down
!= NULL
)
538 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
545 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
548 fsm_rconfnakrej(f
, code
, id
, inp
, len
)
554 int (*proc
) __P((fsm
*, u_char
*, int));
557 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
558 return; /* Nope, toss... */
559 proc
= (code
== CODE_CONFNAK
)? f
->callbacks
->nakci
: f
->callbacks
->rejci
;
560 if (proc
== NULL
|| !(ret
= proc(f
, inp
, len
))) {
561 /* Nak/reject is bad - ignore it */
562 error("Received bad configure-nak/rej: %P", inp
, len
);
570 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
575 /* They didn't agree to what we wanted - try another request */
576 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
578 f
->state
= STOPPED
; /* kludge for stopping CCP */
580 fsm_sconfreq(f
, 0); /* Send Configure-Request */
584 /* Got a Nak/reject when we had already had an Ack?? oh well... */
585 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
591 /* Go down and restart negotiation */
592 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
594 if (f
->callbacks
->down
!= NULL
)
595 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
602 * fsm_rtermreq - Receive Terminate-Req.
605 fsm_rtermreq(f
, id
, p
, len
)
614 f
->state
= REQSENT
; /* Start over but keep trying */
619 info("%s terminated by peer (%0.*v)", PROTO_NAME(f
), len
, p
);
621 info("%s terminated by peer", PROTO_NAME(f
));
624 if (f
->callbacks
->down
!= NULL
)
625 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
627 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
631 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
636 * fsm_rtermack - Receive Terminate-Ack.
644 UNTIMEOUT(fsm_timeout
, f
);
646 if (f
->callbacks
->finished
!= NULL
)
647 (*f
->callbacks
->finished
)(f
);
650 UNTIMEOUT(fsm_timeout
, f
);
652 if (f
->callbacks
->finished
!= NULL
)
653 (*f
->callbacks
->finished
)(f
);
663 if (f
->callbacks
->down
!= NULL
)
664 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
671 * fsm_rcoderej - Receive a Code-Reject.
674 fsm_rcoderej(f
, inp
, len
)
682 if (len
< HEADERLEN
) {
683 error("%s: Code-Reject too short (%d < %d)", PROTO_NAME(f
), len
,
690 warn("%s: Rcvd Code-Reject for %s id %d", PROTO_NAME(f
),
691 code_name(code
,0), id
);
693 setbit(f
->codemask
, code
);
695 /* Let the protocol know what happened. */
696 if (f
->callbacks
->codereject
!= NULL
) {
697 seriouserr
= (*f
->callbacks
->codereject
)(f
,code
,id
,inp
,len
);
700 * By default, it's RXJ- for well-known codes and RXJ+ for
703 seriouserr
= (code
>= CODE_CONFREQ
&& code
<= CODE_CODEREJ
);
707 /* RXJ- -- shut down the protocol. */
710 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
714 if (f
->callbacks
->finished
!= NULL
)
715 (*f
->callbacks
->finished
)(f
);
722 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
726 if (f
->callbacks
->finished
!= NULL
)
727 (*f
->callbacks
->finished
)(f
);
732 if (f
->callbacks
->down
!= NULL
)
733 (*f
->callbacks
->down
)(f
);
735 if (f
->term_reason
== NULL
) {
736 f
->term_reason
= "unacceptable Code-Reject received";
737 f
->term_reason_len
= strlen(f
->term_reason
);
740 /* Init restart counter, send Terminate-Request */
741 f
->retransmits
= f
->maxtermtransmits
;
742 fsm_sdata(f
, CODE_TERMREQ
, f
->reqid
= ++f
->id
,
743 (u_char
*) f
->term_reason
, f
->term_reason_len
);
744 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
749 fatal("state error");
752 /* RXJ+ -- just back up from Ack-Rcvd to Req-Sent. */
753 if (f
->state
== ACKRCVD
)
760 * fsm_protreject - Peer doesn't speak this protocol.
762 * Treat this as a catastrophic error (RXJ-).
770 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
774 if (f
->callbacks
->finished
!= NULL
)
775 (*f
->callbacks
->finished
)(f
);
782 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
786 if (f
->callbacks
->finished
!= NULL
)
787 (*f
->callbacks
->finished
)(f
);
792 if (f
->callbacks
->down
!= NULL
)
793 (*f
->callbacks
->down
)(f
);
795 /* Init restart counter, send Terminate-Request */
796 f
->retransmits
= f
->maxtermtransmits
;
797 fsm_sdata(f
, CODE_TERMREQ
, f
->reqid
= ++f
->id
,
798 (u_char
*) f
->term_reason
, f
->term_reason_len
);
799 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
804 dbglog("%s: Protocol-Reject in state %s", PROTO_NAME(f
),
805 fsm_state(f
->state
));
811 * fsm_sconfreq - Send a Configure-Request.
814 fsm_sconfreq(f
, retransmit
)
821 if( f
->state
!= REQSENT
&& f
->state
!= ACKRCVD
&& f
->state
!= ACKSENT
){
822 /* Not currently negotiating - reset options */
823 if (f
->callbacks
->resetci
!= NULL
)
824 (*f
->callbacks
->resetci
)(f
);
829 /* New request - reset retransmission counter, use new ID */
830 f
->retransmits
= f
->maxconfreqtransmits
;
837 * Make up the request packet
839 outp
= outpacket_buf
+ PPP_HDRLEN
+ HEADERLEN
;
840 if (f
->callbacks
->cilen
!= NULL
) {
841 cilen
= (*f
->callbacks
->cilen
)(f
);
842 if (cilen
> peer_mru
[f
->unit
] - HEADERLEN
)
843 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
845 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
848 if (f
->callbacks
->addci
!= NULL
)
849 (*f
->callbacks
->addci
)(f
, outp
, &cilen
);
853 /* send the request to our peer */
854 fsm_sdata(f
, CODE_CONFREQ
, f
->reqid
, outp
, cilen
);
856 /* start the retransmit timer */
858 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
863 * fsm_sdata - Send some data.
865 * Used for all packets sent to our peer by this module.
868 fsm_sdata(f
, code
, id
, data
, datalen
)
877 if (isset(f
->codemask
,code
)) {
878 dbglog("%s: Peer has rejected %s; not sending another",
879 PROTO_NAME(f
), code_name(code
,0));
883 /* Adjust length to be smaller than MTU */
884 outp
= outpacket_buf
;
885 if (datalen
> peer_mru
[f
->unit
] - HEADERLEN
)
886 datalen
= peer_mru
[f
->unit
] - HEADERLEN
;
887 if (datalen
&& data
!= outp
+ PPP_HDRLEN
+ HEADERLEN
)
888 BCOPY(data
, outp
+ PPP_HDRLEN
+ HEADERLEN
, datalen
);
889 outlen
= datalen
+ HEADERLEN
;
890 MAKEHEADER(outp
, f
->protocol
);
893 PUTSHORT(outlen
, outp
);
894 output(f
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
898 * fsm_setpeermru - Set our idea of the peer's mru
900 * Used by routines in lcp.c which negotiate this value.
903 fsm_setpeermru(unit
, mru
)
907 if (unit
>= NUM_PPP
) {
908 dbglog("fsm_setpeermru: unit out of bounds");
910 peer_mru
[unit
] = mru
;