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