1 /*****************************************************************************
2 * fsm.c - Network Control Protocol Finite State Machine program file.
4 * Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
5 * portions Copyright (c) 1997 by Global Election Systems Inc.
7 * The authors hereby grant permission to use, copy, modify, distribute,
8 * and license this software and its documentation for any purpose, provided
9 * that existing copyright notices are retained in all copies and that this
10 * notice and the following disclaimer are included verbatim in any
11 * distributions. No written agreement, license, or royalty fee is required
12 * for any of the authorized uses.
14 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 ******************************************************************************
28 * 03-01-01 Marc Boucher <marc@mbsi.ca>
30 * 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
31 * Original based on BSD fsm.c.
32 *****************************************************************************/
34 * fsm.c - {Link, IP} Control Protocol Finite State Machine.
36 * Copyright (c) 1989 Carnegie Mellon University.
37 * All rights reserved.
39 * Redistribution and use in source and binary forms are permitted
40 * provided that the above copyright notice and this paragraph are
41 * duplicated in all such forms and that any documentation,
42 * advertising materials, and other materials related to such
43 * distribution and use acknowledge that the software was developed
44 * by Carnegie Mellon University. The name of the
45 * University may not be used to endorse or promote products derived
46 * from this software without specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
54 * Randomize fsm id on link/init.
55 * Deal with variable outgoing MTU.
60 #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
70 /*************************/
71 /*** LOCAL DEFINITIONS ***/
72 /*************************/
76 static const char *ppperr_strerr[] = {
77 "LS_INITIAL", /* LS_INITIAL 0 */
78 "LS_STARTING", /* LS_STARTING 1 */
79 "LS_CLOSED", /* LS_CLOSED 2 */
80 "LS_STOPPED", /* LS_STOPPED 3 */
81 "LS_CLOSING", /* LS_CLOSING 4 */
82 "LS_STOPPING", /* LS_STOPPING 5 */
83 "LS_REQSENT", /* LS_REQSENT 6 */
84 "LS_ACKRCVD", /* LS_ACKRCVD 7 */
85 "LS_ACKSENT", /* LS_ACKSENT 8 */
86 "LS_OPENED" /* LS_OPENED 9 */
89 #endif /* PPP_DEBUG */
91 /************************/
92 /*** LOCAL DATA TYPES ***/
93 /************************/
96 /***********************************/
97 /*** LOCAL FUNCTION DECLARATIONS ***/
98 /***********************************/
99 static void fsm_timeout (void *);
100 static void fsm_rconfreq (fsm *, u_char, u_char *, int);
101 static void fsm_rconfack (fsm *, int, u_char *, int);
102 static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
103 static void fsm_rtermreq (fsm *, int, u_char *, int);
104 static void fsm_rtermack (fsm *);
105 static void fsm_rcoderej (fsm *, u_char *, int);
106 static void fsm_sconfreq (fsm *, int);
108 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
111 /******************************/
112 /*** PUBLIC DATA STRUCTURES ***/
113 /******************************/
116 /*****************************/
117 /*** LOCAL DATA STRUCTURES ***/
118 /*****************************/
119 int peer_mru[NUM_PPP];
122 /***********************************/
123 /*** PUBLIC FUNCTION DEFINITIONS ***/
124 /***********************************/
127 * fsm_init - Initialize fsm.
129 * Initialize fsm state.
134 f->state = LS_INITIAL;
136 f->id = 0; /* XXX Start with random id? */
137 f->timeouttime = FSM_DEFTIMEOUT;
138 f->maxconfreqtransmits = FSM_DEFMAXCONFREQS;
139 f->maxtermtransmits = FSM_DEFMAXTERMREQS;
140 f->maxnakloops = FSM_DEFMAXNAKLOOPS;
141 f->term_reason_len = 0;
146 * fsm_lowerup - The lower layer is up.
151 int oldState = f->state;
153 LWIP_UNUSED_ARG(oldState);
157 f->state = LS_CLOSED;
161 if( f->flags & OPT_SILENT ) {
162 f->state = LS_STOPPED;
164 /* Send an initial configure-request */
166 f->state = LS_REQSENT;
171 FSMDEBUG((LOG_INFO, "%s: Up event in state %d (%s)!\n",
172 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
175 FSMDEBUG((LOG_INFO, "%s: lowerup state %d (%s) -> %d (%s)\n",
176 PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
181 * fsm_lowerdown - The lower layer is down.
183 * Cancel all timeouts and inform upper layers.
186 fsm_lowerdown(fsm *f)
188 int oldState = f->state;
190 LWIP_UNUSED_ARG(oldState);
194 f->state = LS_INITIAL;
198 f->state = LS_STARTING;
199 if( f->callbacks->starting ) {
200 (*f->callbacks->starting)(f);
205 f->state = LS_INITIAL;
206 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
213 f->state = LS_STARTING;
214 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
218 if( f->callbacks->down ) {
219 (*f->callbacks->down)(f);
221 f->state = LS_STARTING;
225 FSMDEBUG((LOG_INFO, "%s: Down event in state %d (%s)!\n",
226 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
229 FSMDEBUG((LOG_INFO, "%s: lowerdown state %d (%s) -> %d (%s)\n",
230 PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
235 * fsm_open - Link is allowed to come up.
240 int oldState = f->state;
242 LWIP_UNUSED_ARG(oldState);
246 f->state = LS_STARTING;
247 if( f->callbacks->starting ) {
248 (*f->callbacks->starting)(f);
253 if( f->flags & OPT_SILENT ) {
254 f->state = LS_STOPPED;
256 /* Send an initial configure-request */
258 f->state = LS_REQSENT;
263 f->state = LS_STOPPING;
267 if( f->flags & OPT_RESTART ) {
274 FSMDEBUG((LOG_INFO, "%s: open state %d (%s) -> %d (%s)\n",
275 PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
280 * fsm_close - Start closing connection.
282 * Cancel timeouts and either initiate close or possibly go directly to
283 * the LS_CLOSED state.
286 fsm_close(fsm *f, char *reason)
288 int oldState = f->state;
290 LWIP_UNUSED_ARG(oldState);
292 f->term_reason = reason;
293 f->term_reason_len = (reason == NULL? 0: strlen(reason));
296 f->state = LS_INITIAL;
299 f->state = LS_CLOSED;
302 f->state = LS_CLOSING;
309 if( f->state != LS_OPENED ) {
310 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
311 } else if( f->callbacks->down ) {
312 (*f->callbacks->down)(f); /* Inform upper layers we're down */
314 /* Init restart counter, send Terminate-Request */
315 f->retransmits = f->maxtermtransmits;
316 fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
317 (u_char *) f->term_reason, f->term_reason_len);
318 TIMEOUT(fsm_timeout, f, f->timeouttime);
321 f->state = LS_CLOSING;
325 FSMDEBUG((LOG_INFO, "%s: close reason=%s state %d (%s) -> %d (%s)\n",
326 PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
331 * fsm_sdata - Send some data.
333 * Used for all packets sent to our peer by this module.
336 fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen)
341 /* Adjust length to be smaller than MTU */
342 outp = outpacket_buf[f->unit];
343 if (datalen > peer_mru[f->unit] - (int)HEADERLEN) {
344 datalen = peer_mru[f->unit] - HEADERLEN;
346 if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) {
347 BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
349 outlen = datalen + HEADERLEN;
350 MAKEHEADER(outp, f->protocol);
353 PUTSHORT(outlen, outp);
354 pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN);
355 FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d,%d,%d.\n",
356 PROTO_NAME(f), code, id, outlen));
361 * fsm_input - Input packet.
364 fsm_input(fsm *f, u_char *inpacket, int l)
366 u_char *inp = inpacket;
371 * Parse header (code, id and length).
372 * If packet too short, drop it.
375 FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.\n",
382 if (len < HEADERLEN) {
383 FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.\n",
388 FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.\n",
392 len -= HEADERLEN; /* subtract header length */
394 if( f->state == LS_INITIAL || f->state == LS_STARTING ) {
395 FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d (%s).\n",
396 f->protocol, f->state, ppperr_strerr[f->state]));
399 FSMDEBUG((LOG_INFO, "fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l));
401 * Action depends on code.
405 fsm_rconfreq(f, id, inp, len);
409 fsm_rconfack(f, id, inp, len);
414 fsm_rconfnakrej(f, code, id, inp, len);
418 fsm_rtermreq(f, id, inp, len);
426 fsm_rcoderej(f, inp, len);
430 if( !f->callbacks->extcode ||
431 !(*f->callbacks->extcode)(f, code, id, inp, len) ) {
432 fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
440 * fsm_protreject - Peer doesn't speak this protocol.
442 * Treat this as a catastrophic error (RXJ-).
445 fsm_protreject(fsm *f)
449 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
452 f->state = LS_CLOSED;
453 if( f->callbacks->finished ) {
454 (*f->callbacks->finished)(f);
462 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
465 f->state = LS_STOPPED;
466 if( f->callbacks->finished ) {
467 (*f->callbacks->finished)(f);
472 if( f->callbacks->down ) {
473 (*f->callbacks->down)(f);
475 /* Init restart counter, send Terminate-Request */
476 f->retransmits = f->maxtermtransmits;
477 fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
478 (u_char *) f->term_reason, f->term_reason_len);
479 TIMEOUT(fsm_timeout, f, f->timeouttime);
482 f->state = LS_STOPPING;
486 FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d (%s)!\n",
487 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
495 /**********************************/
496 /*** LOCAL FUNCTION DEFINITIONS ***/
497 /**********************************/
500 * fsm_timeout - Timeout expired.
503 fsm_timeout(void *arg)
505 fsm *f = (fsm *) arg;
510 if( f->retransmits <= 0 ) {
511 FSMDEBUG((LOG_WARNING, "%s: timeout sending Terminate-Request state=%d (%s)\n",
512 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
514 * We've waited for an ack long enough. Peer probably heard us.
516 f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED;
517 if( f->callbacks->finished ) {
518 (*f->callbacks->finished)(f);
521 FSMDEBUG((LOG_WARNING, "%s: timeout resending Terminate-Requests state=%d (%s)\n",
522 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
523 /* Send Terminate-Request */
524 fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
525 (u_char *) f->term_reason, f->term_reason_len);
526 TIMEOUT(fsm_timeout, f, f->timeouttime);
534 if (f->retransmits <= 0) {
535 FSMDEBUG((LOG_WARNING, "%s: timeout sending Config-Requests state=%d (%s)\n",
536 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
537 f->state = LS_STOPPED;
538 if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) {
539 (*f->callbacks->finished)(f);
542 FSMDEBUG((LOG_WARNING, "%s: timeout resending Config-Request state=%d (%s)\n",
543 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
544 /* Retransmit the configure-request */
545 if (f->callbacks->retransmit) {
546 (*f->callbacks->retransmit)(f);
548 fsm_sconfreq(f, 1); /* Re-send Configure-Request */
549 if( f->state == LS_ACKRCVD ) {
550 f->state = LS_REQSENT;
556 FSMDEBUG((LOG_INFO, "%s: UNHANDLED timeout event in state %d (%s)!\n",
557 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
563 * fsm_rconfreq - Receive Configure-Request.
566 fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len)
568 int code, reject_if_disagree;
570 FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n",
571 PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
574 /* Go away, we're closed */
575 fsm_sdata(f, TERMACK, id, NULL, 0);
582 /* Go down and restart negotiation */
583 if( f->callbacks->down ) {
584 (*f->callbacks->down)(f); /* Inform upper layers */
586 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
590 /* Negotiation started by our peer */
591 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
592 f->state = LS_REQSENT;
597 * Pass the requested configuration options
598 * to protocol-specific code for checking.
600 if (f->callbacks->reqci) { /* Check CI */
601 reject_if_disagree = (f->nakloops >= f->maxnakloops);
602 code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
604 code = CONFREJ; /* Reject all CI */
609 /* send the Ack, Nak or Rej to the peer */
610 fsm_sdata(f, (u_char)code, id, inp, len);
612 if (code == CONFACK) {
613 if (f->state == LS_ACKRCVD) {
614 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
615 f->state = LS_OPENED;
616 if (f->callbacks->up) {
617 (*f->callbacks->up)(f); /* Inform upper layers */
620 f->state = LS_ACKSENT;
624 /* we sent CONFACK or CONFREJ */
625 if (f->state != LS_ACKRCVD) {
626 f->state = LS_REQSENT;
628 if( code == CONFNAK ) {
636 * fsm_rconfack - Receive Configure-Ack.
639 fsm_rconfack(fsm *f, int id, u_char *inp, int len)
641 FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n",
642 PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
644 if (id != f->reqid || f->seen_ack) { /* Expected id? */
645 return; /* Nope, toss... */
647 if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) {
648 /* Ack is bad - ignore it */
649 FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)\n",
650 PROTO_NAME(f), len));
658 fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
662 f->state = LS_ACKRCVD;
663 f->retransmits = f->maxconfreqtransmits;
667 /* Huh? an extra valid Ack? oh well... */
668 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
670 f->state = LS_REQSENT;
674 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
675 f->state = LS_OPENED;
676 f->retransmits = f->maxconfreqtransmits;
677 if (f->callbacks->up) {
678 (*f->callbacks->up)(f); /* Inform upper layers */
683 /* Go down and restart negotiation */
684 if (f->callbacks->down) {
685 (*f->callbacks->down)(f); /* Inform upper layers */
687 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
688 f->state = LS_REQSENT;
695 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
698 fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
700 int (*proc) (fsm *, u_char *, int);
703 FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n",
704 PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
706 if (id != f->reqid || f->seen_ack) { /* Expected id? */
707 return; /* Nope, toss... */
709 proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
710 if (!proc || !((ret = proc(f, inp, len)))) {
711 /* Nak/reject is bad - ignore it */
712 FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)\n",
713 PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
721 fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
726 /* They didn't agree to what we wanted - try another request */
727 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
729 f->state = LS_STOPPED; /* kludge for stopping CCP */
731 fsm_sconfreq(f, 0); /* Send Configure-Request */
736 /* Got a Nak/reject when we had already had an Ack?? oh well... */
737 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
739 f->state = LS_REQSENT;
743 /* Go down and restart negotiation */
744 if (f->callbacks->down) {
745 (*f->callbacks->down)(f); /* Inform upper layers */
747 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
748 f->state = LS_REQSENT;
755 * fsm_rtermreq - Receive Terminate-Req.
758 fsm_rtermreq(fsm *f, int id, u_char *p, int len)
762 FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n",
763 PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
768 f->state = LS_REQSENT; /* Start over but keep trying */
773 FSMDEBUG((LOG_INFO, "%s terminated by peer (%x)\n", PROTO_NAME(f), p));
775 FSMDEBUG((LOG_INFO, "%s terminated by peer\n", PROTO_NAME(f)));
777 if (f->callbacks->down) {
778 (*f->callbacks->down)(f); /* Inform upper layers */
781 f->state = LS_STOPPING;
782 TIMEOUT(fsm_timeout, f, f->timeouttime);
786 fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
791 * fsm_rtermack - Receive Terminate-Ack.
796 FSMDEBUG((LOG_INFO, "fsm_rtermack(%s): state=%d (%s)\n",
797 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
801 UNTIMEOUT(fsm_timeout, f);
802 f->state = LS_CLOSED;
803 if( f->callbacks->finished ) {
804 (*f->callbacks->finished)(f);
809 UNTIMEOUT(fsm_timeout, f);
810 f->state = LS_STOPPED;
811 if( f->callbacks->finished ) {
812 (*f->callbacks->finished)(f);
817 f->state = LS_REQSENT;
821 if (f->callbacks->down) {
822 (*f->callbacks->down)(f); /* Inform upper layers */
831 * fsm_rcoderej - Receive an Code-Reject.
834 fsm_rcoderej(fsm *f, u_char *inp, int len)
838 FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s): state=%d (%s)\n",
839 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
841 if (len < HEADERLEN) {
842 FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
847 FSMDEBUG((LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d\n",
848 PROTO_NAME(f), code, id));
850 if( f->state == LS_ACKRCVD ) {
851 f->state = LS_REQSENT;
857 * fsm_sconfreq - Send a Configure-Request.
860 fsm_sconfreq(fsm *f, int retransmit)
865 if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) {
866 /* Not currently negotiating - reset options */
867 if( f->callbacks->resetci ) {
868 (*f->callbacks->resetci)(f);
874 /* New request - reset retransmission counter, use new ID */
875 f->retransmits = f->maxconfreqtransmits;
882 * Make up the request packet
884 outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN;
885 if( f->callbacks->cilen && f->callbacks->addci ) {
886 cilen = (*f->callbacks->cilen)(f);
887 if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) {
888 cilen = peer_mru[f->unit] - HEADERLEN;
890 if (f->callbacks->addci) {
891 (*f->callbacks->addci)(f, outp, &cilen);
897 /* send the request to our peer */
898 fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
900 /* start the retransmit timer */
902 TIMEOUT(fsm_timeout, f, f->timeouttime);
904 FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d\n",
905 PROTO_NAME(f), f->reqid));
908 #endif /* PPP_SUPPORT */