pppd patches from wl500g.googlecode.com project
[tomato.git] / release / src / router / pppd / pppd / fsm.c
blobcfd80bea500a6335f8286c4af9c74ff308a9d22d
1 /*
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
8 * are met:
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
16 * distribution.
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
24 * 5000 Forbes Avenue
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
30 * acknowledgment:
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 $"
46 * TODO:
47 * Randomize fsm id on link/init.
48 * Deal with variable outgoing MTU.
51 #include <stdio.h>
52 #include <string.h>
53 #include <sys/types.h>
55 #include "pppd.h"
56 #include "fsm.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.
81 void
82 fsm_init(f)
83 fsm *f;
85 f->state = INITIAL;
86 f->flags = 0;
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.
99 void
100 fsm_lowerup(f)
101 fsm *f;
103 switch( f->state ){
104 case INITIAL:
105 f->state = CLOSED;
106 break;
108 case STARTING:
109 if( f->flags & OPT_SILENT )
110 f->state = STOPPED;
111 else {
112 /* Send an initial configure-request */
113 fsm_sconfreq(f, 0);
114 f->state = REQSENT;
116 break;
118 default:
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.
129 void
130 fsm_lowerdown(f)
131 fsm *f;
133 switch( f->state ){
134 case CLOSED:
135 f->state = INITIAL;
136 break;
138 case STOPPED:
139 f->state = STARTING;
140 if( f->callbacks->starting )
141 (*f->callbacks->starting)(f);
142 break;
144 case CLOSING:
145 f->state = INITIAL;
146 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
147 break;
149 case STOPPING:
150 case REQSENT:
151 case ACKRCVD:
152 case ACKSENT:
153 f->state = STARTING;
154 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
155 break;
157 case OPENED:
158 if( f->callbacks->down )
159 (*f->callbacks->down)(f);
160 f->state = STARTING;
161 break;
163 default:
164 FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
170 * fsm_open - Link is allowed to come up.
172 void
173 fsm_open(f)
174 fsm *f;
176 switch( f->state ){
177 case INITIAL:
178 f->state = STARTING;
179 if( f->callbacks->starting )
180 (*f->callbacks->starting)(f);
181 break;
183 case CLOSED:
184 if( f->flags & OPT_SILENT )
185 f->state = STOPPED;
186 else {
187 /* Send an initial configure-request */
188 fsm_sconfreq(f, 0);
189 f->state = REQSENT;
191 break;
193 case CLOSING:
194 f->state = STOPPING;
195 /* fall through */
196 case STOPPED:
197 case OPENED:
198 if( f->flags & OPT_RESTART ){
199 fsm_lowerdown(f);
200 fsm_lowerup(f);
202 break;
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.
212 static void
213 terminate_layer(f, nextstate)
214 fsm *f;
215 int 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);
236 return;
239 TIMEOUT(fsm_timeout, f, f->timeouttime);
240 --f->retransmits;
242 f->state = nextstate;
246 * fsm_close - Start closing connection.
248 * Cancel timeouts and either initiate close or possibly go directly to
249 * the CLOSED state.
251 void
252 fsm_close(f, reason)
253 fsm *f;
254 char *reason;
256 f->term_reason = reason;
257 f->term_reason_len = (reason == NULL? 0: strlen(reason));
258 switch( f->state ){
259 case STARTING:
260 f->state = INITIAL;
261 break;
262 case STOPPED:
263 f->state = CLOSED;
264 break;
265 case STOPPING:
266 f->state = CLOSING;
267 break;
269 case REQSENT:
270 case ACKRCVD:
271 case ACKSENT:
272 case OPENED:
273 terminate_layer(f, CLOSING);
274 break;
280 * fsm_timeout - Timeout expired.
282 static void
283 fsm_timeout(arg)
284 void *arg;
286 fsm *f = (fsm *) arg;
288 switch (f->state) {
289 case CLOSING:
290 case STOPPING:
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);
298 } else {
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);
303 --f->retransmits;
305 break;
307 case REQSENT:
308 case ACKRCVD:
309 case ACKSENT:
310 if (f->retransmits <= 0) {
311 /* JYWeng 20031216: add to wanstatus.log */
312 int statusindex=0;
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));
316 f->state = STOPPED;
317 if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
318 (*f->callbacks->finished)(f);
320 } else {
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 )
326 f->state = REQSENT;
328 break;
330 default:
331 FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
337 * fsm_input - Input packet.
339 void
340 fsm_input(f, inpacket, l)
341 fsm *f;
342 u_char *inpacket;
343 int l;
345 u_char *inp;
346 u_char code, id;
347 int len;
350 * Parse header (code, id and length).
351 * If packet too short, drop it.
353 inp = inpacket;
354 if (l < HEADERLEN) {
355 FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
356 return;
358 GETCHAR(code, inp);
359 GETCHAR(id, inp);
360 GETSHORT(len, inp);
361 if (len < HEADERLEN) {
362 FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
363 return;
365 if (len > l) {
366 FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
367 return;
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));
374 return;
378 * Action depends on code.
380 switch (code) {
381 case CONFREQ:
382 fsm_rconfreq(f, id, inp, len);
383 break;
385 case CONFACK:
386 fsm_rconfack(f, id, inp, len);
387 break;
389 case CONFNAK:
390 case CONFREJ:
391 fsm_rconfnakrej(f, code, id, inp, len);
392 break;
394 case TERMREQ:
395 fsm_rtermreq(f, id, inp, len);
396 break;
398 case TERMACK:
399 fsm_rtermack(f);
400 break;
402 case CODEREJ:
403 fsm_rcoderej(f, inp, len);
404 break;
406 default:
407 if( !f->callbacks->extcode
408 || !(*f->callbacks->extcode)(f, code, id, inp, len) )
409 fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
410 break;
416 * fsm_rconfreq - Receive Configure-Request.
418 static void
419 fsm_rconfreq(f, id, inp, len)
420 fsm *f;
421 u_char id;
422 u_char *inp;
423 int len;
425 int code, reject_if_disagree;
427 switch( f->state ){
428 case CLOSED:
429 /* Go away, we're closed */
430 fsm_sdata(f, TERMACK, id, NULL, 0);
431 return;
432 case CLOSING:
433 case STOPPING:
434 return;
436 case OPENED:
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 */
441 f->state = REQSENT;
442 break;
444 case STOPPED:
445 /* Negotiation started by our peer */
446 fsm_sconfreq(f, 0); /* Send initial Configure-Request */
447 f->state = REQSENT;
448 break;
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);
458 } else if (len)
459 code = CONFREJ; /* Reject all CI */
460 else
461 code = CONFACK;
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 */
469 f->state = OPENED;
470 if (f->callbacks->up)
471 (*f->callbacks->up)(f); /* Inform upper layers */
472 } else
473 f->state = ACKSENT;
474 f->nakloops = 0;
476 } else {
477 /* we sent CONFACK or CONFREJ */
478 if (f->state != ACKRCVD)
479 f->state = REQSENT;
480 if( code == CONFNAK )
481 ++f->nakloops;
487 * fsm_rconfack - Receive Configure-Ack.
489 static void
490 fsm_rconfack(f, id, inp, len)
491 fsm *f;
492 int id;
493 u_char *inp;
494 int 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):
499 (len == 0)) ){
500 /* Ack is bad - ignore it */
501 error("Received bad configure-ack: %P", inp, len);
502 return;
504 f->seen_ack = 1;
505 f->rnakloops = 0;
507 switch (f->state) {
508 case CLOSED:
509 case STOPPED:
510 fsm_sdata(f, TERMACK, id, NULL, 0);
511 break;
513 case REQSENT:
514 f->state = ACKRCVD;
515 f->retransmits = f->maxconfreqtransmits;
516 break;
518 case ACKRCVD:
519 /* Huh? an extra valid Ack? oh well... */
520 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
521 fsm_sconfreq(f, 0);
522 f->state = REQSENT;
523 break;
525 case ACKSENT:
526 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
527 f->state = OPENED;
528 f->retransmits = f->maxconfreqtransmits;
529 if (f->callbacks->up)
530 (*f->callbacks->up)(f); /* Inform upper layers */
531 break;
533 case OPENED:
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 */
538 f->state = REQSENT;
539 break;
545 * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
547 static void
548 fsm_rconfnakrej(f, code, id, inp, len)
549 fsm *f;
550 int code, id;
551 u_char *inp;
552 int len;
554 int ret;
555 int treat_as_reject;
557 if (id != f->reqid || f->seen_ack) /* Expected id? */
558 return; /* Nope, toss... */
560 if (code == CONFNAK) {
561 ++f->rnakloops;
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);
566 return;
568 } else {
569 f->rnakloops = 0;
570 if (f->callbacks->rejci == NULL
571 || !(ret = f->callbacks->rejci(f, inp, len))) {
572 error("Received bad configure-rej: %P", inp, len);
573 return;
577 f->seen_ack = 1;
579 switch (f->state) {
580 case CLOSED:
581 case STOPPED:
582 fsm_sdata(f, TERMACK, id, NULL, 0);
583 break;
585 case REQSENT:
586 case ACKSENT:
587 /* They didn't agree to what we wanted - try another request */
588 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
589 if (ret < 0)
590 f->state = STOPPED; /* kludge for stopping CCP */
591 else
592 fsm_sconfreq(f, 0); /* Send Configure-Request */
593 break;
595 case ACKRCVD:
596 /* Got a Nak/reject when we had already had an Ack?? oh well... */
597 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
598 fsm_sconfreq(f, 0);
599 f->state = REQSENT;
600 break;
602 case OPENED:
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 */
607 f->state = REQSENT;
608 break;
614 * fsm_rtermreq - Receive Terminate-Req.
616 static void
617 fsm_rtermreq(f, id, p, len)
618 fsm *f;
619 int id;
620 u_char *p;
621 int len;
623 switch (f->state) {
624 case ACKRCVD:
625 case ACKSENT:
626 f->state = REQSENT; /* Start over but keep trying */
627 break;
629 case OPENED:
630 if (len > 0) {
631 info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
632 } else
633 info("%s terminated by peer", PROTO_NAME(f));
634 f->retransmits = 0;
635 f->state = STOPPING;
636 if (f->callbacks->down)
637 (*f->callbacks->down)(f); /* Inform upper layers */
638 TIMEOUT(fsm_timeout, f, f->timeouttime);
639 break;
642 fsm_sdata(f, TERMACK, id, NULL, 0);
647 * fsm_rtermack - Receive Terminate-Ack.
649 static void
650 fsm_rtermack(f)
651 fsm *f;
653 switch (f->state) {
654 case CLOSING:
655 UNTIMEOUT(fsm_timeout, f);
656 f->state = CLOSED;
657 if( f->callbacks->finished )
658 (*f->callbacks->finished)(f);
659 break;
660 case STOPPING:
661 UNTIMEOUT(fsm_timeout, f);
662 f->state = STOPPED;
663 if( f->callbacks->finished )
664 (*f->callbacks->finished)(f);
665 break;
667 case ACKRCVD:
668 f->state = REQSENT;
669 break;
671 case OPENED:
672 if (f->callbacks->down)
673 (*f->callbacks->down)(f); /* Inform upper layers */
674 fsm_sconfreq(f, 0);
675 f->state = REQSENT;
676 break;
682 * fsm_rcoderej - Receive an Code-Reject.
684 static void
685 fsm_rcoderej(f, inp, len)
686 fsm *f;
687 u_char *inp;
688 int len;
690 u_char code, id;
692 if (len < HEADERLEN) {
693 FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
694 return;
696 GETCHAR(code, inp);
697 GETCHAR(id, inp);
698 warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
700 if( f->state == ACKRCVD )
701 f->state = REQSENT;
706 * fsm_protreject - Peer doesn't speak this protocol.
708 * Treat this as a catastrophic error (RXJ-).
710 void
711 fsm_protreject(f)
712 fsm *f;
714 switch( f->state ){
715 case CLOSING:
716 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
717 /* fall through */
718 case CLOSED:
719 f->state = CLOSED;
720 if( f->callbacks->finished )
721 (*f->callbacks->finished)(f);
722 break;
724 case STOPPING:
725 case REQSENT:
726 case ACKRCVD:
727 case ACKSENT:
728 UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
729 /* fall through */
730 case STOPPED:
731 f->state = STOPPED;
732 if( f->callbacks->finished )
733 (*f->callbacks->finished)(f);
734 break;
736 case OPENED:
737 terminate_layer(f, STOPPING);
738 break;
740 default:
741 FSMDEBUG(("%s: Protocol-reject event in state %d!",
742 PROTO_NAME(f), f->state));
748 * fsm_sconfreq - Send a Configure-Request.
750 static void
751 fsm_sconfreq(f, retransmit)
752 fsm *f;
753 int retransmit;
755 u_char *outp;
756 int cilen;
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);
762 f->nakloops = 0;
763 f->rnakloops = 0;
766 if( !retransmit ){
767 /* New request - reset retransmission counter, use new ID */
768 f->retransmits = f->maxconfreqtransmits;
769 f->reqid = ++f->id;
772 f->seen_ack = 0;
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);
784 } else
785 cilen = 0;
787 /* send the request to our peer */
788 fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
790 /* start the retransmit timer */
791 --f->retransmits;
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.
801 void
802 fsm_sdata(f, code, id, data, datalen)
803 fsm *f;
804 u_char code, id;
805 u_char *data;
806 int datalen;
808 u_char *outp;
809 int outlen;
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);
819 PUTCHAR(code, outp);
820 PUTCHAR(id, outp);
821 PUTSHORT(outlen, outp);
822 output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);