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