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.
55 * Randomize fsm id on link/init.
56 * Deal with variable outgoing MTU.
65 /*************************/
66 /*** LOCAL DEFINITIONS ***/
67 /*************************/
70 /************************/
71 /*** LOCAL DATA TYPES ***/
72 /************************/
75 /***********************************/
76 /*** LOCAL FUNCTION DECLARATIONS ***/
77 /***********************************/
78 static void fsm_timeout (void *);
79 static void fsm_rconfreq (fsm
*, u_char
, u_char
*, int);
80 static void fsm_rconfack (fsm
*, int, u_char
*, int);
81 static void fsm_rconfnakrej (fsm
*, int, int, u_char
*, int);
82 static void fsm_rtermreq (fsm
*, int, u_char
*, int);
83 static void fsm_rtermack (fsm
*);
84 static void fsm_rcoderej (fsm
*, u_char
*, int);
85 static void fsm_sconfreq (fsm
*, int);
87 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
90 /******************************/
91 /*** PUBLIC DATA STRUCTURES ***/
92 /******************************/
95 /*****************************/
96 /*** LOCAL DATA STRUCTURES ***/
97 /*****************************/
98 int peer_mru
[NUM_PPP
];
101 /***********************************/
102 /*** PUBLIC FUNCTION DEFINITIONS ***/
103 /***********************************/
106 * fsm_init - Initialize fsm.
108 * Initialize fsm state.
110 void fsm_init(fsm
*f
)
114 f
->id
= 0; /* XXX Start with random id? */
115 f
->timeouttime
= FSM_DEFTIMEOUT
;
116 f
->maxconfreqtransmits
= FSM_DEFMAXCONFREQS
;
117 f
->maxtermtransmits
= FSM_DEFMAXTERMREQS
;
118 f
->maxnakloops
= FSM_DEFMAXNAKLOOPS
;
119 f
->term_reason_len
= 0;
124 * fsm_lowerup - The lower layer is up.
126 void fsm_lowerup(fsm
*f
)
128 int oldState
= f
->state
;
136 if( f
->flags
& OPT_SILENT
)
139 /* Send an initial configure-request */
146 FSMDEBUG((LOG_INFO
, "%s: Up event in state %d!\n",
147 PROTO_NAME(f
), f
->state
));
150 FSMDEBUG((LOG_INFO
, "%s: lowerup state %d -> %d\n",
151 PROTO_NAME(f
), oldState
, f
->state
));
156 * fsm_lowerdown - The lower layer is down.
158 * Cancel all timeouts and inform upper layers.
160 void fsm_lowerdown(fsm
*f
)
162 int oldState
= f
->state
;
171 if( f
->callbacks
->starting
)
172 (*f
->callbacks
->starting
)(f
);
177 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
185 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
189 if( f
->callbacks
->down
)
190 (*f
->callbacks
->down
)(f
);
195 FSMDEBUG((LOG_INFO
, "%s: Down event in state %d!\n",
196 PROTO_NAME(f
), f
->state
));
199 FSMDEBUG((LOG_INFO
, "%s: lowerdown state %d -> %d\n",
200 PROTO_NAME(f
), oldState
, f
->state
));
205 * fsm_open - Link is allowed to come up.
207 void fsm_open(fsm
*f
)
209 int oldState
= f
->state
;
214 if( f
->callbacks
->starting
)
215 (*f
->callbacks
->starting
)(f
);
219 if( f
->flags
& OPT_SILENT
)
222 /* Send an initial configure-request */
233 if( f
->flags
& OPT_RESTART
){
240 FSMDEBUG((LOG_INFO
, "%s: open state %d -> %d\n",
241 PROTO_NAME(f
), oldState
, f
->state
));
246 * fsm_close - Start closing connection.
248 * Cancel timeouts and either initiate close or possibly go directly to
251 void fsm_close(fsm
*f
, char *reason
)
253 int oldState
= f
->state
;
255 f
->term_reason
= reason
;
256 f
->term_reason_len
= (reason
== NULL
? 0: strlen(reason
));
272 if( f
->state
!= OPENED
)
273 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
274 else if( f
->callbacks
->down
)
275 (*f
->callbacks
->down
)(f
); /* Inform upper layers we're down */
277 /* Init restart counter, send Terminate-Request */
278 f
->retransmits
= f
->maxtermtransmits
;
279 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
280 (u_char
*) f
->term_reason
, f
->term_reason_len
);
281 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
288 FSMDEBUG((LOG_INFO
, "%s: close reason=%s state %d -> %d\n",
289 PROTO_NAME(f
), reason
, oldState
, f
->state
));
294 * fsm_sdata - Send some data.
296 * Used for all packets sent to our peer by this module.
309 /* Adjust length to be smaller than MTU */
310 outp
= outpacket_buf
[f
->unit
];
311 if (datalen
> peer_mru
[f
->unit
] - (int)HEADERLEN
)
312 datalen
= peer_mru
[f
->unit
] - HEADERLEN
;
313 if (datalen
&& data
!= outp
+ PPP_HDRLEN
+ HEADERLEN
)
314 BCOPY(data
, outp
+ PPP_HDRLEN
+ HEADERLEN
, datalen
);
315 outlen
= datalen
+ HEADERLEN
;
316 MAKEHEADER(outp
, f
->protocol
);
319 PUTSHORT(outlen
, outp
);
320 pppWrite(f
->unit
, outpacket_buf
[f
->unit
], outlen
+ PPP_HDRLEN
);
321 FSMDEBUG((LOG_INFO
, "fsm_sdata(%s): Sent code %d,%d,%d.\n",
322 PROTO_NAME(f
), code
, id
, outlen
));
327 * fsm_input - Input packet.
329 void fsm_input(fsm
*f
, u_char
*inpacket
, int l
)
331 u_char
*inp
= inpacket
;
336 * Parse header (code, id and length).
337 * If packet too short, drop it.
340 FSMDEBUG((LOG_WARNING
, "fsm_input(%x): Rcvd short header.\n",
347 if (len
< HEADERLEN
) {
348 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd illegal length.\n",
353 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd short packet.\n",
357 len
-= HEADERLEN
; /* subtract header length */
359 if( f
->state
== INITIAL
|| f
->state
== STARTING
){
360 FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd packet in state %d.\n",
361 f
->protocol
, f
->state
));
364 FSMDEBUG((LOG_INFO
, "fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f
), code
, id
, l
));
366 * Action depends on code.
370 fsm_rconfreq(f
, id
, inp
, len
);
374 fsm_rconfack(f
, id
, inp
, len
);
379 fsm_rconfnakrej(f
, code
, id
, inp
, len
);
383 fsm_rtermreq(f
, id
, inp
, len
);
391 fsm_rcoderej(f
, inp
, len
);
395 if( !f
->callbacks
->extcode
396 || !(*f
->callbacks
->extcode
)(f
, code
, id
, inp
, len
) )
397 fsm_sdata(f
, CODEREJ
, ++f
->id
, inpacket
, len
+ HEADERLEN
);
404 * fsm_protreject - Peer doesn't speak this protocol.
406 * Treat this as a catastrophic error (RXJ-).
408 void fsm_protreject(fsm
*f
)
412 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
416 if( f
->callbacks
->finished
)
417 (*f
->callbacks
->finished
)(f
);
424 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
428 if( f
->callbacks
->finished
)
429 (*f
->callbacks
->finished
)(f
);
433 if( f
->callbacks
->down
)
434 (*f
->callbacks
->down
)(f
);
436 /* Init restart counter, send Terminate-Request */
437 f
->retransmits
= f
->maxtermtransmits
;
438 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
439 (u_char
*) f
->term_reason
, f
->term_reason_len
);
440 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
447 FSMDEBUG((LOG_INFO
, "%s: Protocol-reject event in state %d!\n",
448 PROTO_NAME(f
), f
->state
));
456 /**********************************/
457 /*** LOCAL FUNCTION DEFINITIONS ***/
458 /**********************************/
461 * fsm_timeout - Timeout expired.
463 static void fsm_timeout(void *arg
)
465 fsm
*f
= (fsm
*) arg
;
470 if( f
->retransmits
<= 0 ){
471 FSMDEBUG((LOG_WARNING
, "%s: timeout sending Terminate-Request state=%d\n",
472 PROTO_NAME(f
), f
->state
));
474 * We've waited for an ack long enough. Peer probably heard us.
476 f
->state
= (f
->state
== CLOSING
)? CLOSED
: STOPPED
;
477 if( f
->callbacks
->finished
)
478 (*f
->callbacks
->finished
)(f
);
480 FSMDEBUG((LOG_WARNING
, "%s: timeout resending Terminate-Requests state=%d\n",
481 PROTO_NAME(f
), f
->state
));
482 /* Send Terminate-Request */
483 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
484 (u_char
*) f
->term_reason
, f
->term_reason_len
);
485 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
493 if (f
->retransmits
<= 0) {
494 FSMDEBUG((LOG_WARNING
, "%s: timeout sending Config-Requests state=%d\n",
495 PROTO_NAME(f
), f
->state
));
497 if( (f
->flags
& OPT_PASSIVE
) == 0 && f
->callbacks
->finished
)
498 (*f
->callbacks
->finished
)(f
);
501 FSMDEBUG((LOG_WARNING
, "%s: timeout resending Config-Request state=%d\n",
502 PROTO_NAME(f
), f
->state
));
503 /* Retransmit the configure-request */
504 if (f
->callbacks
->retransmit
)
505 (*f
->callbacks
->retransmit
)(f
);
506 fsm_sconfreq(f
, 1); /* Re-send Configure-Request */
507 if( f
->state
== ACKRCVD
)
513 FSMDEBUG((LOG_INFO
, "%s: Timeout event in state %d!\n",
514 PROTO_NAME(f
), f
->state
));
520 * fsm_rconfreq - Receive Configure-Request.
522 static void fsm_rconfreq(fsm
*f
, u_char id
, u_char
*inp
, int len
)
524 int code
, reject_if_disagree
;
526 FSMDEBUG((LOG_INFO
, "fsm_rconfreq(%s): Rcvd id %d state=%d\n",
527 PROTO_NAME(f
), id
, f
->state
));
530 /* Go away, we're closed */
531 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
538 /* Go down and restart negotiation */
539 if( f
->callbacks
->down
)
540 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
541 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
545 /* Negotiation started by our peer */
546 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
552 * Pass the requested configuration options
553 * to protocol-specific code for checking.
555 if (f
->callbacks
->reqci
){ /* Check CI */
556 reject_if_disagree
= (f
->nakloops
>= f
->maxnakloops
);
557 code
= (*f
->callbacks
->reqci
)(f
, inp
, &len
, reject_if_disagree
);
560 code
= CONFREJ
; /* Reject all CI */
564 /* send the Ack, Nak or Rej to the peer */
565 fsm_sdata(f
, (u_char
)code
, id
, inp
, len
);
567 if (code
== CONFACK
) {
568 if (f
->state
== ACKRCVD
) {
569 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
571 if (f
->callbacks
->up
)
572 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
579 /* we sent CONFACK or CONFREJ */
580 if (f
->state
!= ACKRCVD
)
582 if( code
== CONFNAK
)
589 * fsm_rconfack - Receive Configure-Ack.
591 static void fsm_rconfack(fsm
*f
, int id
, u_char
*inp
, int len
)
593 FSMDEBUG((LOG_INFO
, "fsm_rconfack(%s): Rcvd id %d state=%d\n",
594 PROTO_NAME(f
), id
, f
->state
));
596 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
597 return; /* Nope, toss... */
598 if( !(f
->callbacks
->ackci
? (*f
->callbacks
->ackci
)(f
, inp
, len
):
600 /* Ack is bad - ignore it */
601 FSMDEBUG((LOG_INFO
, "%s: received bad Ack (length %d)\n",
602 PROTO_NAME(f
), len
));
610 fsm_sdata(f
, TERMACK
, (u_char
)id
, NULL
, 0);
615 f
->retransmits
= f
->maxconfreqtransmits
;
619 /* Huh? an extra valid Ack? oh well... */
620 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
626 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
628 f
->retransmits
= f
->maxconfreqtransmits
;
629 if (f
->callbacks
->up
)
630 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
634 /* Go down and restart negotiation */
635 if (f
->callbacks
->down
)
636 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
637 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
645 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
647 static void fsm_rconfnakrej(fsm
*f
, int code
, int id
, u_char
*inp
, int len
)
649 int (*proc
) (fsm
*, u_char
*, int);
652 FSMDEBUG((LOG_INFO
, "fsm_rconfnakrej(%s): Rcvd id %d state=%d\n",
653 PROTO_NAME(f
), id
, f
->state
));
655 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
656 return; /* Nope, toss... */
657 proc
= (code
== CONFNAK
)? f
->callbacks
->nakci
: f
->callbacks
->rejci
;
658 if (!proc
|| !(ret
= proc(f
, inp
, len
))) {
659 /* Nak/reject is bad - ignore it */
660 FSMDEBUG((LOG_INFO
, "%s: received bad %s (length %d)\n",
661 PROTO_NAME(f
), (code
==CONFNAK
? "Nak": "reject"), len
));
669 fsm_sdata(f
, TERMACK
, (u_char
)id
, NULL
, 0);
674 /* They didn't agree to what we wanted - try another request */
675 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
677 f
->state
= STOPPED
; /* kludge for stopping CCP */
679 fsm_sconfreq(f
, 0); /* Send Configure-Request */
683 /* Got a Nak/reject when we had already had an Ack?? oh well... */
684 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
690 /* Go down and restart negotiation */
691 if (f
->callbacks
->down
)
692 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
693 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
701 * fsm_rtermreq - Receive Terminate-Req.
703 static void fsm_rtermreq(fsm
*f
, int id
, u_char
*p
, int len
)
705 FSMDEBUG((LOG_INFO
, "fsm_rtermreq(%s): Rcvd id %d state=%d\n",
706 PROTO_NAME(f
), id
, f
->state
));
711 f
->state
= REQSENT
; /* Start over but keep trying */
716 FSMDEBUG((LOG_INFO
, "%s terminated by peer (%x)\n", PROTO_NAME(f
), p
));
718 FSMDEBUG((LOG_INFO
, "%s terminated by peer\n", PROTO_NAME(f
)));
720 if (f
->callbacks
->down
)
721 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
724 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
728 fsm_sdata(f
, TERMACK
, (u_char
)id
, NULL
, 0);
733 * fsm_rtermack - Receive Terminate-Ack.
735 static void fsm_rtermack(fsm
*f
)
737 FSMDEBUG((LOG_INFO
, "fsm_rtermack(%s): state=%d\n",
738 PROTO_NAME(f
), f
->state
));
742 UNTIMEOUT(fsm_timeout
, f
);
744 if( f
->callbacks
->finished
)
745 (*f
->callbacks
->finished
)(f
);
748 UNTIMEOUT(fsm_timeout
, f
);
750 if( f
->callbacks
->finished
)
751 (*f
->callbacks
->finished
)(f
);
759 if (f
->callbacks
->down
)
760 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
768 * fsm_rcoderej - Receive an Code-Reject.
770 static void fsm_rcoderej(fsm
*f
, u_char
*inp
, int len
)
774 FSMDEBUG((LOG_INFO
, "fsm_rcoderej(%s): state=%d\n",
775 PROTO_NAME(f
), f
->state
));
777 if (len
< HEADERLEN
) {
778 FSMDEBUG((LOG_INFO
, "fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
783 FSMDEBUG((LOG_WARNING
, "%s: Rcvd Code-Reject for code %d, id %d\n",
784 PROTO_NAME(f
), code
, id
));
786 if( f
->state
== ACKRCVD
)
792 * fsm_sconfreq - Send a Configure-Request.
794 static void fsm_sconfreq(fsm
*f
, int retransmit
)
799 if( f
->state
!= REQSENT
&& f
->state
!= ACKRCVD
&& f
->state
!= ACKSENT
){
800 /* Not currently negotiating - reset options */
801 if( f
->callbacks
->resetci
)
802 (*f
->callbacks
->resetci
)(f
);
807 /* New request - reset retransmission counter, use new ID */
808 f
->retransmits
= f
->maxconfreqtransmits
;
815 * Make up the request packet
817 outp
= outpacket_buf
[f
->unit
] + PPP_HDRLEN
+ HEADERLEN
;
818 if( f
->callbacks
->cilen
&& f
->callbacks
->addci
){
819 cilen
= (*f
->callbacks
->cilen
)(f
);
820 if( cilen
> peer_mru
[f
->unit
] - (int)HEADERLEN
)
821 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
822 if (f
->callbacks
->addci
)
823 (*f
->callbacks
->addci
)(f
, outp
, &cilen
);
827 /* send the request to our peer */
828 fsm_sdata(f
, CONFREQ
, f
->reqid
, outp
, cilen
);
830 /* start the retransmit timer */
832 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
834 FSMDEBUG((LOG_INFO
, "%s: sending Configure-Request, id %d\n",
835 PROTO_NAME(f
), f
->reqid
));
838 #endif /* PPP_SUPPORT */