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.
25 * Randomize fsm id on link/init.
26 * Deal with variable outgoing MTU.
31 #include <sys/types.h>
34 #endif /* NO_DRAND48 */
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));
48 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
50 static int peer_mru
[NUM_PPP
];
53 fsm_state(int statenum
)
55 static const char *fsm_states
[] = { FSM__STATES
};
58 if (statenum
< 0 || statenum
>= Dim(fsm_states
)) {
59 (void) slprintf(buf
, sizeof (buf
), "unknown#%d", statenum
);
62 return fsm_states
[statenum
];
66 * fsm_init - Initialize fsm.
68 * Initialize fsm state.
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;
86 * fsm_lowerup - The lower layer is up.
98 if( f
->flags
& OPT_SILENT
)
101 /* Send an initial configure-request */
108 error("%s: Up event in state %s", PROTO_NAME(f
), fsm_state(f
->state
));
114 * fsm_lowerdown - The lower layer is down.
116 * Cancel all timeouts and inform upper layers.
129 if (f
->callbacks
->starting
!= NULL
)
130 (*f
->callbacks
->starting
)(f
);
135 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
143 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
148 if (f
->callbacks
->down
!= NULL
)
149 (*f
->callbacks
->down
)(f
);
153 dbglog("%s: Down event in state %s", PROTO_NAME(f
),
154 fsm_state(f
->state
));
160 * fsm_open - Link is allowed to come up.
169 if (f
->callbacks
->starting
!= NULL
)
170 (*f
->callbacks
->starting
)(f
);
174 if( f
->flags
& OPT_SILENT
)
177 /* Send an initial configure-request */
189 if( f
->flags
& OPT_RESTART
){
199 /* explicitly do nothing here. */
206 * fsm_close - Start closing connection.
208 * Cancel timeouts and either initiate close or possibly go directly to
216 int prevstate
= f
->state
;
218 f
->term_reason
= reason
;
219 f
->term_reason_len
= (reason
== NULL
? 0: strlen(reason
));
223 if (f
->callbacks
->finished
!= NULL
)
224 (*f
->callbacks
->finished
)(f
);
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 */
245 * Note that this-layer-down means "stop transmitting."
246 * This-layer-finished means "stop everything."
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
);
260 /* explicitly do nothing here. */
267 * fsm_timeout - Timeout expired.
273 fsm
*f
= (fsm
*) arg
;
278 if( f
->retransmits
<= 0 ){
280 * We've waited for an ack long enough. Peer probably heard us.
282 f
->state
= (f
->state
== CLOSING
)? CLOSED
: STOPPED
;
283 if (f
->callbacks
->finished
!= NULL
)
284 (*f
->callbacks
->finished
)(f
);
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
);
297 if (f
->retransmits
<= 0) {
298 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f
));
300 if (!(f
->flags
& OPT_PASSIVE
) && f
->callbacks
->finished
!= NULL
)
301 (*f
->callbacks
->finished
)(f
);
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
)
314 fatal("%s: Timeout event in state %s!", PROTO_NAME(f
),
315 fsm_state(f
->state
));
321 * fsm_input - Input packet.
324 fsm_input(f
, inpacket
, l
)
334 * Parse header (code, id and length).
335 * If packet too short, drop it.
339 error("%s packet: discard; too small (%d < %d)", PROTO_NAME(f
), l
,
346 if (len
< HEADERLEN
) {
347 error("%s packet: discard; invalid length (%d < %d)", PROTO_NAME(f
),
352 error("%s packet: discard; truncated (%d > %d)", PROTO_NAME(f
), len
,
356 len
-= HEADERLEN
; /* subtract header length */
358 if (f
->state
== INITIAL
|| f
->state
== STARTING
) {
359 dbglog("%s: discarded packet in state %s", PROTO_NAME(f
),
360 fsm_state(f
->state
));
365 * Action depends on code.
369 fsm_rconfreq(f
, id
, inp
, len
);
373 fsm_rconfack(f
, id
, inp
, len
);
378 fsm_rconfnakrej(f
, code
, id
, inp
, len
);
382 fsm_rtermreq(f
, id
, inp
, len
);
390 fsm_rcoderej(f
, inp
, len
);
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
);
403 * fsm_rconfreq - Receive Configure-Request.
406 fsm_rconfreq(f
, id
, inp
, len
)
412 int code
, reject_if_disagree
;
416 /* Go away, we're closed */
417 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
422 dbglog("%s: discarded Configure-Request in state %s", PROTO_NAME(f
),
423 fsm_state(f
->state
));
427 /* Go down and restart negotiation */
428 if (f
->callbacks
->down
!= NULL
)
429 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
434 if (inp
>= outpacket_buf
&& inp
< outpacket_buf
+PPP_MRU
+PPP_HDRLEN
)
435 fatal("bad pointer");
439 * Pass the requested configuration options
440 * to protocol-specific code for checking.
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
);
446 code
= CODE_CONFREJ
; /* Reject all CI */
450 /* Allow NCP to do fancy footwork, such as reinitializing. */
454 if (f
->state
== OPENED
|| f
->state
== STOPPED
)
455 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
457 /* send the Ack, Nak or Rej to the peer */
458 fsm_sdata(f
, code
, id
, inp
, len
);
460 if (code
== CODE_CONFACK
) {
461 /* RFC 1661 event RCR+ */
462 if (f
->state
== ACKRCVD
) {
463 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
465 if (f
->callbacks
->up
!= NULL
)
466 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
472 /* RFC 1661 event RCR- */
473 /* (we sent CODE_CONFNAK or CODE_CONFREJ) */
474 if (f
->state
!= ACKRCVD
)
476 if( code
== CODE_CONFNAK
)
483 * fsm_rconfack - Receive Configure-Ack.
486 fsm_rconfack(f
, id
, inp
, len
)
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
):
496 /* Ack is bad - ignore it */
497 error("Received bad configure-ack: %P", inp
, len
);
505 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
510 f
->retransmits
= f
->maxconfreqtransmits
;
514 /* Huh? an extra valid Ack? oh well... */
515 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
521 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
523 f
->retransmits
= f
->maxconfreqtransmits
;
524 if (f
->callbacks
->up
!= NULL
)
525 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
529 /* Go down and restart negotiation */
530 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
532 if (f
->callbacks
->down
!= NULL
)
533 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
540 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
543 fsm_rconfnakrej(f
, code
, id
, inp
, len
)
549 int (*proc
) __P((fsm
*, u_char
*, int));
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
);
565 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
570 /* They didn't agree to what we wanted - try another request */
571 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
573 f
->state
= STOPPED
; /* kludge for stopping CCP */
575 fsm_sconfreq(f
, 0); /* Send Configure-Request */
579 /* Got a Nak/reject when we had already had an Ack?? oh well... */
580 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
586 /* Go down and restart negotiation */
587 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
589 if (f
->callbacks
->down
!= NULL
)
590 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
597 * fsm_rtermreq - Receive Terminate-Req.
600 fsm_rtermreq(f
, id
, p
, len
)
609 f
->state
= REQSENT
; /* Start over but keep trying */
614 info("%s terminated by peer (%0.*v)", PROTO_NAME(f
), len
, p
);
616 info("%s terminated by peer", PROTO_NAME(f
));
619 if (f
->callbacks
->down
!= NULL
)
620 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
622 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
626 fsm_sdata(f
, CODE_TERMACK
, id
, NULL
, 0);
631 * fsm_rtermack - Receive Terminate-Ack.
639 UNTIMEOUT(fsm_timeout
, f
);
641 if (f
->callbacks
->finished
!= NULL
)
642 (*f
->callbacks
->finished
)(f
);
645 UNTIMEOUT(fsm_timeout
, f
);
647 if (f
->callbacks
->finished
!= NULL
)
648 (*f
->callbacks
->finished
)(f
);
658 if (f
->callbacks
->down
!= NULL
)
659 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
666 * fsm_rcoderej - Receive a Code-Reject.
669 fsm_rcoderej(f
, inp
, len
)
677 if (len
< HEADERLEN
) {
678 error("%s: Code-Reject too short (%d < %d)", PROTO_NAME(f
), len
,
685 warn("%s: Rcvd Code-Reject for %s id %d", PROTO_NAME(f
),
686 code_name(code
,0), id
);
688 setbit(f
->codemask
, code
);
690 /* Let the protocol know what happened. */
691 if (f
->callbacks
->codereject
!= NULL
) {
692 seriouserr
= (*f
->callbacks
->codereject
)(f
,code
,id
,inp
,len
);
695 * By default, it's RXJ- for well-known codes and RXJ+ for
698 seriouserr
= (code
>= CODE_CONFREQ
&& code
<= CODE_CODEREJ
);
702 /* RXJ- -- shut down the protocol. */
705 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
709 if (f
->callbacks
->finished
!= NULL
)
710 (*f
->callbacks
->finished
)(f
);
717 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
721 if (f
->callbacks
->finished
!= NULL
)
722 (*f
->callbacks
->finished
)(f
);
727 if (f
->callbacks
->down
!= NULL
)
728 (*f
->callbacks
->down
)(f
);
730 if (f
->term_reason
== NULL
) {
731 f
->term_reason
= "unacceptable Code-Reject received";
732 f
->term_reason_len
= strlen(f
->term_reason
);
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
);
744 fatal("state error");
747 /* RXJ+ -- just back up from Ack-Rcvd to Req-Sent. */
748 if (f
->state
== ACKRCVD
)
755 * fsm_protreject - Peer doesn't speak this protocol.
757 * Treat this as a catastrophic error (RXJ-).
765 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
769 if (f
->callbacks
->finished
!= NULL
)
770 (*f
->callbacks
->finished
)(f
);
777 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
781 if (f
->callbacks
->finished
!= NULL
)
782 (*f
->callbacks
->finished
)(f
);
787 if (f
->callbacks
->down
!= NULL
)
788 (*f
->callbacks
->down
)(f
);
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
);
799 dbglog("%s: Protocol-Reject in state %s", PROTO_NAME(f
),
800 fsm_state(f
->state
));
806 * fsm_sconfreq - Send a Configure-Request.
809 fsm_sconfreq(f
, retransmit
)
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
);
824 /* New request - reset retransmission counter, use new ID */
825 f
->retransmits
= f
->maxconfreqtransmits
;
832 * Make up the request packet
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
;
840 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
843 if (f
->callbacks
->addci
!= NULL
)
844 (*f
->callbacks
->addci
)(f
, outp
, &cilen
);
848 /* send the request to our peer */
849 fsm_sdata(f
, CODE_CONFREQ
, f
->reqid
, outp
, cilen
);
851 /* start the retransmit timer */
853 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
858 * fsm_sdata - Send some data.
860 * Used for all packets sent to our peer by this module.
863 fsm_sdata(f
, code
, id
, data
, datalen
)
872 if (isset(f
->codemask
,code
)) {
873 dbglog("%s: Peer has rejected %s; not sending another",
874 PROTO_NAME(f
), code_name(code
,0));
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
);
888 PUTSHORT(outlen
, outp
);
889 output(f
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
893 * fsm_setpeermru - Set our idea of the peer's mru
895 * Used by routines in lcp.c which negotiate this value.
898 fsm_setpeermru(unit
, mru
)
902 if (unit
>= NUM_PPP
) {
903 dbglog("fsm_setpeermru: unit out of bounds");
905 peer_mru
[unit
] = mru
;