Home | History | Annotate | Download | only in ppp
      1 /*****************************************************************************
      2 * fsm.c - Network Control Protocol Finite State Machine program file.
      3 *
      4 * Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
      5 * portions Copyright (c) 1997 by Global Election Systems Inc.
      6 *
      7 * The authors hereby grant permission to use, copy, modify, distribute,
      8 * and license this software and its documentation for any purpose, provided
      9 * that existing copyright notices are retained in all copies and that this
     10 * notice and the following disclaimer are included verbatim in any
     11 * distributions. No written agreement, license, or royalty fee is required
     12 * for any of the authorized uses.
     13 *
     14 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
     15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     17 * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24 *
     25 ******************************************************************************
     26 * REVISION HISTORY
     27 *
     28 * 03-01-01 Marc Boucher <marc (at) mbsi.ca>
     29 *   Ported to lwIP.
     30 * 97-12-01 Guy Lancaster <lancasterg (at) acm.org>, Global Election Systems Inc.
     31 *   Original based on BSD fsm.c.
     32 *****************************************************************************/
     33 /*
     34  * fsm.c - {Link, IP} Control Protocol Finite State Machine.
     35  *
     36  * Copyright (c) 1989 Carnegie Mellon University.
     37  * All rights reserved.
     38  *
     39  * Redistribution and use in source and binary forms are permitted
     40  * provided that the above copyright notice and this paragraph are
     41  * duplicated in all such forms and that any documentation,
     42  * advertising materials, and other materials related to such
     43  * distribution and use acknowledge that the software was developed
     44  * by Carnegie Mellon University.  The name of the
     45  * University may not be used to endorse or promote products derived
     46  * from this software without specific prior written permission.
     47  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     48  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     49  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     50  */
     51 
     52 /*
     53  * TODO:
     54  * Randomize fsm id on link/init.
     55  * Deal with variable outgoing MTU.
     56  */
     57 
     58 #include "lwip/opt.h"
     59 
     60 #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
     61 
     62 #include "ppp.h"
     63 #include "pppdebug.h"
     64 
     65 #include "fsm.h"
     66 
     67 #include <string.h>
     68 
     69 #if PPP_DEBUG
     70 static const char *ppperr_strerr[] = {
     71            "LS_INITIAL",  /* LS_INITIAL  0 */
     72            "LS_STARTING", /* LS_STARTING 1 */
     73            "LS_CLOSED",   /* LS_CLOSED   2 */
     74            "LS_STOPPED",  /* LS_STOPPED  3 */
     75            "LS_CLOSING",  /* LS_CLOSING  4 */
     76            "LS_STOPPING", /* LS_STOPPING 5 */
     77            "LS_REQSENT",  /* LS_REQSENT  6 */
     78            "LS_ACKRCVD",  /* LS_ACKRCVD  7 */
     79            "LS_ACKSENT",  /* LS_ACKSENT  8 */
     80            "LS_OPENED"    /* LS_OPENED   9 */
     81 };
     82 #endif /* PPP_DEBUG */
     83 
     84 static void fsm_timeout (void *);
     85 static void fsm_rconfreq (fsm *, u_char, u_char *, int);
     86 static void fsm_rconfack (fsm *, int, u_char *, int);
     87 static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
     88 static void fsm_rtermreq (fsm *, int, u_char *, int);
     89 static void fsm_rtermack (fsm *);
     90 static void fsm_rcoderej (fsm *, u_char *, int);
     91 static void fsm_sconfreq (fsm *, int);
     92 
     93 #define PROTO_NAME(f) ((f)->callbacks->proto_name)
     94 
     95 int peer_mru[NUM_PPP];
     96 
     97 
     98 /*
     99  * fsm_init - Initialize fsm.
    100  *
    101  * Initialize fsm state.
    102  */
    103 void
    104 fsm_init(fsm *f)
    105 {
    106   f->state = LS_INITIAL;
    107   f->flags = 0;
    108   f->id = 0;        /* XXX Start with random id? */
    109   f->timeouttime = FSM_DEFTIMEOUT;
    110   f->maxconfreqtransmits = FSM_DEFMAXCONFREQS;
    111   f->maxtermtransmits = FSM_DEFMAXTERMREQS;
    112   f->maxnakloops = FSM_DEFMAXNAKLOOPS;
    113   f->term_reason_len = 0;
    114 }
    115 
    116 
    117 /*
    118  * fsm_lowerup - The lower layer is up.
    119  */
    120 void
    121 fsm_lowerup(fsm *f)
    122 {
    123   int oldState = f->state;
    124 
    125   LWIP_UNUSED_ARG(oldState);
    126 
    127   switch( f->state ) {
    128     case LS_INITIAL:
    129       f->state = LS_CLOSED;
    130       break;
    131 
    132     case LS_STARTING:
    133       if( f->flags & OPT_SILENT ) {
    134         f->state = LS_STOPPED;
    135       } else {
    136         /* Send an initial configure-request */
    137         fsm_sconfreq(f, 0);
    138         f->state = LS_REQSENT;
    139       }
    140     break;
    141 
    142     default:
    143       FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n",
    144           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    145   }
    146 
    147   FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n",
    148       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
    149 }
    150 
    151 
    152 /*
    153  * fsm_lowerdown - The lower layer is down.
    154  *
    155  * Cancel all timeouts and inform upper layers.
    156  */
    157 void
    158 fsm_lowerdown(fsm *f)
    159 {
    160   int oldState = f->state;
    161 
    162   LWIP_UNUSED_ARG(oldState);
    163 
    164   switch( f->state ) {
    165     case LS_CLOSED:
    166       f->state = LS_INITIAL;
    167       break;
    168 
    169     case LS_STOPPED:
    170       f->state = LS_STARTING;
    171       if( f->callbacks->starting ) {
    172         (*f->callbacks->starting)(f);
    173       }
    174       break;
    175 
    176     case LS_CLOSING:
    177       f->state = LS_INITIAL;
    178       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    179       break;
    180 
    181     case LS_STOPPING:
    182     case LS_REQSENT:
    183     case LS_ACKRCVD:
    184     case LS_ACKSENT:
    185       f->state = LS_STARTING;
    186       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    187       break;
    188 
    189     case LS_OPENED:
    190       if( f->callbacks->down ) {
    191         (*f->callbacks->down)(f);
    192       }
    193       f->state = LS_STARTING;
    194       break;
    195 
    196     default:
    197       FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n",
    198           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    199   }
    200 
    201   FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n",
    202       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
    203 }
    204 
    205 
    206 /*
    207  * fsm_open - Link is allowed to come up.
    208  */
    209 void
    210 fsm_open(fsm *f)
    211 {
    212   int oldState = f->state;
    213 
    214   LWIP_UNUSED_ARG(oldState);
    215 
    216   switch( f->state ) {
    217     case LS_INITIAL:
    218       f->state = LS_STARTING;
    219       if( f->callbacks->starting ) {
    220         (*f->callbacks->starting)(f);
    221       }
    222       break;
    223 
    224     case LS_CLOSED:
    225       if( f->flags & OPT_SILENT ) {
    226         f->state = LS_STOPPED;
    227       } else {
    228         /* Send an initial configure-request */
    229         fsm_sconfreq(f, 0);
    230         f->state = LS_REQSENT;
    231       }
    232       break;
    233 
    234     case LS_CLOSING:
    235       f->state = LS_STOPPING;
    236       /* fall through */
    237     case LS_STOPPED:
    238     case LS_OPENED:
    239       if( f->flags & OPT_RESTART ) {
    240         fsm_lowerdown(f);
    241         fsm_lowerup(f);
    242       }
    243       break;
    244   }
    245 
    246   FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n",
    247       PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
    248 }
    249 
    250 #if 0 /* backport pppd 2.4.4b1; */
    251 /*
    252  * terminate_layer - Start process of shutting down the FSM
    253  *
    254  * Cancel any timeout running, notify upper layers we're done, and
    255  * send a terminate-request message as configured.
    256  */
    257 static void
    258 terminate_layer(fsm *f, int nextstate)
    259 {
    260   /* @todo */
    261 }
    262 #endif
    263 
    264 /*
    265  * fsm_close - Start closing connection.
    266  *
    267  * Cancel timeouts and either initiate close or possibly go directly to
    268  * the LS_CLOSED state.
    269  */
    270 void
    271 fsm_close(fsm *f, char *reason)
    272 {
    273   int oldState = f->state;
    274 
    275   LWIP_UNUSED_ARG(oldState);
    276 
    277   f->term_reason = reason;
    278   f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason));
    279   switch( f->state ) {
    280     case LS_STARTING:
    281       f->state = LS_INITIAL;
    282       break;
    283     case LS_STOPPED:
    284       f->state = LS_CLOSED;
    285       break;
    286     case LS_STOPPING:
    287       f->state = LS_CLOSING;
    288       break;
    289 
    290     case LS_REQSENT:
    291     case LS_ACKRCVD:
    292     case LS_ACKSENT:
    293     case LS_OPENED:
    294       if( f->state != LS_OPENED ) {
    295         UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    296       } else if( f->callbacks->down ) {
    297         (*f->callbacks->down)(f);  /* Inform upper layers we're down */
    298       }
    299       /* Init restart counter, send Terminate-Request */
    300       f->retransmits = f->maxtermtransmits;
    301       fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
    302             (u_char *) f->term_reason, f->term_reason_len);
    303       TIMEOUT(fsm_timeout, f, f->timeouttime);
    304       --f->retransmits;
    305 
    306       f->state = LS_CLOSING;
    307       break;
    308   }
    309 
    310   FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n",
    311       PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
    312 }
    313 
    314 
    315 /*
    316  * fsm_timeout - Timeout expired.
    317  */
    318 static void
    319 fsm_timeout(void *arg)
    320 {
    321   fsm *f = (fsm *) arg;
    322 
    323   switch (f->state) {
    324     case LS_CLOSING:
    325     case LS_STOPPING:
    326       if( f->retransmits <= 0 ) {
    327         FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n",
    328              PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    329         /*
    330          * We've waited for an ack long enough.  Peer probably heard us.
    331          */
    332         f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED;
    333         if( f->callbacks->finished ) {
    334           (*f->callbacks->finished)(f);
    335         }
    336       } else {
    337         FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n",
    338              PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    339         /* Send Terminate-Request */
    340         fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
    341             (u_char *) f->term_reason, f->term_reason_len);
    342         TIMEOUT(fsm_timeout, f, f->timeouttime);
    343         --f->retransmits;
    344       }
    345       break;
    346 
    347     case LS_REQSENT:
    348     case LS_ACKRCVD:
    349     case LS_ACKSENT:
    350       if (f->retransmits <= 0) {
    351         FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n",
    352          PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    353         f->state = LS_STOPPED;
    354         if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) {
    355           (*f->callbacks->finished)(f);
    356         }
    357       } else {
    358         FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n",
    359          PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    360         /* Retransmit the configure-request */
    361         if (f->callbacks->retransmit) {
    362           (*f->callbacks->retransmit)(f);
    363         }
    364         fsm_sconfreq(f, 1);    /* Re-send Configure-Request */
    365         if( f->state == LS_ACKRCVD ) {
    366           f->state = LS_REQSENT;
    367         }
    368       }
    369       break;
    370 
    371     default:
    372       FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n",
    373           PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    374   }
    375 }
    376 
    377 
    378 /*
    379  * fsm_input - Input packet.
    380  */
    381 void
    382 fsm_input(fsm *f, u_char *inpacket, int l)
    383 {
    384   u_char *inp = inpacket;
    385   u_char code, id;
    386   int len;
    387 
    388   /*
    389   * Parse header (code, id and length).
    390   * If packet too short, drop it.
    391   */
    392   if (l < HEADERLEN) {
    393     FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n",
    394           f->protocol));
    395     return;
    396   }
    397   GETCHAR(code, inp);
    398   GETCHAR(id, inp);
    399   GETSHORT(len, inp);
    400   if (len < HEADERLEN) {
    401     FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n",
    402         f->protocol));
    403     return;
    404   }
    405   if (len > l) {
    406     FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n",
    407         f->protocol));
    408     return;
    409   }
    410   len -= HEADERLEN;    /* subtract header length */
    411 
    412   if( f->state == LS_INITIAL || f->state == LS_STARTING ) {
    413     FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n",
    414         f->protocol, f->state, ppperr_strerr[f->state]));
    415     return;
    416   }
    417   FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l));
    418   /*
    419    * Action depends on code.
    420    */
    421   switch (code) {
    422     case CONFREQ:
    423       fsm_rconfreq(f, id, inp, len);
    424       break;
    425 
    426     case CONFACK:
    427       fsm_rconfack(f, id, inp, len);
    428       break;
    429 
    430     case CONFNAK:
    431     case CONFREJ:
    432       fsm_rconfnakrej(f, code, id, inp, len);
    433       break;
    434 
    435     case TERMREQ:
    436       fsm_rtermreq(f, id, inp, len);
    437       break;
    438 
    439     case TERMACK:
    440       fsm_rtermack(f);
    441       break;
    442 
    443     case CODEREJ:
    444       fsm_rcoderej(f, inp, len);
    445       break;
    446 
    447     default:
    448       FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f)));
    449       if( !f->callbacks->extcode ||
    450           !(*f->callbacks->extcode)(f, code, id, inp, len) ) {
    451         fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
    452       }
    453       break;
    454   }
    455 }
    456 
    457 
    458 /*
    459  * fsm_rconfreq - Receive Configure-Request.
    460  */
    461 static void
    462 fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len)
    463 {
    464   int code, reject_if_disagree;
    465 
    466   FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n",
    467         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
    468   switch( f->state ) {
    469     case LS_CLOSED:
    470       /* Go away, we're closed */
    471       fsm_sdata(f, TERMACK, id, NULL, 0);
    472       return;
    473     case LS_CLOSING:
    474     case LS_STOPPING:
    475       return;
    476 
    477     case LS_OPENED:
    478       /* Go down and restart negotiation */
    479       if( f->callbacks->down ) {
    480         (*f->callbacks->down)(f);  /* Inform upper layers */
    481       }
    482       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
    483       break;
    484 
    485     case LS_STOPPED:
    486       /* Negotiation started by our peer */
    487       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
    488       f->state = LS_REQSENT;
    489       break;
    490   }
    491 
    492   /*
    493   * Pass the requested configuration options
    494   * to protocol-specific code for checking.
    495   */
    496   if (f->callbacks->reqci) {    /* Check CI */
    497     reject_if_disagree = (f->nakloops >= f->maxnakloops);
    498     code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
    499   } else if (len) {
    500     code = CONFREJ;      /* Reject all CI */
    501   } else {
    502     code = CONFACK;
    503   }
    504 
    505   /* send the Ack, Nak or Rej to the peer */
    506   fsm_sdata(f, (u_char)code, id, inp, len);
    507 
    508   if (code == CONFACK) {
    509     if (f->state == LS_ACKRCVD) {
    510       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    511       f->state = LS_OPENED;
    512       if (f->callbacks->up) {
    513         (*f->callbacks->up)(f);  /* Inform upper layers */
    514       }
    515     } else {
    516       f->state = LS_ACKSENT;
    517     }
    518     f->nakloops = 0;
    519   } else {
    520     /* we sent CONFACK or CONFREJ */
    521     if (f->state != LS_ACKRCVD) {
    522       f->state = LS_REQSENT;
    523     }
    524     if( code == CONFNAK ) {
    525       ++f->nakloops;
    526     }
    527   }
    528 }
    529 
    530 
    531 /*
    532  * fsm_rconfack - Receive Configure-Ack.
    533  */
    534 static void
    535 fsm_rconfack(fsm *f, int id, u_char *inp, int len)
    536 {
    537   FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n",
    538         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
    539 
    540   if (id != f->reqid || f->seen_ack) {   /* Expected id? */
    541     return; /* Nope, toss... */
    542   }
    543   if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) {
    544     /* Ack is bad - ignore it */
    545     FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n",
    546           PROTO_NAME(f), len));
    547     return;
    548   }
    549   f->seen_ack = 1;
    550 
    551   switch (f->state) {
    552     case LS_CLOSED:
    553     case LS_STOPPED:
    554       fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
    555       break;
    556 
    557     case LS_REQSENT:
    558       f->state = LS_ACKRCVD;
    559       f->retransmits = f->maxconfreqtransmits;
    560       break;
    561 
    562     case LS_ACKRCVD:
    563       /* Huh? an extra valid Ack? oh well... */
    564       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    565       fsm_sconfreq(f, 0);
    566       f->state = LS_REQSENT;
    567       break;
    568 
    569     case LS_ACKSENT:
    570       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    571       f->state = LS_OPENED;
    572       f->retransmits = f->maxconfreqtransmits;
    573       if (f->callbacks->up) {
    574         (*f->callbacks->up)(f);  /* Inform upper layers */
    575       }
    576       break;
    577 
    578     case LS_OPENED:
    579       /* Go down and restart negotiation */
    580       if (f->callbacks->down) {
    581         (*f->callbacks->down)(f);  /* Inform upper layers */
    582       }
    583       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
    584       f->state = LS_REQSENT;
    585       break;
    586   }
    587 }
    588 
    589 
    590 /*
    591  * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
    592  */
    593 static void
    594 fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
    595 {
    596   int (*proc) (fsm *, u_char *, int);
    597   int ret;
    598 
    599   FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n",
    600         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
    601 
    602   if (id != f->reqid || f->seen_ack) { /* Expected id? */
    603     return;        /* Nope, toss... */
    604   }
    605   proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
    606   if (!proc || !((ret = proc(f, inp, len)))) {
    607     /* Nak/reject is bad - ignore it */
    608     FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n",
    609           PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
    610     return;
    611   }
    612   f->seen_ack = 1;
    613 
    614   switch (f->state) {
    615     case LS_CLOSED:
    616     case LS_STOPPED:
    617       fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
    618       break;
    619 
    620     case LS_REQSENT:
    621     case LS_ACKSENT:
    622       /* They didn't agree to what we wanted - try another request */
    623       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    624       if (ret < 0) {
    625         f->state = LS_STOPPED;    /* kludge for stopping CCP */
    626       } else {
    627         fsm_sconfreq(f, 0);    /* Send Configure-Request */
    628       }
    629       break;
    630 
    631     case LS_ACKRCVD:
    632       /* Got a Nak/reject when we had already had an Ack?? oh well... */
    633       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    634       fsm_sconfreq(f, 0);
    635       f->state = LS_REQSENT;
    636       break;
    637 
    638     case LS_OPENED:
    639       /* Go down and restart negotiation */
    640       if (f->callbacks->down) {
    641         (*f->callbacks->down)(f);  /* Inform upper layers */
    642       }
    643       fsm_sconfreq(f, 0);    /* Send initial Configure-Request */
    644       f->state = LS_REQSENT;
    645       break;
    646   }
    647 }
    648 
    649 
    650 /*
    651  * fsm_rtermreq - Receive Terminate-Req.
    652  */
    653 static void
    654 fsm_rtermreq(fsm *f, int id, u_char *p, int len)
    655 {
    656   LWIP_UNUSED_ARG(p);
    657 
    658   FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n",
    659         PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
    660 
    661   switch (f->state) {
    662     case LS_ACKRCVD:
    663     case LS_ACKSENT:
    664       f->state = LS_REQSENT;    /* Start over but keep trying */
    665       break;
    666 
    667     case LS_OPENED:
    668       if (len > 0) {
    669         FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p));
    670       } else {
    671         FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f)));
    672       }
    673       if (f->callbacks->down) {
    674         (*f->callbacks->down)(f);  /* Inform upper layers */
    675       }
    676       f->retransmits = 0;
    677       f->state = LS_STOPPING;
    678       TIMEOUT(fsm_timeout, f, f->timeouttime);
    679       break;
    680   }
    681 
    682   fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
    683 }
    684 
    685 
    686 /*
    687  * fsm_rtermack - Receive Terminate-Ack.
    688  */
    689 static void
    690 fsm_rtermack(fsm *f)
    691 {
    692   FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n",
    693         PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    694 
    695   switch (f->state) {
    696     case LS_CLOSING:
    697       UNTIMEOUT(fsm_timeout, f);
    698       f->state = LS_CLOSED;
    699       if( f->callbacks->finished ) {
    700         (*f->callbacks->finished)(f);
    701       }
    702       break;
    703 
    704     case LS_STOPPING:
    705       UNTIMEOUT(fsm_timeout, f);
    706       f->state = LS_STOPPED;
    707       if( f->callbacks->finished ) {
    708         (*f->callbacks->finished)(f);
    709       }
    710       break;
    711 
    712     case LS_ACKRCVD:
    713       f->state = LS_REQSENT;
    714       break;
    715 
    716     case LS_OPENED:
    717       if (f->callbacks->down) {
    718         (*f->callbacks->down)(f);  /* Inform upper layers */
    719       }
    720       fsm_sconfreq(f, 0);
    721       break;
    722     default:
    723       FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n",
    724                 PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    725   }
    726 }
    727 
    728 
    729 /*
    730  * fsm_rcoderej - Receive an Code-Reject.
    731  */
    732 static void
    733 fsm_rcoderej(fsm *f, u_char *inp, int len)
    734 {
    735   u_char code, id;
    736 
    737   FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n",
    738         PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    739 
    740   if (len < HEADERLEN) {
    741     FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
    742     return;
    743   }
    744   GETCHAR(code, inp);
    745   GETCHAR(id, inp);
    746   FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n",
    747         PROTO_NAME(f), code, id));
    748 
    749   if( f->state == LS_ACKRCVD ) {
    750     f->state = LS_REQSENT;
    751   }
    752 }
    753 
    754 
    755 /*
    756  * fsm_protreject - Peer doesn't speak this protocol.
    757  *
    758  * Treat this as a catastrophic error (RXJ-).
    759  */
    760 void
    761 fsm_protreject(fsm *f)
    762 {
    763   switch( f->state ) {
    764     case LS_CLOSING:
    765       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    766       /* fall through */
    767     case LS_CLOSED:
    768       f->state = LS_CLOSED;
    769       if( f->callbacks->finished ) {
    770         (*f->callbacks->finished)(f);
    771       }
    772       break;
    773 
    774     case LS_STOPPING:
    775     case LS_REQSENT:
    776     case LS_ACKRCVD:
    777     case LS_ACKSENT:
    778       UNTIMEOUT(fsm_timeout, f);  /* Cancel timeout */
    779       /* fall through */
    780     case LS_STOPPED:
    781       f->state = LS_STOPPED;
    782       if( f->callbacks->finished ) {
    783         (*f->callbacks->finished)(f);
    784       }
    785       break;
    786 
    787     case LS_OPENED:
    788       if( f->callbacks->down ) {
    789         (*f->callbacks->down)(f);
    790       }
    791       /* Init restart counter, send Terminate-Request */
    792       f->retransmits = f->maxtermtransmits;
    793       fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
    794             (u_char *) f->term_reason, f->term_reason_len);
    795       TIMEOUT(fsm_timeout, f, f->timeouttime);
    796       --f->retransmits;
    797 
    798       f->state = LS_STOPPING;
    799       break;
    800 
    801     default:
    802       FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n",
    803             PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
    804     }
    805 }
    806 
    807 
    808 /*
    809  * fsm_sconfreq - Send a Configure-Request.
    810  */
    811 static void
    812 fsm_sconfreq(fsm *f, int retransmit)
    813 {
    814   u_char *outp;
    815   int cilen;
    816 
    817   if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) {
    818     /* Not currently negotiating - reset options */
    819     if( f->callbacks->resetci ) {
    820       (*f->callbacks->resetci)(f);
    821     }
    822     f->nakloops = 0;
    823   }
    824 
    825   if( !retransmit ) {
    826     /* New request - reset retransmission counter, use new ID */
    827     f->retransmits = f->maxconfreqtransmits;
    828     f->reqid = ++f->id;
    829   }
    830 
    831   f->seen_ack = 0;
    832 
    833   /*
    834    * Make up the request packet
    835    */
    836   outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN;
    837   if( f->callbacks->cilen && f->callbacks->addci ) {
    838     cilen = (*f->callbacks->cilen)(f);
    839     if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) {
    840       cilen = peer_mru[f->unit] - HEADERLEN;
    841     }
    842     if (f->callbacks->addci) {
    843       (*f->callbacks->addci)(f, outp, &cilen);
    844     }
    845   } else {
    846     cilen = 0;
    847   }
    848 
    849   /* send the request to our peer */
    850   fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
    851 
    852   /* start the retransmit timer */
    853   --f->retransmits;
    854   TIMEOUT(fsm_timeout, f, f->timeouttime);
    855 
    856   FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n",
    857         PROTO_NAME(f), f->reqid));
    858 }
    859 
    860 
    861 /*
    862  * fsm_sdata - Send some data.
    863  *
    864  * Used for all packets sent to our peer by this module.
    865  */
    866 void
    867 fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen)
    868 {
    869   u_char *outp;
    870   int outlen;
    871 
    872   /* Adjust length to be smaller than MTU */
    873   outp = outpacket_buf[f->unit];
    874   if (datalen > peer_mru[f->unit] - (int)HEADERLEN) {
    875     datalen = peer_mru[f->unit] - HEADERLEN;
    876   }
    877   if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) {
    878     BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
    879   }
    880   outlen = datalen + HEADERLEN;
    881   MAKEHEADER(outp, f->protocol);
    882   PUTCHAR(code, outp);
    883   PUTCHAR(id, outp);
    884   PUTSHORT(outlen, outp);
    885   pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN);
    886   FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n",
    887         PROTO_NAME(f), code, id, outlen));
    888 }
    889 
    890 #endif /* PPP_SUPPORT */
    891