2 * fsm.c - {Link, IP} Control Protocol Finite State Machine.
4 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * 3. The name "Carnegie Mellon University" must not be used to
19 * endorse or promote products derived from this software without
20 * prior written permission. For permission or any legal
21 * details, please contact
22 * Office of Technology Transfer
23 * Carnegie Mellon University
25 * Pittsburgh, PA 15213-3890
26 * (412) 268-4387, fax: (412) 268-7395
27 * tech-transfer@andrew.cmu.edu
29 * 4. Redistributions of any form whatsoever must retain the following
31 * "This product includes software developed by Computing Services
32 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
34 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43 #define RCSID "$Id: fsm.c,v 1.23 2004/11/13 02:28:15 paulus Exp $"
47 * Randomize fsm id on link/init.
48 * Deal with variable outgoing MTU.
53 #include <sys/types.h>
58 static const char rcsid
[] = RCSID
;
60 static void fsm_timeout
__P((void *));
61 static void fsm_rconfreq
__P((fsm
*, int, u_char
*, int));
62 static void fsm_rconfack
__P((fsm
*, int, u_char
*, int));
63 static void fsm_rconfnakrej
__P((fsm
*, int, int, u_char
*, int));
64 static void fsm_rtermreq
__P((fsm
*, int, u_char
*, int));
65 static void fsm_rtermack
__P((fsm
*));
66 static void fsm_rcoderej
__P((fsm
*, u_char
*, int));
67 static void fsm_sconfreq
__P((fsm
*, int));
69 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
71 int peer_mru
[NUM_PPP
];
73 /* JYWeng 20031216: add to wanstatus.log */
74 void saveWANStatus(char *currentstatus
, int statusindex
);
77 * fsm_init - Initialize fsm.
79 * Initialize fsm state.
87 f
->id
= 0; /* XXX Start with random id? */
88 f
->timeouttime
= DEFTIMEOUT
;
89 f
->maxconfreqtransmits
= DEFMAXCONFREQS
;
90 f
->maxtermtransmits
= DEFMAXTERMREQS
;
91 f
->maxnakloops
= DEFMAXNAKLOOPS
;
92 f
->term_reason_len
= 0;
97 * fsm_lowerup - The lower layer is up.
109 if( f
->flags
& OPT_SILENT
)
112 /* Send an initial configure-request */
119 FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f
), f
->state
));
125 * fsm_lowerdown - The lower layer is down.
127 * Cancel all timeouts and inform upper layers.
140 if( f
->callbacks
->starting
)
141 (*f
->callbacks
->starting
)(f
);
146 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
154 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
158 if( f
->callbacks
->down
)
159 (*f
->callbacks
->down
)(f
);
164 FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f
), f
->state
));
170 * fsm_open - Link is allowed to come up.
179 if( f
->callbacks
->starting
)
180 (*f
->callbacks
->starting
)(f
);
184 if( f
->flags
& OPT_SILENT
)
187 /* Send an initial configure-request */
198 if( f
->flags
& OPT_RESTART
){
207 * terminate_layer - Start process of shutting down the FSM
209 * Cancel any timeout running, notify upper layers we're done, and
210 * send a terminate-request message as configured.
213 terminate_layer(f
, nextstate
)
217 if( f
->state
!= OPENED
)
218 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
219 else if( f
->callbacks
->down
)
220 (*f
->callbacks
->down
)(f
); /* Inform upper layers we're down */
222 /* Init restart counter and send Terminate-Request */
223 f
->retransmits
= f
->maxtermtransmits
;
224 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
225 (u_char
*) f
->term_reason
, f
->term_reason_len
);
227 if (f
->retransmits
== 0) {
229 * User asked for no terminate requests at all; just close it.
230 * We've already fired off one Terminate-Request just to be nice
231 * to the peer, but we're not going to wait for a reply.
233 f
->state
= nextstate
== CLOSING
? CLOSED
: STOPPED
;
234 if( f
->callbacks
->finished
)
235 (*f
->callbacks
->finished
)(f
);
239 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
242 f
->state
= nextstate
;
246 * fsm_close - Start closing connection.
248 * Cancel timeouts and either initiate close or possibly go directly to
256 f
->term_reason
= reason
;
257 f
->term_reason_len
= (reason
== NULL
? 0: strlen(reason
));
273 terminate_layer(f
, CLOSING
);
280 * fsm_timeout - Timeout expired.
286 fsm
*f
= (fsm
*) arg
;
291 if( f
->retransmits
<= 0 ){
293 * We've waited for an ack long enough. Peer probably heard us.
295 f
->state
= (f
->state
== CLOSING
)? CLOSED
: STOPPED
;
296 if( f
->callbacks
->finished
)
297 (*f
->callbacks
->finished
)(f
);
299 /* Send Terminate-Request */
300 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
301 (u_char
*) f
->term_reason
, f
->term_reason_len
);
302 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
310 if (f
->retransmits
<= 0) {
311 /* JYWeng 20031216: add to wanstatus.log */
313 saveWANStatus("No response from ISP.", statusindex
);
314 /* JYWeng 20031216: add to wanstatus.log */
315 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f
));
317 if( (f
->flags
& OPT_PASSIVE
) == 0 && f
->callbacks
->finished
)
318 (*f
->callbacks
->finished
)(f
);
321 /* Retransmit the configure-request */
322 if (f
->callbacks
->retransmit
)
323 (*f
->callbacks
->retransmit
)(f
);
324 fsm_sconfreq(f
, 1); /* Re-send Configure-Request */
325 if( f
->state
== ACKRCVD
)
331 FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f
), f
->state
));
337 * fsm_input - Input packet.
340 fsm_input(f
, inpacket
, l
)
350 * Parse header (code, id and length).
351 * If packet too short, drop it.
355 FSMDEBUG(("fsm_input(%x): Rcvd short header.", f
->protocol
));
361 if (len
< HEADERLEN
) {
362 FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f
->protocol
));
366 FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f
->protocol
));
369 len
-= HEADERLEN
; /* subtract header length */
371 if( f
->state
== INITIAL
|| f
->state
== STARTING
){
372 FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
373 f
->protocol
, f
->state
));
378 * Action depends on code.
382 fsm_rconfreq(f
, id
, inp
, len
);
386 fsm_rconfack(f
, id
, inp
, len
);
391 fsm_rconfnakrej(f
, code
, id
, inp
, len
);
395 fsm_rtermreq(f
, id
, inp
, len
);
403 fsm_rcoderej(f
, inp
, len
);
407 if( !f
->callbacks
->extcode
408 || !(*f
->callbacks
->extcode
)(f
, code
, id
, inp
, len
) )
409 fsm_sdata(f
, CODEREJ
, ++f
->id
, inpacket
, len
+ HEADERLEN
);
416 * fsm_rconfreq - Receive Configure-Request.
419 fsm_rconfreq(f
, id
, inp
, len
)
425 int code
, reject_if_disagree
;
429 /* Go away, we're closed */
430 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
437 /* Go down and restart negotiation */
438 if( f
->callbacks
->down
)
439 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
440 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
445 /* Negotiation started by our peer */
446 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
452 * Pass the requested configuration options
453 * to protocol-specific code for checking.
455 if (f
->callbacks
->reqci
){ /* Check CI */
456 reject_if_disagree
= (f
->nakloops
>= f
->maxnakloops
);
457 code
= (*f
->callbacks
->reqci
)(f
, inp
, &len
, reject_if_disagree
);
459 code
= CONFREJ
; /* Reject all CI */
463 /* send the Ack, Nak or Rej to the peer */
464 fsm_sdata(f
, code
, id
, inp
, len
);
466 if (code
== CONFACK
) {
467 if (f
->state
== ACKRCVD
) {
468 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
470 if (f
->callbacks
->up
)
471 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
477 /* we sent CONFACK or CONFREJ */
478 if (f
->state
!= ACKRCVD
)
480 if( code
== CONFNAK
)
487 * fsm_rconfack - Receive Configure-Ack.
490 fsm_rconfack(f
, id
, inp
, len
)
496 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
497 return; /* Nope, toss... */
498 if( !(f
->callbacks
->ackci
? (*f
->callbacks
->ackci
)(f
, inp
, len
):
500 /* Ack is bad - ignore it */
501 error("Received bad configure-ack: %P", inp
, len
);
510 fsm_sdata(f
, 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
)
530 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
534 /* Go down and restart negotiation */
535 if (f
->callbacks
->down
)
536 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
537 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
545 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
548 fsm_rconfnakrej(f
, code
, id
, inp
, len
)
557 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
558 return; /* Nope, toss... */
560 if (code
== CONFNAK
) {
562 treat_as_reject
= (f
->rnakloops
>= f
->maxnakloops
);
563 if (f
->callbacks
->nakci
== NULL
564 || !(ret
= f
->callbacks
->nakci(f
, inp
, len
, treat_as_reject
))) {
565 error("Received bad configure-nak: %P", inp
, len
);
570 if (f
->callbacks
->rejci
== NULL
571 || !(ret
= f
->callbacks
->rejci(f
, inp
, len
))) {
572 error("Received bad configure-rej: %P", inp
, len
);
582 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
587 /* They didn't agree to what we wanted - try another request */
588 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
590 f
->state
= STOPPED
; /* kludge for stopping CCP */
592 fsm_sconfreq(f
, 0); /* Send Configure-Request */
596 /* Got a Nak/reject when we had already had an Ack?? oh well... */
597 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
603 /* Go down and restart negotiation */
604 if (f
->callbacks
->down
)
605 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
606 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
614 * fsm_rtermreq - Receive Terminate-Req.
617 fsm_rtermreq(f
, id
, p
, len
)
626 f
->state
= REQSENT
; /* Start over but keep trying */
631 info("%s terminated by peer (%0.*v)", PROTO_NAME(f
), len
, p
);
633 info("%s terminated by peer", PROTO_NAME(f
));
636 if (f
->callbacks
->down
)
637 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
638 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
642 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
647 * fsm_rtermack - Receive Terminate-Ack.
655 UNTIMEOUT(fsm_timeout
, f
);
657 if( f
->callbacks
->finished
)
658 (*f
->callbacks
->finished
)(f
);
661 UNTIMEOUT(fsm_timeout
, f
);
663 if( f
->callbacks
->finished
)
664 (*f
->callbacks
->finished
)(f
);
672 if (f
->callbacks
->down
)
673 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
682 * fsm_rcoderej - Receive an Code-Reject.
685 fsm_rcoderej(f
, inp
, len
)
692 if (len
< HEADERLEN
) {
693 FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
698 warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f
), code
, id
);
700 if( f
->state
== ACKRCVD
)
706 * fsm_protreject - Peer doesn't speak this protocol.
708 * Treat this as a catastrophic error (RXJ-).
716 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
720 if( f
->callbacks
->finished
)
721 (*f
->callbacks
->finished
)(f
);
728 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
732 if( f
->callbacks
->finished
)
733 (*f
->callbacks
->finished
)(f
);
737 terminate_layer(f
, STOPPING
);
741 FSMDEBUG(("%s: Protocol-reject event in state %d!",
742 PROTO_NAME(f
), f
->state
));
748 * fsm_sconfreq - Send a Configure-Request.
751 fsm_sconfreq(f
, retransmit
)
758 if( f
->state
!= REQSENT
&& f
->state
!= ACKRCVD
&& f
->state
!= ACKSENT
){
759 /* Not currently negotiating - reset options */
760 if( f
->callbacks
->resetci
)
761 (*f
->callbacks
->resetci
)(f
);
767 /* New request - reset retransmission counter, use new ID */
768 f
->retransmits
= f
->maxconfreqtransmits
;
775 * Make up the request packet
777 outp
= outpacket_buf
+ PPP_HDRLEN
+ HEADERLEN
;
778 if( f
->callbacks
->cilen
&& f
->callbacks
->addci
){
779 cilen
= (*f
->callbacks
->cilen
)(f
);
780 if( cilen
> peer_mru
[f
->unit
] - HEADERLEN
)
781 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
782 if (f
->callbacks
->addci
)
783 (*f
->callbacks
->addci
)(f
, outp
, &cilen
);
787 /* send the request to our peer */
788 fsm_sdata(f
, CONFREQ
, f
->reqid
, outp
, cilen
);
790 /* start the retransmit timer */
792 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
797 * fsm_sdata - Send some data.
799 * Used for all packets sent to our peer by this module.
802 fsm_sdata(f
, code
, id
, data
, datalen
)
811 /* Adjust length to be smaller than MTU */
812 outp
= outpacket_buf
;
813 if (datalen
> peer_mru
[f
->unit
] - HEADERLEN
)
814 datalen
= peer_mru
[f
->unit
] - HEADERLEN
;
815 if (datalen
&& data
!= outp
+ PPP_HDRLEN
+ HEADERLEN
)
816 BCOPY(data
, outp
+ PPP_HDRLEN
+ HEADERLEN
, datalen
);
817 outlen
= datalen
+ HEADERLEN
;
818 MAKEHEADER(outp
, f
->protocol
);
821 PUTSHORT(outlen
, outp
);
822 output(f
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);