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