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