2 * fsm.c - {Link, IP} Control Protocol Finite State Machine.
4 * Copyright (c) 1989 Carnegie Mellon University.
7 * Redistribution and use in source and binary forms are permitted
8 * provided that the above copyright notice and this paragraph are
9 * duplicated in all such forms and that any documentation,
10 * advertising materials, and other materials related to such
11 * distribution and use acknowledge that the software was developed
12 * by Carnegie Mellon University. The name of the
13 * University may not be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 #define RCSID "$Id: fsm.c,v 1.1.1.4 2003/10/14 08:09:53 sparq Exp $"
24 * Randomize fsm id on link/init.
25 * Deal with variable outgoing MTU.
30 #include <sys/types.h>
35 static const char rcsid
[] = RCSID
;
37 static void fsm_timeout
__P((void *));
38 static void fsm_rconfreq
__P((fsm
*, int, u_char
*, int));
39 static void fsm_rconfack
__P((fsm
*, int, u_char
*, int));
40 static void fsm_rconfnakrej
__P((fsm
*, int, int, u_char
*, int));
41 static void fsm_rtermreq
__P((fsm
*, int, u_char
*, int));
42 static void fsm_rtermack
__P((fsm
*));
43 static void fsm_rcoderej
__P((fsm
*, u_char
*, int));
44 static void fsm_sconfreq
__P((fsm
*, int));
46 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
49 int peer_mru
[NUM_PPP
];
51 const char code_log_details
[16][32] = {
53 "Configuration-Request",
56 "Configuration-Reject",
57 "Termination-Request",
61 "#09", // Echo-Request
72 * fsm_init - Initialize fsm.
74 * Initialize fsm state.
83 f
->timeouttime
= DEFTIMEOUT
;
84 f
->maxconfreqtransmits
= DEFMAXCONFREQS
;
85 f
->maxtermtransmits
= DEFMAXTERMREQS
;
86 f
->maxnakloops
= DEFMAXNAKLOOPS
;
87 f
->term_reason_len
= 0;
92 * fsm_lowerup - The lower layer is up.
104 if( f
->flags
& OPT_SILENT
)
107 /* Send an initial configure-request */
114 FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f
), f
->state
));
120 * fsm_lowerdown - The lower layer is down.
122 * Cancel all timeouts and inform upper layers.
135 if( f
->callbacks
->starting
)
136 (*f
->callbacks
->starting
)(f
);
141 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
149 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
153 if( f
->callbacks
->down
)
154 (*f
->callbacks
->down
)(f
);
159 FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f
), f
->state
));
165 * fsm_open - Link is allowed to come up.
174 if( f
->callbacks
->starting
)
175 (*f
->callbacks
->starting
)(f
);
179 if( f
->flags
& OPT_SILENT
)
182 /* Send an initial configure-request */
193 if( f
->flags
& OPT_RESTART
){
203 * fsm_close - Start closing connection.
205 * Cancel timeouts and either initiate close or possibly go directly to
213 f
->term_reason
= reason
;
214 f
->term_reason_len
= (reason
== NULL
? 0: strlen(reason
));
230 if( f
->state
!= OPENED
)
231 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
232 else if( f
->callbacks
->down
)
233 (*f
->callbacks
->down
)(f
); /* Inform upper layers we're down */
235 /* Init restart counter, send Terminate-Request */
236 f
->retransmits
= f
->maxtermtransmits
;
237 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
238 (u_char
*) f
->term_reason
, f
->term_reason_len
);
239 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
249 * fsm_timeout - Timeout expired.
255 fsm
*f
= (fsm
*) arg
;
260 if( f
->retransmits
<= 0 ){
262 * We've waited for an ack long enough. Peer probably heard us.
264 f
->state
= (f
->state
== CLOSING
)? CLOSED
: STOPPED
;
265 if( f
->callbacks
->finished
)
266 (*f
->callbacks
->finished
)(f
);
268 /* Send Terminate-Request */
269 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
270 (u_char
*) f
->term_reason
, f
->term_reason_len
);
271 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
279 if (f
->retransmits
<= 0) {
280 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f
));
282 if( (f
->flags
& OPT_PASSIVE
) == 0 && f
->callbacks
->finished
)
283 (*f
->callbacks
->finished
)(f
);
286 /* Retransmit the configure-request */
287 if (f
->callbacks
->retransmit
)
288 (*f
->callbacks
->retransmit
)(f
);
289 fsm_sconfreq(f
, 1); /* Re-send Configure-Request */
290 if( f
->state
== ACKRCVD
)
296 FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f
), f
->state
));
302 * fsm_input - Input packet.
305 fsm_input(f
, inpacket
, l
)
315 * Parse header (code, id and length).
316 * If packet too short, drop it.
320 FSMDEBUG(("fsm_input(%x): Rcvd short header.", f
->protocol
));
326 if (len
< HEADERLEN
) {
327 FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f
->protocol
));
331 FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f
->protocol
));
334 len
-= HEADERLEN
; /* subtract header length */
336 if( f
->state
== INITIAL
|| f
->state
== STARTING
){
337 FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
338 f
->protocol
, f
->state
));
343 * Action depends on code.
347 fsm_rconfreq(f
, id
, inp
, len
);
351 fsm_rconfack(f
, id
, inp
, len
);
356 fsm_rconfnakrej(f
, code
, id
, inp
, len
);
360 fsm_rtermreq(f
, id
, inp
, len
);
368 fsm_rcoderej(f
, inp
, len
);
372 if( !f
->callbacks
->extcode
373 || !(*f
->callbacks
->extcode
)(f
, code
, id
, inp
, len
) )
375 fsm_sdata(f
, CODEREJ
, ++f
->id
, inpacket
, len
+ HEADERLEN
);
376 #if 0 //fixed by crazy 20070613 - bug id unknown (20070613)
377 // Please see line 118 on sheet 'WAN_Broadband' in document 'WG414-Q-LS_V1.0 Product Verification Plan(IN).xls'.
378 if(f
->protocol
== PPP_LCP
)
380 /* Init restart counter, send Terminate-Request */
381 f
->retransmits
= f
->maxtermtransmits
;
382 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
383 (u_char
*) f
->term_reason
, f
->term_reason_len
);
384 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
388 #endif //FIXME: After sent a LCP (it must be LCP???) CODEREJ, it must renegotiate the PPP link (CODE field must be 0xFF???)
396 * fsm_rconfreq - Receive Configure-Request.
399 fsm_rconfreq(f
, id
, inp
, len
)
405 int code
, reject_if_disagree
;
407 LOGX_INFO("Received %s %s", PNAME(f
), code_log_details
[CONFREQ
]);
411 /* Go away, we're closed */
412 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
419 /* Go down and restart negotiation */
420 if( f
->callbacks
->down
)
421 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
422 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
426 /* Negotiation started by our peer */
427 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
433 * Pass the requested configuration options
434 * to protocol-specific code for checking.
436 if (f
->callbacks
->reqci
){ /* Check CI */
437 reject_if_disagree
= (f
->nakloops
>= f
->maxnakloops
);
438 code
= (*f
->callbacks
->reqci
)(f
, inp
, &len
, reject_if_disagree
);
440 code
= CONFREJ
; /* Reject all CI */
444 /* send the Ack, Nak or Rej to the peer */
445 fsm_sdata(f
, code
, id
, inp
, len
);
447 if (code
== CONFACK
) {
448 if (f
->state
== ACKRCVD
) {
449 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
451 if (f
->callbacks
->up
)
452 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
458 /* we sent CONFACK or CONFREJ */
459 if (f
->state
!= ACKRCVD
)
461 if( code
== CONFNAK
)
468 * fsm_rconfack - Receive Configure-Ack.
471 fsm_rconfack(f
, id
, inp
, len
)
477 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
478 return; /* Nope, toss... */
479 if( !(f
->callbacks
->ackci
? (*f
->callbacks
->ackci
)(f
, inp
, len
):
481 /* Ack is bad - ignore it */
482 error("Received bad configure-ack: %P", inp
, len
);
487 LOGX_INFO("Received %s %s", PNAME(f
), code_log_details
[CONFACK
]);
492 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
497 f
->retransmits
= f
->maxconfreqtransmits
;
501 /* Huh? an extra valid Ack? oh well... */
502 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
508 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
510 f
->retransmits
= f
->maxconfreqtransmits
;
511 if (f
->callbacks
->up
)
512 (*f
->callbacks
->up
)(f
); /* Inform upper layers */
516 /* Go down and restart negotiation */
517 if (f
->callbacks
->down
)
518 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
519 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
527 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
530 fsm_rconfnakrej(f
, code
, id
, inp
, len
)
536 int (*proc
) __P((fsm
*, u_char
*, int));
539 if (id
!= f
->reqid
|| f
->seen_ack
) /* Expected id? */
540 return; /* Nope, toss... */
541 proc
= (code
== CONFNAK
)? f
->callbacks
->nakci
: f
->callbacks
->rejci
;
542 if (!proc
|| !(ret
= proc(f
, inp
, len
))) {
543 /* Nak/reject is bad - ignore it */
544 error("Received bad configure-nak/rej: %P", inp
, len
);
549 if ((code
> 0) && (code
< 16)) {
550 LOGX_INFO("Received %s %s", PNAME(f
), code_log_details
[code
]);
556 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
561 /* They didn't agree to what we wanted - try another request */
562 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
564 f
->state
= STOPPED
; /* kludge for stopping CCP */
566 fsm_sconfreq(f
, 0); /* Send Configure-Request */
570 /* Got a Nak/reject when we had already had an Ack?? oh well... */
571 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
577 /* Go down and restart negotiation */
578 if (f
->callbacks
->down
)
579 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
580 fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
588 * fsm_rtermreq - Receive Terminate-Req.
591 fsm_rtermreq(f
, id
, p
, len
)
597 LOGX_INFO("Received %s %s", PNAME(f
), code_log_details
[TERMREQ
]);
602 f
->state
= REQSENT
; /* Start over but keep trying */
607 info("%s terminated by peer (%0.*v)", PROTO_NAME(f
), len
, p
);
609 info("%s terminated by peer", PROTO_NAME(f
));
610 if (f
->callbacks
->down
)
611 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
614 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
618 fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
623 * fsm_rtermack - Receive Terminate-Ack.
629 LOGX_INFO("Received %s %s", PNAME(f
), code_log_details
[TERMACK
]);
633 UNTIMEOUT(fsm_timeout
, f
);
635 if( f
->callbacks
->finished
)
636 (*f
->callbacks
->finished
)(f
);
639 UNTIMEOUT(fsm_timeout
, f
);
641 if( f
->callbacks
->finished
)
642 (*f
->callbacks
->finished
)(f
);
650 if (f
->callbacks
->down
)
651 (*f
->callbacks
->down
)(f
); /* Inform upper layers */
659 * fsm_rcoderej - Receive an Code-Reject.
662 fsm_rcoderej(f
, inp
, len
)
669 if (len
< HEADERLEN
) {
670 FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
676 LOGX_INFO("Received %s %s", PNAME(f
), code_log_details
[CODEREJ
]);
678 warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f
), code
, id
);
680 if( f
->state
== ACKRCVD
)
686 * fsm_protreject - Peer doesn't speak this protocol.
688 * Treat this as a catastrophic error (RXJ-).
696 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
700 if( f
->callbacks
->finished
)
701 (*f
->callbacks
->finished
)(f
);
708 UNTIMEOUT(fsm_timeout
, f
); /* Cancel timeout */
712 if( f
->callbacks
->finished
)
713 (*f
->callbacks
->finished
)(f
);
717 if( f
->callbacks
->down
)
718 (*f
->callbacks
->down
)(f
);
720 /* Init restart counter, send Terminate-Request */
721 f
->retransmits
= f
->maxtermtransmits
;
722 fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
,
723 (u_char
*) f
->term_reason
, f
->term_reason_len
);
724 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
731 FSMDEBUG(("%s: Protocol-reject event in state %d!",
732 PROTO_NAME(f
), f
->state
));
738 * fsm_sconfreq - Send a Configure-Request.
741 fsm_sconfreq(f
, retransmit
)
748 if( f
->state
!= REQSENT
&& f
->state
!= ACKRCVD
&& f
->state
!= ACKSENT
){
749 /* Not currently negotiating - reset options */
750 if( f
->callbacks
->resetci
)
751 (*f
->callbacks
->resetci
)(f
);
756 /* New request - reset retransmission counter, use new ID */
757 f
->retransmits
= f
->maxconfreqtransmits
;
764 * Make up the request packet
766 outp
= outpacket_buf
+ PPP_HDRLEN
+ HEADERLEN
;
767 if( f
->callbacks
->cilen
&& f
->callbacks
->addci
){
768 cilen
= (*f
->callbacks
->cilen
)(f
);
769 if( cilen
> peer_mru
[f
->unit
] - HEADERLEN
)
770 cilen
= peer_mru
[f
->unit
] - HEADERLEN
;
771 if (f
->callbacks
->addci
)
772 (*f
->callbacks
->addci
)(f
, outp
, &cilen
);
776 /* send the request to our peer */
777 fsm_sdata(f
, CONFREQ
, f
->reqid
, outp
, cilen
);
779 /* start the retransmit timer */
781 TIMEOUT(fsm_timeout
, f
, f
->timeouttime
);
786 * fsm_sdata - Send some data.
788 * Used for all packets sent to our peer by this module.
791 fsm_sdata(f
, code
, id
, data
, datalen
)
800 /* Adjust length to be smaller than MTU */
801 outp
= outpacket_buf
;
802 if (datalen
> peer_mru
[f
->unit
] - HEADERLEN
)
803 datalen
= peer_mru
[f
->unit
] - HEADERLEN
;
804 if (datalen
&& data
!= outp
+ PPP_HDRLEN
+ HEADERLEN
)
805 BCOPY(data
, outp
+ PPP_HDRLEN
+ HEADERLEN
, datalen
);
806 outlen
= datalen
+ HEADERLEN
;
807 MAKEHEADER(outp
, f
->protocol
);
810 PUTSHORT(outlen
, outp
);
811 output(f
->unit
, outpacket_buf
, outlen
+ PPP_HDRLEN
);
813 // if ((code > 0) && (code < 16)) {
814 if ((code
> 0) && (code
< 16) && (code_log_details
[code
][0] != '#')) {
815 LOGX_INFO("Sending %s %s", PNAME(f
), code_log_details
[code
]);