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