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