http://downloads.netgear.com/files/GPL/GPL_Source_V361j_DM111PSP_series_consumer_rele...
[bcm963xx.git] / userapps / opensource / ppp / pppoe / fsm.c
1 /*
2  * fsm.c - {Link, IP} Control Protocol Finite State Machine.
3  *
4  * Copyright (c) 1989 Carnegie Mellon University.
5  * All rights reserved.
6  *
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.
18  */
19
20 #define RCSID   "$Id: fsm.c,v 1.2 2006/05/31 09:34:26 andylin Exp $"
21
22 /*
23  * TODO:
24  * Randomize fsm id on link/init.
25  * Deal with variable outgoing MTU.
26  */
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31
32 #include "pppd.h"
33 #include "fsm.h"
34
35 static const char rcsid[] = RCSID;
36
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));
45
46 #define PROTO_NAME(f)   ((f)->callbacks->proto_name)
47
48 int peer_mru[NUM_PPP];
49
50 //#ifdef BBB_XML_API //Wilson add, (02/13/2006)
51 #if defined(SUPPORT_XML_API) //Wilson add, (03/14/2006)
52 extern int ppp_session;
53 #endif
54
55 /*
56  * fsm_init - Initialize fsm.
57  *
58  * Initialize fsm state.
59  */
60 void
61 fsm_init(f)
62     fsm *f;
63 {
64     f->state = INITIAL;
65     f->flags = 0;
66     f->id = 0;                          /* XXX Start with random id? */
67     f->timeouttime = DEFTIMEOUT;
68     f->maxconfreqtransmits = DEFMAXCONFREQS;
69     // brcm
70     if (autoscan)
71         f->maxconfreqtransmits = ses_retries;
72     f->maxtermtransmits = DEFMAXTERMREQS;
73     f->maxnakloops = DEFMAXNAKLOOPS;
74     f->term_reason_len = 0;
75 }
76
77
78 /*
79  * fsm_lowerup - The lower layer is up.
80  */
81 void
82 fsm_lowerup(f)
83     fsm *f;
84 {
85     switch( f->state ){
86     case INITIAL:
87         f->state = CLOSED;
88         break;
89
90     case STARTING:
91         if( f->flags & OPT_SILENT )
92             f->state = STOPPED;
93         else {
94             /* Send an initial configure-request */
95             fsm_sconfreq(f, 0);
96             f->state = REQSENT;
97         }
98         break;
99
100     default:
101         FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
102     }
103 }
104
105
106 /*
107  * fsm_lowerdown - The lower layer is down.
108  *
109  * Cancel all timeouts and inform upper layers.
110  */
111 void
112 fsm_lowerdown(f)
113     fsm *f;
114 {
115     switch( f->state ){
116     case CLOSED:
117         f->state = INITIAL;
118         break;
119
120     case STOPPED:
121         f->state = STARTING;
122         if( f->callbacks->starting )
123             (*f->callbacks->starting)(f);
124         break;
125
126     case CLOSING:
127         f->state = INITIAL;
128         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
129         break;
130
131     case STOPPING:
132     case REQSENT:
133     case ACKRCVD:
134     case ACKSENT:
135         f->state = STARTING;
136         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
137         break;
138
139     case OPENED:
140         if( f->callbacks->down )
141             (*f->callbacks->down)(f);
142         f->state = STARTING;
143         break;
144
145     default:
146         FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
147     }
148 }
149
150
151 /*
152  * fsm_open - Link is allowed to come up.
153  */
154 void
155 fsm_open(f)
156     fsm *f;
157 {
158     switch( f->state ){
159     case INITIAL:
160         f->state = STARTING;
161         if( f->callbacks->starting )
162             (*f->callbacks->starting)(f);
163         break;
164
165     case CLOSED:
166         if( f->flags & OPT_SILENT )
167             f->state = STOPPED;
168         else {
169             /* Send an initial configure-request */
170             fsm_sconfreq(f, 0);
171             f->state = REQSENT;
172         }
173         break;
174
175     case CLOSING:
176         f->state = STOPPING;
177         /* fall through */
178     case STOPPED:
179     case OPENED:
180         if( f->flags & OPT_RESTART ){
181             fsm_lowerdown(f);
182             fsm_lowerup(f);
183         }
184         break;
185     }
186 }
187
188
189 /*
190  * fsm_close - Start closing connection.
191  *
192  * Cancel timeouts and either initiate close or possibly go directly to
193  * the CLOSED state.
194  */
195 void
196 fsm_close(f, reason)
197     fsm *f;
198     char *reason;
199 {
200     f->term_reason = reason;
201     f->term_reason_len = (reason == NULL? 0: strlen(reason));
202     switch( f->state ){
203     case STARTING:
204         f->state = INITIAL;
205         break;
206     case STOPPED:
207         f->state = CLOSED;
208         break;
209     case STOPPING:
210         f->state = CLOSING;
211         break;
212
213     case REQSENT:
214     case ACKRCVD:
215     case ACKSENT:
216     case OPENED:
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 */
221
222         /* Init restart counter, send Terminate-Request */
223         f->retransmits = f->maxtermtransmits;
224                 fsm_sdata(f, TERMREQ, f->reqid = ++f->id, (u_char *) f->term_reason, f->term_reason_len);
225         TIMEOUT(fsm_timeout, f, f->timeouttime);
226         --f->retransmits;
227
228         f->state = CLOSING;
229         break;
230     }
231 }
232
233
234 /*
235  * fsm_timeout - Timeout expired.
236  */
237 //extern int ppp_session;
238 extern glbppppid; 
239 static void
240 fsm_timeout(arg)
241     void *arg;
242 {
243     fsm *f = (fsm *) arg;
244         char cmd[30];
245         int oldfsmstate =0;
246 //#ifdef BBB_XML_API //Wilson add, (02/13/2006)
247 #if defined(SUPPORT_XML_API) //Wilson add, (03/14/2006)
248     FILE *fp=NULL;            
249 #endif //endif BBB_XML_API
250
251     switch (f->state) {
252     case CLOSING:
253     case STOPPING:
254         if( f->retransmits <= 0 ){
255             /*
256              * We've waited for an ack long enough.  Peer probably heard us.
257              */
258             f->state = (f->state == CLOSING)? CLOSED: STOPPED;
259             if( f->callbacks->finished )
260                 (*f->callbacks->finished)(f);
261         } else {
262             /* Send Terminate-Request */
263             fsm_sdata(f, TERMREQ, f->reqid = ++f->id, (u_char *) f->term_reason, f->term_reason_len);
264             TIMEOUT(fsm_timeout, f, f->timeouttime);
265             --f->retransmits;
266         }
267         break;
268
269     case REQSENT:
270     case ACKRCVD:
271     case ACKSENT:
272         if (f->retransmits <= 0) {
273             warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f));
274             oldfsmstate = f->state; //wilson add for BBB_XML_API, 02/13/2006
275             f->state = STOPPED;
276             if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
277                 (*f->callbacks->finished)(f);
278                 /*andy add 2005/04/01*/
279                 //if (oldfsmstate == REQSENT && ppp_session == PPPOA){
280                 //      printf("PPPOA:PVC error, Bye!!\n");
281                 //      sprintf(cmd,"kill -9 %d",glbppppid);
282                 //      system(cmd);
283                 //}
284 //#ifdef BBB_XML_API //Wilson add, (02/13/2006)
285 #if defined(SUPPORT_XML_API) //Wilson add, (03/14/2006)
286             if (oldfsmstate == REQSENT && ppp_session == PPPOA){
287                 fp = fopen("/var/pppStatus", "w+");
288                 if(fp){
289                     fprintf(fp, "2,2");
290                     fclose(fp);
291                 }
292             }
293 #endif //endif BBB_XML_API
294         } else {
295             /* Retransmit the configure-request */
296             if (f->callbacks->retransmit)
297                 (*f->callbacks->retransmit)(f);
298             fsm_sconfreq(f, 1);         /* Re-send Configure-Request */
299             if( f->state == ACKRCVD )
300                 f->state = REQSENT;
301         }
302         break;
303
304     default:
305         FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
306     }
307 }
308
309
310 /*
311  * fsm_input - Input packet.
312  */
313 void
314 fsm_input(f, inpacket, l)
315     fsm *f;
316     u_char *inpacket;
317     int l;
318 {
319     u_char *inp;
320     u_char code, id;
321     int len;
322
323     /*
324      * Parse header (code, id and length).
325      * If packet too short, drop it.
326      */
327     inp = inpacket;
328     if (l < HEADERLEN) {
329         FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
330         return;
331     }
332     GETCHAR(code, inp);
333     GETCHAR(id, inp);
334     GETSHORT(len, inp);
335     if (len < HEADERLEN) {
336         FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
337         return;
338     }
339     if (len > l) {
340         FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
341         return;
342     }
343     len -= HEADERLEN;           /* subtract header length */
344
345     if( f->state == INITIAL || f->state == STARTING ){
346                 FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.", f->protocol, f->state));
347         return;
348     }
349
350     /*
351      * Action depends on code.
352      */
353     switch (code) {
354     case CONFREQ:
355         fsm_rconfreq(f, id, inp, len);
356         break;
357     
358     case CONFACK:
359         fsm_rconfack(f, id, inp, len);
360         break;
361     
362     case CONFNAK:
363     case CONFREJ:
364         fsm_rconfnakrej(f, code, id, inp, len);
365         break;
366     
367     case TERMREQ:
368         fsm_rtermreq(f, id, inp, len);
369         break;
370     
371     case TERMACK:
372         fsm_rtermack(f);
373         break;
374     
375     case CODEREJ:
376         fsm_rcoderej(f, inp, len);
377         break;
378     
379     default:
380                 if( !f->callbacks->extcode || !(*f->callbacks->extcode)(f, code, id, inp, len) )
381             fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
382         break;
383     }
384 }
385
386
387 /*
388  * fsm_rconfreq - Receive Configure-Request.
389  */
390 static void
391 fsm_rconfreq(f, id, inp, len)
392     fsm *f;
393     u_char id;
394     u_char *inp;
395     int len;
396 {
397     int code, reject_if_disagree;
398
399     switch( f->state ){
400     case CLOSED:
401         /* Go away, we're closed */
402         fsm_sdata(f, TERMACK, id, NULL, 0);
403         return;
404     case CLOSING:
405     case STOPPING:
406         return;
407
408     case OPENED:
409         /* Go down and restart negotiation */
410         if( f->callbacks->down )
411             (*f->callbacks->down)(f);   /* Inform upper layers */
412         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
413         break;
414
415     case STOPPED:
416         /* Negotiation started by our peer */
417         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
418         f->state = REQSENT;
419         break;
420     }
421
422     /*
423      * Pass the requested configuration options
424      * to protocol-specific code for checking.
425      */
426     if (f->callbacks->reqci){           /* Check CI */
427         reject_if_disagree = (f->nakloops >= f->maxnakloops);
428         code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
429     } else if (len)
430         code = CONFREJ;                 /* Reject all CI */
431     else
432         code = CONFACK;
433
434     /* send the Ack, Nak or Rej to the peer */
435     fsm_sdata(f, code, id, inp, len);
436
437     if (code == CONFACK) {
438         if (f->state == ACKRCVD) {
439             UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
440             f->state = OPENED;
441                 if (f->callbacks->up) {
442                 (*f->callbacks->up)(f); /* Inform upper layers */
443                 }
444         } else
445             f->state = ACKSENT;
446         f->nakloops = 0;
447
448     } else {
449         /* we sent CONFACK or CONFREJ */
450         if (f->state != ACKRCVD)
451             f->state = REQSENT;
452         if( code == CONFNAK )
453             ++f->nakloops;
454     }
455 }
456
457
458 /*
459  * fsm_rconfack - Receive Configure-Ack.
460  */
461 static void
462 fsm_rconfack(f, id, inp, len)
463     fsm *f;
464     int id;
465     u_char *inp;
466     int len;
467 {
468     if (id != f->reqid || f->seen_ack)          /* Expected id? */
469         return;                                 /* Nope, toss... */
470     if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ){
471         /* Ack is bad - ignore it */
472         error("Received bad configure-ack: %P", inp, len);
473         return;
474     }
475     f->seen_ack = 1;
476
477     switch (f->state) {
478     case CLOSED:
479     case STOPPED:
480         fsm_sdata(f, TERMACK, id, NULL, 0);
481         break;
482
483     case REQSENT:
484         f->state = ACKRCVD;
485         f->retransmits = f->maxconfreqtransmits;
486         break;
487
488     case ACKRCVD:
489         /* Huh? an extra valid Ack? oh well... */
490         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
491         fsm_sconfreq(f, 0);
492         f->state = REQSENT;
493         break;
494
495     case ACKSENT:
496         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
497         f->state = OPENED;
498         f->retransmits = f->maxconfreqtransmits;
499                 if (f->callbacks->up) {
500             (*f->callbacks->up)(f);     /* Inform upper layers */
501                 }
502         break;
503
504     case OPENED:
505         /* Go down and restart negotiation */
506         if (f->callbacks->down)
507             (*f->callbacks->down)(f);   /* Inform upper layers */
508         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
509         f->state = REQSENT;
510         break;
511     }
512 }
513
514
515 /*
516  * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
517  */
518 static void
519 fsm_rconfnakrej(f, code, id, inp, len)
520     fsm *f;
521     int code, id;
522     u_char *inp;
523     int len;
524 {
525     int (*proc) __P((fsm *, u_char *, int));
526     int ret;
527
528     if (id != f->reqid || f->seen_ack)  /* Expected id? */
529         return;                         /* Nope, toss... */
530     proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
531     if (!proc || !(ret = proc(f, inp, len))) {
532         /* Nak/reject is bad - ignore it */
533         error("Received bad configure-nak/rej: %P", inp, len);
534         return;
535     }
536     f->seen_ack = 1;
537
538     switch (f->state) {
539     case CLOSED:
540     case STOPPED:
541         fsm_sdata(f, TERMACK, id, NULL, 0);
542         break;
543
544     case REQSENT:
545     case ACKSENT:
546         /* They didn't agree to what we wanted - try another request */
547         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
548         if (ret < 0)
549             f->state = STOPPED;         /* kludge for stopping CCP */
550         else
551             fsm_sconfreq(f, 0);         /* Send Configure-Request */
552         break;
553
554     case ACKRCVD:
555         /* Got a Nak/reject when we had already had an Ack?? oh well... */
556         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
557         fsm_sconfreq(f, 0);
558         f->state = REQSENT;
559         break;
560
561     case OPENED:
562         /* Go down and restart negotiation */
563         if (f->callbacks->down)
564             (*f->callbacks->down)(f);   /* Inform upper layers */
565         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
566         f->state = REQSENT;
567         break;
568     }
569 }
570
571
572 /*
573  * fsm_rtermreq - Receive Terminate-Req.
574  */
575 static void
576 fsm_rtermreq(f, id, p, len)
577     fsm *f;
578     int id;
579     u_char *p;
580     int len;
581 {
582     switch (f->state) {
583     case ACKRCVD:
584     case ACKSENT:
585         f->state = REQSENT;             /* Start over but keep trying */
586         break;
587
588     case OPENED:
589         if (len > 0) {
590             info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
591         } else
592             info("%s terminated by peer", PROTO_NAME(f));
593         if (f->callbacks->down)
594             (*f->callbacks->down)(f);   /* Inform upper layers */
595         f->retransmits = 0;
596         f->state = STOPPING;
597         TIMEOUT(fsm_timeout, f, f->timeouttime);
598         break;
599     }
600
601     fsm_sdata(f, TERMACK, id, NULL, 0);
602 }
603
604
605 /*
606  * fsm_rtermack - Receive Terminate-Ack.
607  */
608 static void
609 fsm_rtermack(f)
610     fsm *f;
611 {
612     switch (f->state) {
613     case CLOSING:
614         UNTIMEOUT(fsm_timeout, f);
615         f->state = CLOSED;
616         if( f->callbacks->finished )
617             (*f->callbacks->finished)(f);
618         break;
619     case STOPPING:
620         UNTIMEOUT(fsm_timeout, f);
621         f->state = STOPPED;
622         if( f->callbacks->finished )
623             (*f->callbacks->finished)(f);
624         break;
625
626     case ACKRCVD:
627         f->state = REQSENT;
628         break;
629
630     case OPENED:
631         if (f->callbacks->down)
632             (*f->callbacks->down)(f);   /* Inform upper layers */
633         fsm_sconfreq(f, 0);
634         break;
635     }
636 }
637
638
639 /*
640  * fsm_rcoderej - Receive an Code-Reject.
641  */
642 static void
643 fsm_rcoderej(f, inp, len)
644     fsm *f;
645     u_char *inp;
646     int len;
647 {
648     u_char code, id;
649
650     if (len < HEADERLEN) {
651         FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
652         return;
653     }
654     GETCHAR(code, inp);
655     GETCHAR(id, inp);
656     warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
657
658     if( f->state == ACKRCVD )
659         f->state = REQSENT;
660 }
661
662
663 /*
664  * fsm_protreject - Peer doesn't speak this protocol.
665  *
666  * Treat this as a catastrophic error (RXJ-).
667  */
668 void
669 fsm_protreject(f)
670     fsm *f;
671 {
672     switch( f->state ){
673     case CLOSING:
674         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
675         /* fall through */
676     case CLOSED:
677         f->state = CLOSED;
678         if( f->callbacks->finished )
679             (*f->callbacks->finished)(f);
680         break;
681
682     case STOPPING:
683     case REQSENT:
684     case ACKRCVD:
685     case ACKSENT:
686         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
687         /* fall through */
688     case STOPPED:
689         f->state = STOPPED;
690         if( f->callbacks->finished )
691             (*f->callbacks->finished)(f);
692         break;
693
694     case OPENED:
695         if( f->callbacks->down )
696             (*f->callbacks->down)(f);
697
698         /* Init restart counter, send Terminate-Request */
699         f->retransmits = f->maxtermtransmits;
700         fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
701                   (u_char *) f->term_reason, f->term_reason_len);
702         TIMEOUT(fsm_timeout, f, f->timeouttime);
703         --f->retransmits;
704
705         f->state = STOPPING;
706         break;
707
708     default:
709         FSMDEBUG(("%s: Protocol-reject event in state %d!",
710                   PROTO_NAME(f), f->state));
711     }
712 }
713
714
715 /*
716  * fsm_sconfreq - Send a Configure-Request.
717  */
718 static void
719 fsm_sconfreq(f, retransmit)
720     fsm *f;
721     int retransmit;
722 {
723     u_char *outp;
724     int cilen;
725
726     if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
727         /* Not currently negotiating - reset options */
728         if( f->callbacks->resetci )
729             (*f->callbacks->resetci)(f);
730         f->nakloops = 0;
731     }
732
733     if( !retransmit ){
734         /* New request - reset retransmission counter, use new ID */
735         f->retransmits = f->maxconfreqtransmits;
736         f->reqid = ++f->id;
737     }
738
739     f->seen_ack = 0;
740
741     /*
742      * Make up the request packet
743      */
744     outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
745     if( f->callbacks->cilen && f->callbacks->addci ){
746         cilen = (*f->callbacks->cilen)(f);
747                 if( cilen > peer_mru[f->unit] - HEADERLEN ){
748             cilen = peer_mru[f->unit] - HEADERLEN;
749                 }    
750                 
751                 if (f->callbacks->addci){
752             (*f->callbacks->addci)(f, outp, &cilen);
753                 }    
754     } else
755         cilen = 0;
756
757     /* send the request to our peer */
758     fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
759
760     /* start the retransmit timer */
761     --f->retransmits;
762     TIMEOUT(fsm_timeout, f, f->timeouttime);
763 }
764
765
766 /*
767  * fsm_sdata - Send some data.
768  *
769  * Used for all packets sent to our peer by this module.
770  */
771 void
772 fsm_sdata(f, code, id, data, datalen)
773     fsm *f;
774     u_char code, id;
775     u_char *data;
776     int datalen;
777 {
778     u_char *outp;
779     int outlen;
780
781     /* Adjust length to be smaller than MTU */
782     outp = outpacket_buf;
783     if (datalen > peer_mru[f->unit] - HEADERLEN)
784         datalen = peer_mru[f->unit] - HEADERLEN;
785     if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
786         BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
787     outlen = datalen + HEADERLEN;
788     MAKEHEADER(outp, f->protocol);
789     PUTCHAR(code, outp);
790     PUTCHAR(id, outp);
791     PUTSHORT(outlen, outp);
792     output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
793 }