]> git.ozlabs.org Git - ppp.git/blob - pppd/fsm.c
Change some Makefile.linux files to use COPTS and CFLAGS the same way
[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.20 2003/06/29 10:06:14 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         f->state = REQSENT;
410         break;
411
412     case STOPPED:
413         /* Negotiation started by our peer */
414         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
415         f->state = REQSENT;
416         break;
417     }
418
419     /*
420      * Pass the requested configuration options
421      * to protocol-specific code for checking.
422      */
423     if (f->callbacks->reqci){           /* Check CI */
424         reject_if_disagree = (f->nakloops >= f->maxnakloops);
425         code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
426     } else if (len)
427         code = CONFREJ;                 /* Reject all CI */
428     else
429         code = CONFACK;
430
431     /* send the Ack, Nak or Rej to the peer */
432     fsm_sdata(f, code, id, inp, len);
433
434     if (code == CONFACK) {
435         if (f->state == ACKRCVD) {
436             UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
437             f->state = OPENED;
438             if (f->callbacks->up)
439                 (*f->callbacks->up)(f); /* Inform upper layers */
440         } else
441             f->state = ACKSENT;
442         f->nakloops = 0;
443
444     } else {
445         /* we sent CONFACK or CONFREJ */
446         if (f->state != ACKRCVD)
447             f->state = REQSENT;
448         if( code == CONFNAK )
449             ++f->nakloops;
450     }
451 }
452
453
454 /*
455  * fsm_rconfack - Receive Configure-Ack.
456  */
457 static void
458 fsm_rconfack(f, id, inp, len)
459     fsm *f;
460     int id;
461     u_char *inp;
462     int len;
463 {
464     if (id != f->reqid || f->seen_ack)          /* Expected id? */
465         return;                                 /* Nope, toss... */
466     if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
467           (len == 0)) ){
468         /* Ack is bad - ignore it */
469         error("Received bad configure-ack: %P", inp, len);
470         return;
471     }
472     f->seen_ack = 1;
473
474     switch (f->state) {
475     case CLOSED:
476     case STOPPED:
477         fsm_sdata(f, TERMACK, id, NULL, 0);
478         break;
479
480     case REQSENT:
481         f->state = ACKRCVD;
482         f->retransmits = f->maxconfreqtransmits;
483         break;
484
485     case ACKRCVD:
486         /* Huh? an extra valid Ack? oh well... */
487         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
488         fsm_sconfreq(f, 0);
489         f->state = REQSENT;
490         break;
491
492     case ACKSENT:
493         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
494         f->state = OPENED;
495         f->retransmits = f->maxconfreqtransmits;
496         if (f->callbacks->up)
497             (*f->callbacks->up)(f);     /* Inform upper layers */
498         break;
499
500     case OPENED:
501         /* Go down and restart negotiation */
502         if (f->callbacks->down)
503             (*f->callbacks->down)(f);   /* Inform upper layers */
504         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
505         f->state = REQSENT;
506         break;
507     }
508 }
509
510
511 /*
512  * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
513  */
514 static void
515 fsm_rconfnakrej(f, code, id, inp, len)
516     fsm *f;
517     int code, id;
518     u_char *inp;
519     int len;
520 {
521     int (*proc) __P((fsm *, u_char *, int));
522     int ret;
523
524     if (id != f->reqid || f->seen_ack)  /* Expected id? */
525         return;                         /* Nope, toss... */
526     proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
527     if (!proc || !(ret = proc(f, inp, len))) {
528         /* Nak/reject is bad - ignore it */
529         error("Received bad configure-nak/rej: %P", inp, len);
530         return;
531     }
532     f->seen_ack = 1;
533
534     switch (f->state) {
535     case CLOSED:
536     case STOPPED:
537         fsm_sdata(f, TERMACK, id, NULL, 0);
538         break;
539
540     case REQSENT:
541     case ACKSENT:
542         /* They didn't agree to what we wanted - try another request */
543         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
544         if (ret < 0)
545             f->state = STOPPED;         /* kludge for stopping CCP */
546         else
547             fsm_sconfreq(f, 0);         /* Send Configure-Request */
548         break;
549
550     case ACKRCVD:
551         /* Got a Nak/reject when we had already had an Ack?? oh well... */
552         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
553         fsm_sconfreq(f, 0);
554         f->state = REQSENT;
555         break;
556
557     case OPENED:
558         /* Go down and restart negotiation */
559         if (f->callbacks->down)
560             (*f->callbacks->down)(f);   /* Inform upper layers */
561         fsm_sconfreq(f, 0);             /* Send initial Configure-Request */
562         f->state = REQSENT;
563         break;
564     }
565 }
566
567
568 /*
569  * fsm_rtermreq - Receive Terminate-Req.
570  */
571 static void
572 fsm_rtermreq(f, id, p, len)
573     fsm *f;
574     int id;
575     u_char *p;
576     int len;
577 {
578     switch (f->state) {
579     case ACKRCVD:
580     case ACKSENT:
581         f->state = REQSENT;             /* Start over but keep trying */
582         break;
583
584     case OPENED:
585         if (len > 0) {
586             info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
587         } else
588             info("%s terminated by peer", PROTO_NAME(f));
589         f->retransmits = 0;
590         f->state = STOPPING;
591         if (f->callbacks->down)
592             (*f->callbacks->down)(f);   /* Inform upper layers */
593         TIMEOUT(fsm_timeout, f, f->timeouttime);
594         break;
595     }
596
597     fsm_sdata(f, TERMACK, id, NULL, 0);
598 }
599
600
601 /*
602  * fsm_rtermack - Receive Terminate-Ack.
603  */
604 static void
605 fsm_rtermack(f)
606     fsm *f;
607 {
608     switch (f->state) {
609     case CLOSING:
610         UNTIMEOUT(fsm_timeout, f);
611         f->state = CLOSED;
612         if( f->callbacks->finished )
613             (*f->callbacks->finished)(f);
614         break;
615     case STOPPING:
616         UNTIMEOUT(fsm_timeout, f);
617         f->state = STOPPED;
618         if( f->callbacks->finished )
619             (*f->callbacks->finished)(f);
620         break;
621
622     case ACKRCVD:
623         f->state = REQSENT;
624         break;
625
626     case OPENED:
627         if (f->callbacks->down)
628             (*f->callbacks->down)(f);   /* Inform upper layers */
629         fsm_sconfreq(f, 0);
630         f->state = REQSENT;
631         break;
632     }
633 }
634
635
636 /*
637  * fsm_rcoderej - Receive an Code-Reject.
638  */
639 static void
640 fsm_rcoderej(f, inp, len)
641     fsm *f;
642     u_char *inp;
643     int len;
644 {
645     u_char code, id;
646
647     if (len < HEADERLEN) {
648         FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
649         return;
650     }
651     GETCHAR(code, inp);
652     GETCHAR(id, inp);
653     warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
654
655     if( f->state == ACKRCVD )
656         f->state = REQSENT;
657 }
658
659
660 /*
661  * fsm_protreject - Peer doesn't speak this protocol.
662  *
663  * Treat this as a catastrophic error (RXJ-).
664  */
665 void
666 fsm_protreject(f)
667     fsm *f;
668 {
669     switch( f->state ){
670     case CLOSING:
671         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
672         /* fall through */
673     case CLOSED:
674         f->state = CLOSED;
675         if( f->callbacks->finished )
676             (*f->callbacks->finished)(f);
677         break;
678
679     case STOPPING:
680     case REQSENT:
681     case ACKRCVD:
682     case ACKSENT:
683         UNTIMEOUT(fsm_timeout, f);      /* Cancel timeout */
684         /* fall through */
685     case STOPPED:
686         f->state = STOPPED;
687         if( f->callbacks->finished )
688             (*f->callbacks->finished)(f);
689         break;
690
691     case OPENED:
692         if( f->callbacks->down )
693             (*f->callbacks->down)(f);
694
695         /* Init restart counter, send Terminate-Request */
696         f->retransmits = f->maxtermtransmits;
697         fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
698                   (u_char *) f->term_reason, f->term_reason_len);
699         TIMEOUT(fsm_timeout, f, f->timeouttime);
700         --f->retransmits;
701
702         f->state = STOPPING;
703         break;
704
705     default:
706         FSMDEBUG(("%s: Protocol-reject event in state %d!",
707                   PROTO_NAME(f), f->state));
708     }
709 }
710
711
712 /*
713  * fsm_sconfreq - Send a Configure-Request.
714  */
715 static void
716 fsm_sconfreq(f, retransmit)
717     fsm *f;
718     int retransmit;
719 {
720     u_char *outp;
721     int cilen;
722
723     if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
724         /* Not currently negotiating - reset options */
725         if( f->callbacks->resetci )
726             (*f->callbacks->resetci)(f);
727         f->nakloops = 0;
728     }
729
730     if( !retransmit ){
731         /* New request - reset retransmission counter, use new ID */
732         f->retransmits = f->maxconfreqtransmits;
733         f->reqid = ++f->id;
734     }
735
736     f->seen_ack = 0;
737
738     /*
739      * Make up the request packet
740      */
741     outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
742     if( f->callbacks->cilen && f->callbacks->addci ){
743         cilen = (*f->callbacks->cilen)(f);
744         if( cilen > peer_mru[f->unit] - HEADERLEN )
745             cilen = peer_mru[f->unit] - HEADERLEN;
746         if (f->callbacks->addci)
747             (*f->callbacks->addci)(f, outp, &cilen);
748     } else
749         cilen = 0;
750
751     /* send the request to our peer */
752     fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
753
754     /* start the retransmit timer */
755     --f->retransmits;
756     TIMEOUT(fsm_timeout, f, f->timeouttime);
757 }
758
759
760 /*
761  * fsm_sdata - Send some data.
762  *
763  * Used for all packets sent to our peer by this module.
764  */
765 void
766 fsm_sdata(f, code, id, data, datalen)
767     fsm *f;
768     u_char code, id;
769     u_char *data;
770     int datalen;
771 {
772     u_char *outp;
773     int outlen;
774
775     /* Adjust length to be smaller than MTU */
776     outp = outpacket_buf;
777     if (datalen > peer_mru[f->unit] - HEADERLEN)
778         datalen = peer_mru[f->unit] - HEADERLEN;
779     if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
780         BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
781     outlen = datalen + HEADERLEN;
782     MAKEHEADER(outp, f->protocol);
783     PUTCHAR(code, outp);
784     PUTCHAR(id, outp);
785     PUTSHORT(outlen, outp);
786     output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
787 }