CPU: Wrong CPU Load %.
[tomato.git] / release / src / router / ppp / pppd / fsm.c
blob03bd5799d8eed3d59b2b125d90c25e91d9178e03
1 /*
2 * fsm.c - {Link, IP} Control Protocol Finite State Machine.
4 * Copyright (c) 1989 Carnegie Mellon University.
5 * All rights reserved.
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 $"
23 * TODO:
24 * Randomize fsm id on link/init.
25 * Deal with variable outgoing MTU.
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
32 #include <pppd.h>
33 #include "fsm.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] = {
52 "#00",
53 "Configuration-Request",
54 "Configuration-Ack",
55 "Configuration-Nak",
56 "Configuration-Reject",
57 "Termination-Request",
58 "Termination-Ack",
59 "Code-Reject",
60 "Protocol-Reject",
61 "#09", // Echo-Request
62 "#10", // Echo-Reply
63 "Discard-Request",
64 "#12",
65 "#13",
66 "Reset-Request",
67 "Reset-ACK"
72 * fsm_init - Initialize fsm.
74 * Initialize fsm state.
76 void
77 fsm_init(f)
78 fsm *f;
80 f->state = INITIAL;
81 f->flags = 0;
82 f->id = 0;
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.
94 void
95 fsm_lowerup(f)
96 fsm *f;
98 switch( f->state ){
99 case INITIAL:
100 f->state = CLOSED;
101 break;
103 case STARTING:
104 if( f->flags & OPT_SILENT )
105 f->state = STOPPED;
106 else {
107 /* Send an initial configure-request */
108 fsm_sconfreq(f, 0);
109 f->state = REQSENT;
111 break;
113 default:
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.
124 void
125 fsm_lowerdown(f)
126 fsm *f;
128 switch( f->state ){
129 case CLOSED:
130 f->state = INITIAL;
131 break;
133 case STOPPED:
134 f->state = STARTING;
135 if( f->callbacks->starting )
136 (*f->callbacks->starting)(f);
137 break;
139 case CLOSING:
140 f->state = INITIAL;
141 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
142 break;
144 case STOPPING:
145 case REQSENT:
146 case ACKRCVD:
147 case ACKSENT:
148 f->state = STARTING;
149 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
150 break;
152 case OPENED:
153 if( f->callbacks->down )
154 (*f->callbacks->down)(f);
155 f->state = STARTING;
156 break;
158 default:
159 FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
165 * fsm_open - Link is allowed to come up.
167 void
168 fsm_open(f)
169 fsm *f;
171 switch( f->state ){
172 case INITIAL:
173 f->state = STARTING;
174 if( f->callbacks->starting )
175 (*f->callbacks->starting)(f);
176 break;
178 case CLOSED:
179 if( f->flags & OPT_SILENT )
180 f->state = STOPPED;
181 else {
182 /* Send an initial configure-request */
183 fsm_sconfreq(f, 0);
184 f->state = REQSENT;
186 break;
188 case CLOSING:
189 f->state = STOPPING;
190 /* fall through */
191 case STOPPED:
192 case OPENED:
193 if( f->flags & OPT_RESTART ){
194 fsm_lowerdown(f);
195 fsm_lowerup(f);
197 break;
203 * fsm_close - Start closing connection.
205 * Cancel timeouts and either initiate close or possibly go directly to
206 * the CLOSED state.
208 void
209 fsm_close(f, reason)
210 fsm *f;
211 char *reason;
213 f->term_reason = reason;
214 f->term_reason_len = (reason == NULL? 0: strlen(reason));
215 switch( f->state ){
216 case STARTING:
217 f->state = INITIAL;
218 break;
219 case STOPPED:
220 f->state = CLOSED;
221 break;
222 case STOPPING:
223 f->state = CLOSING;
224 break;
226 case REQSENT:
227 case ACKRCVD:
228 case ACKSENT:
229 case OPENED:
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);
240 --f->retransmits;
242 f->state = CLOSING;
243 break;
249 * fsm_timeout - Timeout expired.
251 static void
252 fsm_timeout(arg)
253 void *arg;
255 fsm *f = (fsm *) arg;
257 switch (f->state) {
258 case CLOSING:
259 case STOPPING:
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);
267 } else {
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);
272 --f->retransmits;
274 break;
276 case REQSENT:
277 case ACKRCVD:
278 case ACKSENT:
279 if (f->retransmits <= 0) {
280 warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f));
281 f->state = STOPPED;
282 if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
283 (*f->callbacks->finished)(f);
285 } else {
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 )
291 f->state = REQSENT;
293 break;
295 default:
296 FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
302 * fsm_input - Input packet.
304 void
305 fsm_input(f, inpacket, l)
306 fsm *f;
307 u_char *inpacket;
308 int l;
310 u_char *inp;
311 u_char code, id;
312 int len;
315 * Parse header (code, id and length).
316 * If packet too short, drop it.
318 inp = inpacket;
319 if (l < HEADERLEN) {
320 FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
321 return;
323 GETCHAR(code, inp);
324 GETCHAR(id, inp);
325 GETSHORT(len, inp);
326 if (len < HEADERLEN) {
327 FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
328 return;
330 if (len > l) {
331 FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
332 return;
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));
339 return;
343 * Action depends on code.
345 switch (code) {
346 case CONFREQ:
347 fsm_rconfreq(f, id, inp, len);
348 break;
350 case CONFACK:
351 fsm_rconfack(f, id, inp, len);
352 break;
354 case CONFNAK:
355 case CONFREJ:
356 fsm_rconfnakrej(f, code, id, inp, len);
357 break;
359 case TERMREQ:
360 fsm_rtermreq(f, id, inp, len);
361 break;
363 case TERMACK:
364 fsm_rtermack(f);
365 break;
367 case CODEREJ:
368 fsm_rcoderej(f, inp, len);
369 break;
371 default:
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);
385 --f->retransmits;
386 f->state = STOPPING;
388 #endif //FIXME: After sent a LCP (it must be LCP???) CODEREJ, it must renegotiate the PPP link (CODE field must be 0xFF???)
390 break;
396 * fsm_rconfreq - Receive Configure-Request.
398 static void
399 fsm_rconfreq(f, id, inp, len)
400 fsm *f;
401 u_char id;
402 u_char *inp;
403 int len;
405 int code, reject_if_disagree;
407 LOGX_INFO("Received %s %s", PNAME(f), code_log_details[CONFREQ]);
409 switch( f->state ){
410 case CLOSED:
411 /* Go away, we're closed */
412 fsm_sdata(f, TERMACK, id, NULL, 0);
413 return;
414 case CLOSING:
415 case STOPPING:
416 return;
418 case OPENED:
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 */
423 break;
425 case STOPPED:
426 /* Negotiation started by our peer */
427 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
428 f->state = REQSENT;
429 break;
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);
439 } else if (len)
440 code = CONFREJ; /* Reject all CI */
441 else
442 code = CONFACK;
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 */
450 f->state = OPENED;
451 if (f->callbacks->up)
452 (*f->callbacks->up)(f); /* Inform upper layers */
453 } else
454 f->state = ACKSENT;
455 f->nakloops = 0;
457 } else {
458 /* we sent CONFACK or CONFREJ */
459 if (f->state != ACKRCVD)
460 f->state = REQSENT;
461 if( code == CONFNAK )
462 ++f->nakloops;
468 * fsm_rconfack - Receive Configure-Ack.
470 static void
471 fsm_rconfack(f, id, inp, len)
472 fsm *f;
473 int id;
474 u_char *inp;
475 int 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):
480 (len == 0)) ){
481 /* Ack is bad - ignore it */
482 error("Received bad configure-ack: %P", inp, len);
483 return;
485 f->seen_ack = 1;
487 LOGX_INFO("Received %s %s", PNAME(f), code_log_details[CONFACK]);
489 switch (f->state) {
490 case CLOSED:
491 case STOPPED:
492 fsm_sdata(f, TERMACK, id, NULL, 0);
493 break;
495 case REQSENT:
496 f->state = ACKRCVD;
497 f->retransmits = f->maxconfreqtransmits;
498 break;
500 case ACKRCVD:
501 /* Huh? an extra valid Ack? oh well... */
502 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
503 fsm_sconfreq(f, 0);
504 f->state = REQSENT;
505 break;
507 case ACKSENT:
508 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
509 f->state = OPENED;
510 f->retransmits = f->maxconfreqtransmits;
511 if (f->callbacks->up)
512 (*f->callbacks->up)(f); /* Inform upper layers */
513 break;
515 case OPENED:
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 */
520 f->state = REQSENT;
521 break;
527 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
529 static void
530 fsm_rconfnakrej(f, code, id, inp, len)
531 fsm *f;
532 int code, id;
533 u_char *inp;
534 int len;
536 int (*proc) __P((fsm *, u_char *, int));
537 int ret;
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);
545 return;
547 f->seen_ack = 1;
549 if ((code > 0) && (code < 16)) {
550 LOGX_INFO("Received %s %s", PNAME(f), code_log_details[code]);
553 switch (f->state) {
554 case CLOSED:
555 case STOPPED:
556 fsm_sdata(f, TERMACK, id, NULL, 0);
557 break;
559 case REQSENT:
560 case ACKSENT:
561 /* They didn't agree to what we wanted - try another request */
562 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
563 if (ret < 0)
564 f->state = STOPPED; /* kludge for stopping CCP */
565 else
566 fsm_sconfreq(f, 0); /* Send Configure-Request */
567 break;
569 case ACKRCVD:
570 /* Got a Nak/reject when we had already had an Ack?? oh well... */
571 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
572 fsm_sconfreq(f, 0);
573 f->state = REQSENT;
574 break;
576 case OPENED:
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 */
581 f->state = REQSENT;
582 break;
588 * fsm_rtermreq - Receive Terminate-Req.
590 static void
591 fsm_rtermreq(f, id, p, len)
592 fsm *f;
593 int id;
594 u_char *p;
595 int len;
597 LOGX_INFO("Received %s %s", PNAME(f), code_log_details[TERMREQ]);
599 switch (f->state) {
600 case ACKRCVD:
601 case ACKSENT:
602 f->state = REQSENT; /* Start over but keep trying */
603 break;
605 case OPENED:
606 if (len > 0) {
607 info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
608 } else
609 info("%s terminated by peer", PROTO_NAME(f));
610 if (f->callbacks->down)
611 (*f->callbacks->down)(f); /* Inform upper layers */
612 f->retransmits = 0;
613 f->state = STOPPING;
614 TIMEOUT(fsm_timeout, f, f->timeouttime);
615 break;
618 fsm_sdata(f, TERMACK, id, NULL, 0);
623 * fsm_rtermack - Receive Terminate-Ack.
625 static void
626 fsm_rtermack(f)
627 fsm *f;
629 LOGX_INFO("Received %s %s", PNAME(f), code_log_details[TERMACK]);
631 switch (f->state) {
632 case CLOSING:
633 UNTIMEOUT(fsm_timeout, f);
634 f->state = CLOSED;
635 if( f->callbacks->finished )
636 (*f->callbacks->finished)(f);
637 break;
638 case STOPPING:
639 UNTIMEOUT(fsm_timeout, f);
640 f->state = STOPPED;
641 if( f->callbacks->finished )
642 (*f->callbacks->finished)(f);
643 break;
645 case ACKRCVD:
646 f->state = REQSENT;
647 break;
649 case OPENED:
650 if (f->callbacks->down)
651 (*f->callbacks->down)(f); /* Inform upper layers */
652 fsm_sconfreq(f, 0);
653 break;
659 * fsm_rcoderej - Receive an Code-Reject.
661 static void
662 fsm_rcoderej(f, inp, len)
663 fsm *f;
664 u_char *inp;
665 int len;
667 u_char code, id;
669 if (len < HEADERLEN) {
670 FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
671 return;
673 GETCHAR(code, inp);
674 GETCHAR(id, inp);
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 )
681 f->state = REQSENT;
686 * fsm_protreject - Peer doesn't speak this protocol.
688 * Treat this as a catastrophic error (RXJ-).
690 void
691 fsm_protreject(f)
692 fsm *f;
694 switch( f->state ){
695 case CLOSING:
696 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
697 /* fall through */
698 case CLOSED:
699 f->state = CLOSED;
700 if( f->callbacks->finished )
701 (*f->callbacks->finished)(f);
702 break;
704 case STOPPING:
705 case REQSENT:
706 case ACKRCVD:
707 case ACKSENT:
708 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
709 /* fall through */
710 case STOPPED:
711 f->state = STOPPED;
712 if( f->callbacks->finished )
713 (*f->callbacks->finished)(f);
714 break;
716 case OPENED:
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);
725 --f->retransmits;
727 f->state = STOPPING;
728 break;
730 default:
731 FSMDEBUG(("%s: Protocol-reject event in state %d!",
732 PROTO_NAME(f), f->state));
738 * fsm_sconfreq - Send a Configure-Request.
740 static void
741 fsm_sconfreq(f, retransmit)
742 fsm *f;
743 int retransmit;
745 u_char *outp;
746 int cilen;
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);
752 f->nakloops = 0;
755 if( !retransmit ){
756 /* New request - reset retransmission counter, use new ID */
757 f->retransmits = f->maxconfreqtransmits;
758 f->reqid = ++f->id;
761 f->seen_ack = 0;
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);
773 } else
774 cilen = 0;
776 /* send the request to our peer */
777 fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
779 /* start the retransmit timer */
780 --f->retransmits;
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.
790 void
791 fsm_sdata(f, code, id, data, datalen)
792 fsm *f;
793 u_char code, id;
794 u_char *data;
795 int datalen;
797 u_char *outp;
798 int outlen;
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);
808 PUTCHAR(code, outp);
809 PUTCHAR(id, outp);
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]);