source: rtems/cpukit/pppd/fsm.c @ 24312f34

5
Last change on this file since 24312f34 was 33a1a4db, checked in by Peng Fan <van.freenix@…>, on 04/05/16 at 12:45:55

cpukit: pppd: fix compile warning

rcsid is defined, but not used. So discard it.

Signed-off-by: Peng Fan <van.freenix@…>

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