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