]> git.ozlabs.org Git - ppp.git/blob - pppd/fsm.c
Fixed bad next-state in previous delta; need to go to Stopping state
[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.22 2004/02/02 03:57:19 carlsonj 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  * terminate_layer - Start process of shutting down the FSM
206  *
207  * Cancel any timeout running, notify upper layers we're done, and
208  * send a terminate-request message as configured.
209  */
210 static void
211 terminate_layer(f, nextstate)
212     fsm *f;
213     int nextstate;
214 {
215     if( f->state != OPENED )
216         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
217     else if( f->callbacks->down )
218         (*f->callbacks->down)(f);       /* Inform upper layers we're down */
219
220     /* Init restart counter and send Terminate-Request */
221     f->retransmits = f->maxtermtransmits;
222     fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
223               (u_char *) f->term_reason, f->term_reason_len);
224
225     if (f->retransmits == 0) {
226         /*
227          * User asked for no terminate requests at all; just close it.
228          * We've already fired off one Terminate-Request just to be nice
229          * to the peer, but we're not going to wait for a reply.
230          */
231         f->state = nextstate == CLOSING ? CLOSED : STOPPED;
232         if( f->callbacks->finished )
233             (*f->callbacks->finished)(f);
234         return;
235     }
236
237     TIMEOUT(fsm_timeout, f, f->timeouttime);
238     --f->retransmits;
239
240     f->state = nextstate;
241 }
242
243 /*
244  * fsm_close - Start closing connection.
245  *
246  * Cancel timeouts and either initiate close or possibly go directly to
247  * the CLOSED state.
248  */
249 void
250 fsm_close(f, reason)
251     fsm *f;
252     char *reason;
253 {
254     f->term_reason = reason;
255     f->term_reason_len = (reason == NULL? 0: strlen(reason));
256     switch( f->state ){
257     case STARTING:
258         f->state = INITIAL;
259         break;
260     case STOPPED:
261         f->state = CLOSED;
262         break;
263     case STOPPING:
264         f->state = CLOSING;
265         break;
266
267     case REQSENT:
268     case ACKRCVD:
269     case ACKSENT:
270     case OPENED:
271         terminate_layer(f, CLOSING);
272         break;
273     }
274 }
275
276
277 /*
278  * fsm_timeout - Timeout expired.
279  */
280 static void
281 fsm_timeout(arg)
282     void *arg;
283 {
284     fsm *f = (fsm *) arg;
285
286     switch (f->state) {
287     case CLOSING:
288     case STOPPING:
289         if( f->retransmits <= 0 ){
290             /*
291              * We've waited for an ack long enough.  Peer probably heard us.
292              */
293             f->state = (f->state == CLOSING)? CLOSED: STOPPED;
294             if( f->callbacks->finished )
295                 (*f->callbacks->finished)(f);
296         } else {
297             /* Send Terminate-Request */
298             fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
299                       (u_char *) f->term_reason, f->term_reason_len);
300             TIMEOUT(fsm_timeout, f, f->timeouttime);
301             --f->retransmits;
302         }
303         break;
304
305     case REQSENT:
306     case ACKRCVD:
307     case ACKSENT:
308         if (f->retransmits <= 0) {
309             warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f));
310             f->state = STOPPED;
311             if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
312                 (*f->callbacks->finished)(f);
313
314         } else {
315             /* Retransmit the configure-request */
316             if (f->callbacks->retransmit)
317                 (*f->callbacks->retransmit)(f);
318             fsm_sconfreq(f, 1);         /* Re-send Configure-Request */
319             if( f->state == ACKRCVD )
320                 f->state = REQSENT;
321         }
322         break;
323
324     default:
325         FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
326     }
327 }
328
329
330 /*
331  * fsm_input - Input packet.
332  */
333 void
334 fsm_input(f, inpacket, l)
335     fsm *f;
336     u_char *inpacket;
337     int l;
338 {
339     u_char *inp;
340     u_char code, id;
341     int len;
342
343     /*
344      * Parse header (code, id and length).
345      * If packet too short, drop it.
346      */
347     inp = inpacket;
348     if (l < HEADERLEN) {
349         FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
350         return;
351     }
352     GETCHAR(code, inp);
353     GETCHAR(id, inp);
354     GETSHORT(len, inp);
355     if (len < HEADERLEN) {
356         FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
357         return;
358     }
359     if (len > l) {
360         FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
361         return;
362     }
363     len -= HEADERLEN;           /* subtract header length */
364
365     if( f->state == INITIAL || f->state == STARTING ){
366         FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
367                   f->protocol, f->state));
368         return;
369     }
370
371     /*
372      * Action depends on code.
373      */
374     switch (code) {
375     case CONFREQ:
376         fsm_rconfreq(f, id, inp, len);
377         break;
378     
379     case CONFACK:
380         fsm_rconfack(f, id, inp, len);
381         break;
382     
383     case CONFNAK:
384     case CONFREJ:
385         fsm_rconfnakrej(f, code, id, inp, len);
386         break;
387     
388     case TERMREQ:
389         fsm_rtermreq(f, id, inp, len);
390         break;
391     
392     case TERMACK:
393         fsm_rtermack(f);
394         break;
395     
396     case CODEREJ:
397         fsm_rcoderej(f, inp, len);
398         break;
399     
400     default:
401         if( !f->callbacks->extcode
402            || !(*f->callbacks->extcode)(f, code, id, inp, len) )
403             fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
404         break;
405     }
406 }
407
408
409 /*
410  * fsm_rconfreq - Receive Configure-Request.
411  */
412 static void
413 fsm_rconfreq(f, id, inp, len)
414     fsm *f;
415     u_char id;
416     u_char *inp;
417     int len;
418 {
419     int code, reject_if_disagree;
420
421     switch( f->state ){
422     case CLOSED:
423         /* Go away, we're closed */
424         fsm_sdata(f, TERMACK, id, NULL, 0);
425         return;
426     case CLOSING:
427     case STOPPING:
428         return;
429
430     case OPENED:
431         /* Go down and restart negotiation */
432         if( f->callbacks->down )
433             (*f->callbacks->down)(f);   /* Inform upper layers */
434         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
435         f->state = REQSENT;
436         break;
437
438     case STOPPED:
439         /* Negotiation started by our peer */
440         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
441         f->state = REQSENT;
442         break;
443     }
444
445     /*
446      * Pass the requested configuration options
447      * to protocol-specific code for checking.
448      */
449     if (f->callbacks->reqci){           /* Check CI */
450         reject_if_disagree = (f->nakloops >= f->maxnakloops);
451         code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
452     } else if (len)
453         code = CONFREJ;                 /* Reject all CI */
454     else
455         code = CONFACK;
456
457     /* send the Ack, Nak or Rej to the peer */
458     fsm_sdata(f, code, id, inp, len);
459
460     if (code == CONFACK) {
461         if (f->state == ACKRCVD) {
462             UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
463             f->state = OPENED;
464             if (f->callbacks->up)
465                 (*f->callbacks->up)(f); /* Inform upper layers */
466         } else
467             f->state = ACKSENT;
468         f->nakloops = 0;
469
470     } else {
471         /* we sent CONFACK or CONFREJ */
472         if (f->state != ACKRCVD)
473             f->state = REQSENT;
474         if( code == CONFNAK )
475             ++f->nakloops;
476     }
477 }
478
479
480 /*
481  * fsm_rconfack - Receive Configure-Ack.
482  */
483 static void
484 fsm_rconfack(f, id, inp, len)
485     fsm *f;
486     int id;
487     u_char *inp;
488     int len;
489 {
490     if (id != f->reqid || f->seen_ack)          /* Expected id? */
491         return;                                 /* Nope, toss... */
492     if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
493           (len == 0)) ){
494         /* Ack is bad - ignore it */
495         error("Received bad configure-ack: %P", inp, len);
496         return;
497     }
498     f->seen_ack = 1;
499
500     switch (f->state) {
501     case CLOSED:
502     case STOPPED:
503         fsm_sdata(f, TERMACK, id, NULL, 0);
504         break;
505
506     case REQSENT:
507         f->state = ACKRCVD;
508         f->retransmits = f->maxconfreqtransmits;
509         break;
510
511     case ACKRCVD:
512         /* Huh? an extra valid Ack? oh well... */
513         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
514         fsm_sconfreq(f, 0);
515         f->state = REQSENT;
516         break;
517
518     case ACKSENT:
519         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
520         f->state = OPENED;
521         f->retransmits = f->maxconfreqtransmits;
522         if (f->callbacks->up)
523             (*f->callbacks->up)(f);     /* Inform upper layers */
524         break;
525
526     case OPENED:
527         /* Go down and restart negotiation */
528         if (f->callbacks->down)
529             (*f->callbacks->down)(f);   /* Inform upper layers */
530         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
531         f->state = REQSENT;
532         break;
533     }
534 }
535
536
537 /*
538  * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
539  */
540 static void
541 fsm_rconfnakrej(f, code, id, inp, len)
542     fsm *f;
543     int code, id;
544     u_char *inp;
545     int len;
546 {
547     int (*proc) __P((fsm *, u_char *, int));
548     int ret;
549
550     if (id != f->reqid || f->seen_ack)  /* Expected id? */
551         return;                         /* Nope, toss... */
552     proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
553     if (!proc || !(ret = proc(f, inp, len))) {
554         /* Nak/reject is bad - ignore it */
555         error("Received bad configure-nak/rej: %P", inp, len);
556         return;
557     }
558     f->seen_ack = 1;
559
560     switch (f->state) {
561     case CLOSED:
562     case STOPPED:
563         fsm_sdata(f, TERMACK, id, NULL, 0);
564         break;
565
566     case REQSENT:
567     case ACKSENT:
568         /* They didn't agree to what we wanted - try another request */
569         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
570         if (ret < 0)
571             f->state = STOPPED;         /* kludge for stopping CCP */
572         else
573             fsm_sconfreq(f, 0);         /* Send Configure-Request */
574         break;
575
576     case ACKRCVD:
577         /* Got a Nak/reject when we had already had an Ack?? oh well... */
578         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
579         fsm_sconfreq(f, 0);
580         f->state = REQSENT;
581         break;
582
583     case OPENED:
584         /* Go down and restart negotiation */
585         if (f->callbacks->down)
586             (*f->callbacks->down)(f);   /* Inform upper layers */
587         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
588         f->state = REQSENT;
589         break;
590     }
591 }
592
593
594 /*
595  * fsm_rtermreq - Receive Terminate-Req.
596  */
597 static void
598 fsm_rtermreq(f, id, p, len)
599     fsm *f;
600     int id;
601     u_char *p;
602     int len;
603 {
604     switch (f->state) {
605     case ACKRCVD:
606     case ACKSENT:
607         f->state = REQSENT;             /* Start over but keep trying */
608         break;
609
610     case OPENED:
611         if (len > 0) {
612             info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
613         } else
614             info("%s terminated by peer", PROTO_NAME(f));
615         f->retransmits = 0;
616         f->state = STOPPING;
617         if (f->callbacks->down)
618             (*f->callbacks->down)(f);   /* Inform upper layers */
619         TIMEOUT(fsm_timeout, f, f->timeouttime);
620         break;
621     }
622
623     fsm_sdata(f, TERMACK, id, NULL, 0);
624 }
625
626
627 /*
628  * fsm_rtermack - Receive Terminate-Ack.
629  */
630 static void
631 fsm_rtermack(f)
632     fsm *f;
633 {
634     switch (f->state) {
635     case CLOSING:
636         UNTIMEOUT(fsm_timeout, f);
637         f->state = CLOSED;
638         if( f->callbacks->finished )
639             (*f->callbacks->finished)(f);
640         break;
641     case STOPPING:
642         UNTIMEOUT(fsm_timeout, f);
643         f->state = STOPPED;
644         if( f->callbacks->finished )
645             (*f->callbacks->finished)(f);
646         break;
647
648     case ACKRCVD:
649         f->state = REQSENT;
650         break;
651
652     case OPENED:
653         if (f->callbacks->down)
654             (*f->callbacks->down)(f);   /* Inform upper layers */
655         fsm_sconfreq(f, 0);
656         f->state = REQSENT;
657         break;
658     }
659 }
660
661
662 /*
663  * fsm_rcoderej - Receive an Code-Reject.
664  */
665 static void
666 fsm_rcoderej(f, inp, len)
667     fsm *f;
668     u_char *inp;
669     int len;
670 {
671     u_char code, id;
672
673     if (len < HEADERLEN) {
674         FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
675         return;
676     }
677     GETCHAR(code, inp);
678     GETCHAR(id, inp);
679     warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
680
681     if( f->state == ACKRCVD )
682         f->state = REQSENT;
683 }
684
685
686 /*
687  * fsm_protreject - Peer doesn't speak this protocol.
688  *
689  * Treat this as a catastrophic error (RXJ-).
690  */
691 void
692 fsm_protreject(f)
693     fsm *f;
694 {
695     switch( f->state ){
696     case CLOSING:
697         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
698         /* fall through */
699     case CLOSED:
700         f->state = CLOSED;
701         if( f->callbacks->finished )
702             (*f->callbacks->finished)(f);
703         break;
704
705     case STOPPING:
706     case REQSENT:
707     case ACKRCVD:
708     case ACKSENT:
709         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
710         /* fall through */
711     case STOPPED:
712         f->state = STOPPED;
713         if( f->callbacks->finished )
714             (*f->callbacks->finished)(f);
715         break;
716
717     case OPENED:
718         terminate_layer(f, STOPPING);
719         break;
720
721     default:
722         FSMDEBUG(("%s: Protocol-reject event in state %d!",
723                   PROTO_NAME(f), f->state));
724     }
725 }
726
727
728 /*
729  * fsm_sconfreq - Send a Configure-Request.
730  */
731 static void
732 fsm_sconfreq(f, retransmit)
733     fsm *f;
734     int retransmit;
735 {
736     u_char *outp;
737     int cilen;
738
739     if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
740         /* Not currently negotiating - reset options */
741         if( f->callbacks->resetci )
742             (*f->callbacks->resetci)(f);
743         f->nakloops = 0;
744     }
745
746     if( !retransmit ){
747         /* New request - reset retransmission counter, use new ID */
748         f->retransmits = f->maxconfreqtransmits;
749         f->reqid = ++f->id;
750     }
751
752     f->seen_ack = 0;
753
754     /*
755      * Make up the request packet
756      */
757     outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
758     if( f->callbacks->cilen && f->callbacks->addci ){
759         cilen = (*f->callbacks->cilen)(f);
760         if( cilen > peer_mru[f->unit] - HEADERLEN )
761             cilen = peer_mru[f->unit] - HEADERLEN;
762         if (f->callbacks->addci)
763             (*f->callbacks->addci)(f, outp, &cilen);
764     } else
765         cilen = 0;
766
767     /* send the request to our peer */
768     fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
769
770     /* start the retransmit timer */
771     --f->retransmits;
772     TIMEOUT(fsm_timeout, f, f->timeouttime);
773 }
774
775
776 /*
777  * fsm_sdata - Send some data.
778  *
779  * Used for all packets sent to our peer by this module.
780  */
781 void
782 fsm_sdata(f, code, id, data, datalen)
783     fsm *f;
784     u_char code, id;
785     u_char *data;
786     int datalen;
787 {
788     u_char *outp;
789     int outlen;
790
791     /* Adjust length to be smaller than MTU */
792     outp = outpacket_buf;
793     if (datalen > peer_mru[f->unit] - HEADERLEN)
794         datalen = peer_mru[f->unit] - HEADERLEN;
795     if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
796         BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
797     outlen = datalen + HEADERLEN;
798     MAKEHEADER(outp, f->protocol);
799     PUTCHAR(code, outp);
800     PUTCHAR(id, outp);
801     PUTSHORT(outlen, outp);
802     output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
803 }