source: rtems/c/src/libnetworking/pppd/fsm.c @ 644a3a4

Last change on this file since 644a3a4 was 644a3a4, checked in by Ralf Corsepius <ralf.corsepius@…>, on 05/16/03 at 13:13:17

2003-05-16 Ralf Corsepius <corsepiu@…>

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