diff packages/net/ppp/current/src/fsm.c @ 1586:e0d88dc6a92f

Added PPP network support.
author nickg
date Wed, 14 Apr 2004 15:08:56 +0000
parents
children
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/net/ppp/current/src/fsm.c
@@ -0,0 +1,847 @@
+//==========================================================================
+//
+//      src/fsm.c
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Portions created by Nick Garnett are
+// Copyright (C) 2003 eCosCentric Ltd.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+//static char rcsid[] = "$FreeBSD: src/usr.sbin/pppd/fsm.c,v 1.8 1999/08/28 01:19:02 peter Exp $";
+#endif
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <cyg/ppp/syslog.h>
+
+#include "cyg/ppp/pppd.h"
+#include "cyg/ppp/fsm.h"
+
+static void fsm_timeout __P((void *));
+static void fsm_rconfreq __P((fsm *, int, u_char *, int));
+static void fsm_rconfack __P((fsm *, int, u_char *, int));
+static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int));
+static void fsm_rtermreq __P((fsm *, int, u_char *, int));
+static void fsm_rtermack __P((fsm *));
+static void fsm_rcoderej __P((fsm *, u_char *, int));
+static void fsm_sconfreq __P((fsm *, int));
+
+#define PROTO_NAME(f)	((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(f)
+    fsm *f;
+{
+    f->state = INITIAL;
+    f->flags = 0;
+    f->id = 0;				/* XXX Start with random id? */
+    f->timeouttime = DEFTIMEOUT;
+    f->maxconfreqtransmits = DEFMAXCONFREQS;
+    f->maxtermtransmits = DEFMAXTERMREQS;
+    f->maxnakloops = DEFMAXNAKLOOPS;
+    f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case INITIAL:
+	f->state = CLOSED;
+	break;
+
+    case STARTING:
+	if( f->flags & OPT_SILENT )
+	    f->state = STOPPED;
+	else {
+	    /* Send an initial configure-request */
+	    fsm_sconfreq(f, 0);
+	    f->state = REQSENT;
+	}
+	break;
+
+    default:
+	FSMDEBUG((LOG_INFO, "%s: Up event in state %d!",
+		  PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case CLOSED:
+	f->state = INITIAL;
+	break;
+
+    case STOPPED:
+	f->state = STARTING;
+	if( f->callbacks->starting )
+	    (*f->callbacks->starting)(f);
+	break;
+
+    case CLOSING:
+	f->state = INITIAL;
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	break;
+
+    case STOPPING:
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+	f->state = STARTING;
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	break;
+
+    case OPENED:
+	if( f->callbacks->down )
+	    (*f->callbacks->down)(f);
+	f->state = STARTING;
+	break;
+
+    default:
+	FSMDEBUG((LOG_INFO, "%s: Down event in state %d!",
+		  PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case INITIAL:
+	f->state = STARTING;
+	if( f->callbacks->starting )
+	    (*f->callbacks->starting)(f);
+	break;
+
+    case CLOSED:
+	if( f->flags & OPT_SILENT )
+	    f->state = STOPPED;
+	else {
+	    /* Send an initial configure-request */
+	    fsm_sconfreq(f, 0);
+	    f->state = REQSENT;
+	}
+	break;
+
+    case CLOSING:
+	f->state = STOPPING;
+	/* fall through */
+    case STOPPED:
+    case OPENED:
+	if( f->flags & OPT_RESTART ){
+	    fsm_lowerdown(f);
+	    fsm_lowerup(f);
+	}
+	break;
+    }
+}
+
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the CLOSED state.
+ */
+void
+fsm_close(f, reason)
+    fsm *f;
+    char *reason;
+{
+    f->term_reason = reason;
+    f->term_reason_len = (reason == NULL? 0: strlen(reason));
+    switch( f->state ){
+    case STARTING:
+	f->state = INITIAL;
+	break;
+    case STOPPED:
+	f->state = CLOSED;
+	break;
+    case STOPPING:
+	f->state = CLOSING;
+	break;
+
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+    case OPENED:
+	if( f->state != OPENED )
+	    UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	else if( f->callbacks->down )
+	    (*f->callbacks->down)(f);	/* Inform upper layers we're down */
+
+	/* Init restart counter, send Terminate-Request */
+	f->retransmits = f->maxtermtransmits;
+	fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+		  (u_char *) f->term_reason, f->term_reason_len);
+	TIMEOUT(fsm_timeout, f, f->timeouttime);
+	--f->retransmits;
+
+	f->state = CLOSING;
+	break;
+    }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(arg)
+    void *arg;
+{
+    fsm *f = (fsm *) arg;
+
+    switch (f->state) {
+    case CLOSING:
+    case STOPPING:
+	if( f->retransmits <= 0 ){
+	    /*
+	     * We've waited for an ack long enough.  Peer probably heard us.
+	     */
+	    f->state = (f->state == CLOSING)? CLOSED: STOPPED;
+	    if( f->callbacks->finished )
+		(*f->callbacks->finished)(f);
+	} else {
+	    /* Send Terminate-Request */
+	    fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+		      (u_char *) f->term_reason, f->term_reason_len);
+	    TIMEOUT(fsm_timeout, f, f->timeouttime);
+	    --f->retransmits;
+	}
+	break;
+
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+	if (f->retransmits <= 0) {
+	    syslog(LOG_WARNING, "%s: timeout sending Config-Requests",
+		   PROTO_NAME(f));
+	    f->state = STOPPED;
+	    if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
+		(*f->callbacks->finished)(f);
+
+	} else {
+	    /* Retransmit the configure-request */
+	    if (f->callbacks->retransmit)
+		(*f->callbacks->retransmit)(f);
+	    fsm_sconfreq(f, 1);		/* Re-send Configure-Request */
+	    if( f->state == ACKRCVD )
+		f->state = REQSENT;
+	}
+	break;
+
+    default:
+	FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!",
+		  PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(f, inpacket, l)
+    fsm *f;
+    u_char *inpacket;
+    int l;
+{
+    u_char *inp;
+    u_char code, id;
+    int len;
+
+    /*
+     * Parse header (code, id and length).
+     * If packet too short, drop it.
+     */
+    inp = inpacket;
+    if (l < HEADERLEN) {
+	FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.",
+		  f->protocol));
+	return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    GETSHORT(len, inp);
+    if (len < HEADERLEN) {
+	FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.",
+		  f->protocol));
+	return;
+    }
+    if (len > l) {
+	FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.",
+		  f->protocol));
+	return;
+    }
+    len -= HEADERLEN;		/* subtract header length */
+
+    if( f->state == INITIAL || f->state == STARTING ){
+	FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.",
+		  f->protocol, f->state));
+	return;
+    }
+
+    /*
+     * Action depends on code.
+     */
+    switch (code) {
+    case CONFREQ:
+	fsm_rconfreq(f, id, inp, len);
+	break;
+    
+    case CONFACK:
+	fsm_rconfack(f, id, inp, len);
+	break;
+    
+    case CONFNAK:
+    case CONFREJ:
+	fsm_rconfnakrej(f, code, id, inp, len);
+	break;
+    
+    case TERMREQ:
+	fsm_rtermreq(f, id, inp, len);
+	break;
+    
+    case TERMACK:
+	fsm_rtermack(f);
+	break;
+    
+    case CODEREJ:
+	fsm_rcoderej(f, inp, len);
+	break;
+    
+    default:
+	if( !f->callbacks->extcode
+	   || !(*f->callbacks->extcode)(f, code, id, inp, len) )
+	    fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+	break;
+    }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(f, id, inp, len)
+    fsm *f;
+    u_char id;
+    u_char *inp;
+    int len;
+{
+    int code, reject_if_disagree;
+
+    FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f), id));
+    switch( f->state ){
+    case CLOSED:
+	/* Go away, we're closed */
+	fsm_sdata(f, TERMACK, id, NULL, 0);
+	return;
+    case CLOSING:
+    case STOPPING:
+	return;
+
+    case OPENED:
+	/* Go down and restart negotiation */
+	if( f->callbacks->down )
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	break;
+
+    case STOPPED:
+	/* Negotiation started by our peer */
+	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	f->state = REQSENT;
+	break;
+    }
+
+    /*
+     * Pass the requested configuration options
+     * to protocol-specific code for checking.
+     */
+    if (f->callbacks->reqci){		/* Check CI */
+	reject_if_disagree = (f->nakloops >= f->maxnakloops);
+	code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+    } else if (len)
+	code = CONFREJ;			/* Reject all CI */
+    else
+	code = CONFACK;
+
+    /* send the Ack, Nak or Rej to the peer */
+    fsm_sdata(f, code, id, inp, len);
+
+    if (code == CONFACK) {
+	if (f->state == ACKRCVD) {
+	    UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	    f->state = OPENED;
+	    if (f->callbacks->up)
+		(*f->callbacks->up)(f);	/* Inform upper layers */
+	} else
+	    f->state = ACKSENT;
+	f->nakloops = 0;
+
+    } else {
+	/* we sent CONFACK or CONFREJ */
+	if (f->state != ACKRCVD)
+	    f->state = REQSENT;
+	if( code == CONFNAK )
+	    ++f->nakloops;
+    }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(f, id, inp, len)
+    fsm *f;
+    int id;
+    u_char *inp;
+    int len;
+{
+    FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d.",
+	      PROTO_NAME(f), id));
+
+    if (id != f->reqid || f->seen_ack)		/* Expected id? */
+	return;					/* Nope, toss... */
+    if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
+	  (len == 0)) ){
+	/* Ack is bad - ignore it */
+	log_packet(inp, len, "Received bad configure-ack: ", LOG_ERR);
+	FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)",
+		  PROTO_NAME(f), len));
+	return;
+    }
+    f->seen_ack = 1;
+
+    switch (f->state) {
+    case CLOSED:
+    case STOPPED:
+	fsm_sdata(f, TERMACK, id, NULL, 0);
+	break;
+
+    case REQSENT:
+	f->state = ACKRCVD;
+	f->retransmits = f->maxconfreqtransmits;
+	break;
+
+    case ACKRCVD:
+	/* Huh? an extra valid Ack? oh well... */
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	fsm_sconfreq(f, 0);
+	f->state = REQSENT;
+	break;
+
+    case ACKSENT:
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	f->state = OPENED;
+	f->retransmits = f->maxconfreqtransmits;
+	if (f->callbacks->up)
+	    (*f->callbacks->up)(f);	/* Inform upper layers */
+	break;
+
+    case OPENED:
+	/* Go down and restart negotiation */
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	f->state = REQSENT;
+	break;
+    }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(f, code, id, inp, len)
+    fsm *f;
+    int code, id;
+    u_char *inp;
+    int len;
+{
+    int (*proc) __P((fsm *, u_char *, int));
+    int ret;
+
+    FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d.",
+	      PROTO_NAME(f), id));
+
+    if (id != f->reqid || f->seen_ack)	/* Expected id? */
+	return;				/* Nope, toss... */
+    proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+    if (!proc || !(ret = proc(f, inp, len))) {
+	/* Nak/reject is bad - ignore it */
+	log_packet(inp, len, "Received bad configure-nak/rej: ", LOG_ERR);
+	FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)",
+		  PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
+	return;
+    }
+    f->seen_ack = 1;
+
+    switch (f->state) {
+    case CLOSED:
+    case STOPPED:
+	fsm_sdata(f, TERMACK, id, NULL, 0);
+	break;
+
+    case REQSENT:
+    case ACKSENT:
+	/* They didn't agree to what we wanted - try another request */
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	if (ret < 0)
+	    f->state = STOPPED;		/* kludge for stopping CCP */
+	else
+	    fsm_sconfreq(f, 0);		/* Send Configure-Request */
+	break;
+
+    case ACKRCVD:
+	/* Got a Nak/reject when we had already had an Ack?? oh well... */
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	fsm_sconfreq(f, 0);
+	f->state = REQSENT;
+	break;
+
+    case OPENED:
+	/* Go down and restart negotiation */
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	fsm_sconfreq(f, 0);		/* Send initial Configure-Request */
+	f->state = REQSENT;
+	break;
+    }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(f, id, p, len)
+    fsm *f;
+    int id;
+    u_char *p;
+    int len;
+{
+    char str[80];
+
+    FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d.",
+	      PROTO_NAME(f), id));
+
+    switch (f->state) {
+    case ACKRCVD:
+    case ACKSENT:
+	f->state = REQSENT;		/* Start over but keep trying */
+	break;
+
+    case OPENED:
+	if (len > 0) {
+	    fmtmsg(str, sizeof(str), "%0.*v", len, p);
+	    syslog(LOG_INFO, "%s terminated by peer (%s)", PROTO_NAME(f), str);
+	} else
+	    syslog(LOG_INFO, "%s terminated by peer", PROTO_NAME(f));
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	f->retransmits = 0;
+	f->state = STOPPING;
+	TIMEOUT(fsm_timeout, f, f->timeouttime);
+	break;
+    }
+
+    fsm_sdata(f, TERMACK, id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(f)
+    fsm *f;
+{
+    FSMDEBUG((LOG_INFO, "fsm_rtermack(%s).", PROTO_NAME(f)));
+
+    switch (f->state) {
+    case CLOSING:
+	UNTIMEOUT(fsm_timeout, f);
+	f->state = CLOSED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	break;
+    case STOPPING:
+	UNTIMEOUT(fsm_timeout, f);
+	f->state = STOPPED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	break;
+
+    case ACKRCVD:
+	f->state = REQSENT;
+	break;
+
+    case OPENED:
+	if (f->callbacks->down)
+	    (*f->callbacks->down)(f);	/* Inform upper layers */
+	fsm_sconfreq(f, 0);
+	break;
+    }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void
+fsm_rcoderej(f, inp, len)
+    fsm *f;
+    u_char *inp;
+    int len;
+{
+    u_char code, id;
+
+    FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s).", PROTO_NAME(f)));
+
+    if (len < HEADERLEN) {
+	FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!"));
+	return;
+    }
+    GETCHAR(code, inp);
+    GETCHAR(id, inp);
+    syslog(LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d",
+	   PROTO_NAME(f), code, id);
+
+    if( f->state == ACKRCVD )
+	f->state = REQSENT;
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(f)
+    fsm *f;
+{
+    switch( f->state ){
+    case CLOSING:
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	/* fall through */
+    case CLOSED:
+	f->state = CLOSED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	break;
+
+    case STOPPING:
+    case REQSENT:
+    case ACKRCVD:
+    case ACKSENT:
+	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
+	/* fall through */
+    case STOPPED:
+	f->state = STOPPED;
+	if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
+	break;
+
+    case OPENED:
+	if( f->callbacks->down )
+	    (*f->callbacks->down)(f);
+
+	/* Init restart counter, send Terminate-Request */
+	f->retransmits = f->maxtermtransmits;
+	fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+		  (u_char *) f->term_reason, f->term_reason_len);
+	TIMEOUT(fsm_timeout, f, f->timeouttime);
+	--f->retransmits;
+
+	f->state = STOPPING;
+	break;
+
+    default:
+	FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!",
+		  PROTO_NAME(f), f->state));
+    }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(f, retransmit)
+    fsm *f;
+    int retransmit;
+{
+    u_char *outp;
+    int cilen;
+
+    if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
+	/* Not currently negotiating - reset options */
+	if( f->callbacks->resetci )
+	    (*f->callbacks->resetci)(f);
+	f->nakloops = 0;
+    }
+
+    if( !retransmit ){
+	/* New request - reset retransmission counter, use new ID */
+	f->retransmits = f->maxconfreqtransmits;
+	f->reqid = ++f->id;
+    }
+
+    f->seen_ack = 0;
+
+    /*
+     * Make up the request packet
+     */
+    outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
+    if( f->callbacks->cilen && f->callbacks->addci ){
+	cilen = (*f->callbacks->cilen)(f);
+	if( cilen > peer_mru[f->unit] - HEADERLEN )
+	    cilen = peer_mru[f->unit] - HEADERLEN;
+	if (f->callbacks->addci)
+	    (*f->callbacks->addci)(f, outp, &cilen);
+    } else
+	cilen = 0;
+
+    /* send the request to our peer */
+    fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+    /* start the retransmit timer */
+    --f->retransmits;
+    TIMEOUT(fsm_timeout, f, f->timeouttime);
+
+    FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d",
+	      PROTO_NAME(f), f->reqid));
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata(f, code, id, data, datalen)
+    fsm *f;
+    u_char code, id;
+    u_char *data;
+    int datalen;
+{
+    u_char *outp;
+    int outlen;
+
+    /* Adjust length to be smaller than MTU */
+    outp = outpacket_buf;
+    if (datalen > peer_mru[f->unit] - HEADERLEN)
+	datalen = peer_mru[f->unit] - HEADERLEN;
+    if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
+	BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+    outlen = datalen + HEADERLEN;
+    MAKEHEADER(outp, f->protocol);
+    PUTCHAR(code, outp);
+    PUTCHAR(id, outp);
+    PUTSHORT(outlen, outp);
+    output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+    FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d, id %d.",
+	      PROTO_NAME(f), code, id));
+}