/*
* fsm.c - {Link, IP} Control Protocol Finite State Machine.
*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* TODO:
* Deal with variable outgoing MTU.
*/
#include <stdio.h>
#include <string.h>
#ifndef NO_DRAND48
#include <stdlib.h>
#endif /* NO_DRAND48 */
#include "pppd.h"
#include "fsm.h"
#endif
static void fsm_timeout __P((void *));
const char *
{
return buf;
}
return fsm_states[statenum];
}
/*
* fsm_init - Initialize fsm.
*
* Initialize fsm state.
*/
void
fsm_init(f)
fsm *f;
{
f->flags = 0;
f->timeouttime = DEFTIMEOUT;
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:
break;
case STARTING:
if( f->flags & OPT_SILENT )
else {
/* Send an initial configure-request */
fsm_sconfreq(f, 0);
}
break;
default:
}
}
/*
* fsm_lowerdown - The lower layer is down.
*
* Cancel all timeouts and inform upper layers.
*/
void
fsm *f;
{
switch( f->state ){
case CLOSED:
break;
case STOPPED:
break;
case CLOSING:
break;
case STOPPING:
case REQSENT:
case ACKRCVD:
case ACKSENT:
break;
case OPENED:
break;
default:
}
}
/*
* fsm_open - Link is allowed to come up.
*/
void
fsm_open(f)
fsm *f;
{
switch( f->state ){
case INITIAL:
break;
case CLOSED:
if( f->flags & OPT_SILENT )
else {
/* Send an initial configure-request */
fsm_sconfreq(f, 0);
}
break;
case CLOSING:
/*FALLTHROUGH*/
case STOPPING:
case STOPPED:
case OPENED:
if( f->flags & OPT_RESTART ){
fsm_lowerdown(f);
fsm_lowerup(f);
}
break;
case STARTING:
case REQSENT:
case ACKRCVD:
case ACKSENT:
/* explicitly do nothing here. */
break;
}
}
/*
* fsm_close - Start closing connection.
*
* Cancel timeouts and either initiate close or possibly go directly to
* the CLOSED state.
*/
void
fsm *f;
char *reason;
{
f->term_reason = reason;
switch( f->state ){
case STARTING:
break;
case STOPPED:
break;
case STOPPING:
break;
case REQSENT:
case ACKRCVD:
case ACKSENT:
case OPENED:
/*
* Note that this-layer-down means "stop transmitting."
* This-layer-finished means "stop everything."
*/
/* Init restart counter, send Terminate-Request */
f->retransmits = f->maxtermtransmits;
--f->retransmits;
break;
case INITIAL:
case CLOSED:
case CLOSING:
/* explicitly do nothing here. */
break;
}
}
/*
* fsm_timeout - Timeout expired.
*/
static void
void *arg;
{
switch (f->state) {
case CLOSING:
case STOPPING:
if( f->retransmits <= 0 ){
/*
* We've waited for an ack long enough. Peer probably heard us.
*/
} else {
/* Send Terminate-Request */
--f->retransmits;
}
break;
case REQSENT:
case ACKRCVD:
case ACKSENT:
if (f->retransmits <= 0) {
} else {
/* Retransmit the configure-request */
(*f->callbacks->retransmit)(f);
}
break;
default:
}
}
/*
* fsm_input - Input packet.
*/
void
fsm *f;
int l;
{
int len;
/*
* Parse header (code, id and length).
* If packet too short, drop it.
*/
if (l < HEADERLEN) {
return;
}
return;
}
if (len > l) {
l);
return;
}
return;
}
/*
* Action depends on code.
*/
switch (code) {
case CODE_CONFREQ:
break;
case CODE_CONFACK:
break;
case CODE_CONFNAK:
case CODE_CONFREJ:
break;
case CODE_TERMREQ:
break;
case CODE_TERMACK:
fsm_rtermack(f);
break;
case CODE_CODEREJ:
break;
default:
break;
}
}
/*
* fsm_rconfreq - Receive Configure-Request.
*/
static void
fsm *f;
int len;
{
switch( f->state ){
case CLOSED:
/* Go away, we're closed */
return;
case CLOSING:
case STOPPING:
return;
case OPENED:
/* Go down and restart negotiation */
break;
}
#ifdef DEBUG
fatal("bad pointer");
#endif
/*
* Pass the requested configuration options
* to protocol-specific code for checking.
*/
} else if (len > 0)
else
code = CODE_CONFACK;
/* Allow NCP to do fancy footwork, such as reinitializing. */
if (code <= 0)
return;
fsm_sconfreq(f, 0); /* Send initial Configure-Request */
/* send the Ack, Nak or Rej to the peer */
if (code == CODE_CONFACK) {
/* RFC 1661 event RCR+ */
} else
f->nakloops = 0;
} else {
/* RFC 1661 event RCR- */
/* (we sent CODE_CONFNAK or CODE_CONFREJ) */
if( code == CODE_CONFNAK )
++f->nakloops;
}
}
/*
* fsm_rconfack - Receive Configure-Ack.
*/
static void
fsm *f;
int id;
int len;
{
return; /* Nope, toss... */
(len == 0)) ){
/* Ack is bad - ignore it */
return;
}
f->seen_ack = 1;
switch (f->state) {
case CLOSED:
case STOPPED:
break;
case REQSENT:
f->retransmits = f->maxconfreqtransmits;
break;
case ACKRCVD:
/* Huh? an extra valid Ack? oh well... */
fsm_sconfreq(f, 0);
break;
case ACKSENT:
f->retransmits = f->maxconfreqtransmits;
break;
case OPENED:
/* Go down and restart negotiation */
fsm_sconfreq(f, 0); /* Send initial Configure-Request */
break;
}
}
/*
* fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
*/
static void
fsm *f;
int len;
{
int ret;
return; /* Nope, toss... */
return;
}
f->seen_ack = 1;
switch (f->state) {
case CLOSED:
case STOPPED:
break;
case REQSENT:
case ACKSENT:
/* They didn't agree to what we wanted - try another request */
if (ret < 0)
else
fsm_sconfreq(f, 0); /* Send Configure-Request */
break;
case ACKRCVD:
fsm_sconfreq(f, 0);
break;
case OPENED:
/* Go down and restart negotiation */
fsm_sconfreq(f, 0); /* Send initial Configure-Request */
break;
}
}
/*
* fsm_rtermreq - Receive Terminate-Req.
*/
static void
fsm *f;
int id;
u_char *p;
int len;
{
switch (f->state) {
case ACKRCVD:
case ACKSENT:
break;
case OPENED:
if (len > 0) {
} else {
}
f->retransmits = 0;
break;
}
}
/*
* fsm_rtermack - Receive Terminate-Ack.
*/
static void
fsm_rtermack(f)
fsm *f;
{
switch (f->state) {
case CLOSING:
UNTIMEOUT(fsm_timeout, f);
break;
case STOPPING:
UNTIMEOUT(fsm_timeout, f);
break;
case ACKRCVD:
break;
case OPENED:
fsm_sconfreq(f, 0);
break;
}
}
/*
* fsm_rcoderej - Receive a Code-Reject.
*/
static void
fsm *f;
int len;
{
int seriouserr;
return;
}
len -= 2;
/* Let the protocol know what happened. */
} else {
/*
* By default, it's RXJ- for well-known codes and RXJ+ for
* unknown ones.
*/
}
if (seriouserr) {
/* RXJ- -- shut down the protocol. */
switch (f->state) {
case CLOSING:
/*FALLTHROUGH*/
case CLOSED:
break;
case STOPPING:
case REQSENT:
case ACKRCVD:
case ACKSENT:
/*FALLTHROUGH*/
case STOPPED:
break;
case OPENED:
if (f->term_reason == NULL) {
f->term_reason = "unacceptable Code-Reject received";
}
/* Init restart counter, send Terminate-Request */
f->retransmits = f->maxtermtransmits;
--f->retransmits;
break;
default:
fatal("state error");
}
} else {
/* RXJ+ -- just back up from Ack-Rcvd to Req-Sent. */
}
}
/*
* fsm_protreject - Peer doesn't speak this protocol.
*
* Treat this as a catastrophic error (RXJ-).
*/
void
fsm *f;
{
switch( f->state ){
case CLOSING:
/*FALLTHROUGH*/
case CLOSED:
break;
case STOPPING:
case REQSENT:
case ACKRCVD:
case ACKSENT:
/*FALLTHROUGH*/
case STOPPED:
break;
case OPENED:
/* Init restart counter, send Terminate-Request */
f->retransmits = f->maxtermtransmits;
--f->retransmits;
break;
default:
}
}
/*
* fsm_sconfreq - Send a Configure-Request.
*/
static void
fsm *f;
int retransmit;
{
int cilen;
/* Not currently negotiating - reset options */
f->nakloops = 0;
}
if( !retransmit ){
/* New request - reset retransmission counter, use new ID */
f->retransmits = f->maxconfreqtransmits;
}
f->seen_ack = 0;
/*
* Make up the request packet
*/
} else {
}
else
cilen = 0;
/* send the request to our peer */
/* start the retransmit timer */
--f->retransmits;
}
/*
* fsm_sdata - Send some data.
*
* Used for all packets sent to our peer by this module.
*/
void
fsm *f;
int datalen;
{
int outlen;
dbglog("%s: Peer has rejected %s; not sending another",
return;
}
/* Adjust length to be smaller than MTU */
}
/*
* fsm_setpeermru - Set our idea of the peer's mru
*
* Used by routines in lcp.c which negotiate this value.
*/
void
int unit;
int mru;
{
dbglog("fsm_setpeermru: unit out of bounds");
} else {
}
}