* fsm.c - {Link, IP} Control Protocol Finite State Machine.
* Copyright (c) 1989 Carnegie Mellon University.
* 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.
static char rcsid
[] = "$Id: fsm.c,v 1.2 1994/03/30 09:31:25 jkh Exp $";
* Randomize fsm id on link/init.
* Deal with variable outgoing MTU.
extern char *proto_name();
static void fsm_timeout
__ARGS((caddr_t
));
static void fsm_rconfreq
__ARGS((fsm
*, int, u_char
*, int));
static void fsm_rconfack
__ARGS((fsm
*, int, u_char
*, int));
static void fsm_rconfnakrej
__ARGS((fsm
*, int, int, u_char
*, int));
static void fsm_rtermreq
__ARGS((fsm
*, int));
static void fsm_rtermack
__ARGS((fsm
*));
static void fsm_rcoderej
__ARGS((fsm
*, u_char
*, int));
static void fsm_sconfreq
__ARGS((fsm
*, int));
#define PROTO_NAME(f) ((f)->callbacks->proto_name)
* fsm_init - Initialize fsm.
f
->id
= 0; /* XXX Start with random id? */
f
->timeouttime
= DEFTIMEOUT
;
f
->maxconfreqtransmits
= DEFMAXCONFREQS
;
f
->maxtermtransmits
= DEFMAXTERMREQS
;
f
->maxnakloops
= DEFMAXNAKLOOPS
;
* fsm_lowerup - The lower layer is up.
if( f
->flags
& OPT_SILENT
)
/* Send an initial configure-request */
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.
if( f
->callbacks
->starting
)
(*f
->callbacks
->starting
)(f
);
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
(*f
->callbacks
->down
)(f
);
FSMDEBUG((LOG_INFO
, "%s: Down event in state %d!",
PROTO_NAME(f
), f
->state
));
* fsm_open - Link is allowed to come up.
if( f
->callbacks
->starting
)
(*f
->callbacks
->starting
)(f
);
if( f
->flags
& OPT_SILENT
)
/* Send an initial configure-request */
if( f
->flags
& OPT_RESTART
){
* fsm_close - Start closing connection.
* Cancel timeouts and either initiate close or possibly go directly to
UNTIMEOUT(fsm_timeout
, (caddr_t
) 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
, NULL
, 0);
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
* fsm_timeout - Timeout expired.
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
);
/* Send Terminate-Request */
fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
, NULL
, 0);
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
if (f
->retransmits
<= 0) {
syslog(LOG_WARNING
, "%s: timeout sending Config-Requests",
if( (f
->flags
& OPT_PASSIVE
) == 0 && f
->callbacks
->finished
)
(*f
->callbacks
->finished
)(f
);
/* Retransmit the configure-request */
if (f
->callbacks
->retransmit
)
(*f
->callbacks
->retransmit
)(f
);
fsm_sconfreq(f
, 1); /* Re-send Configure-Request */
if( f
->state
== ACKRCVD
)
FSMDEBUG((LOG_INFO
, "%s: Timeout event in state %d!",
PROTO_NAME(f
), f
->state
));
* fsm_input - Input packet.
fsm_input(f
, inpacket
, l
)
* Parse header (code, id and length).
* If packet too short, drop it.
FSMDEBUG((LOG_WARNING
, "fsm_input(%x): Rcvd short header.",
FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd illegal length.",
FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd short packet.",
len
-= HEADERLEN
; /* subtract header length */
if( f
->state
== INITIAL
|| f
->state
== STARTING
){
FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd packet in state %d.",
* Action depends on code.
fsm_rconfreq(f
, id
, inp
, len
);
fsm_rconfack(f
, id
, inp
, len
);
fsm_rconfnakrej(f
, code
, id
, inp
, len
);
fsm_rcoderej(f
, inp
, len
);
if( !f
->callbacks
->extcode
|| !(*f
->callbacks
->extcode
)(f
, code
, id
, inp
, len
) )
fsm_sdata(f
, CODEREJ
, ++f
->id
, inpacket
, len
+ HEADERLEN
);
* fsm_rconfreq - Receive Configure-Request.
fsm_rconfreq(f
, id
, inp
, len
)
int code
, reject_if_disagree
;
FSMDEBUG((LOG_INFO
, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f
), id
));
/* Go away, we're closed */
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
/* Go down and restart negotiation */
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
/* Negotiation started by our peer */
fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
* 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
);
code
= CONFREJ
; /* Reject all CI */
/* send the Ack, Nak or Rej to the peer */
fsm_sdata(f
, code
, id
, inp
, len
);
if (f
->state
== ACKRCVD
) {
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
(*f
->callbacks
->up
)(f
); /* Inform upper layers */
/* we sent CONFACK or CONFREJ */
* fsm_rconfack - Receive Configure-Ack.
fsm_rconfack(f
, id
, inp
, len
)
FSMDEBUG((LOG_INFO
, "fsm_rconfack(%s): Rcvd id %d.",
if (id
!= f
->reqid
) /* Expected id? */
return; /* Nope, toss... */
if( !(f
->callbacks
->ackci
? (*f
->callbacks
->ackci
)(f
, inp
, len
): (len
== 0)) ){
/* Ack is bad - ignore it */
FSMDEBUG((LOG_INFO
, "%s: received bad Ack (length %d)",
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
f
->retransmits
= f
->maxconfreqtransmits
;
/* Huh? an extra Ack? oh well... */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
f
->retransmits
= f
->maxconfreqtransmits
;
(*f
->callbacks
->up
)(f
); /* Inform upper layers */
/* Go down and restart negotiation */
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
* fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
fsm_rconfnakrej(f
, code
, id
, inp
, len
)
FSMDEBUG((LOG_INFO
, "fsm_rconfnakrej(%s): Rcvd id %d.",
if (id
!= f
->reqid
) /* Expected id? */
return; /* Nope, toss... */
proc
= (code
== CONFNAK
)? f
->callbacks
->nakci
: f
->callbacks
->rejci
;
if( !proc
|| !proc(f
, inp
, len
) ){
/* Nak/reject is bad - ignore it */
FSMDEBUG((LOG_INFO
, "%s: received bad %s (length %d)",
PROTO_NAME(f
), (code
==CONFNAK
? "Nak": "reject"), len
));
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
/* They didn't agree to what we wanted - try another request */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
fsm_sconfreq(f
, 0); /* Send Configure-Request */
/* Got a Nak/reject when we had already had an Ack?? oh well... */
/* Go down and restart negotiation */
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
fsm_sconfreq(f
, 0); /* Send initial Configure-Request */
* fsm_rtermreq - Receive Terminate-Req.
FSMDEBUG((LOG_INFO
, "fsm_rtermreq(%s): Rcvd id %d.",
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
f
->state
= REQSENT
; /* Start over but keep trying */
syslog(LOG_INFO
, "%s terminated at peer's request", PROTO_NAME(f
));
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
* fsm_rtermack - Receive Terminate-Ack.
FSMDEBUG((LOG_INFO
, "fsm_rtermack(%s).", PROTO_NAME(f
)));
if( f
->callbacks
->finished
)
(*f
->callbacks
->finished
)(f
);
if( f
->callbacks
->finished
)
(*f
->callbacks
->finished
)(f
);
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
* fsm_rcoderej - Receive an Code-Reject.
fsm_rcoderej(f
, inp
, len
)
FSMDEBUG((LOG_INFO
, "fsm_rcoderej(%s).", PROTO_NAME(f
)));
FSMDEBUG((LOG_INFO
, "fsm_rcoderej: Rcvd short Code-Reject packet!"));
syslog(LOG_WARNING
, "%s: Rcvd Code-Reject for code %d, id %d",
PROTO_NAME(f
), code
, id
);
if( f
->state
== ACKRCVD
)
* fsm_protreject - Peer doesn't speak this protocol.
* Treat this as a catastrophic error (RXJ-).
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
if( f
->callbacks
->finished
)
(*f
->callbacks
->finished
)(f
);
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
if( f
->callbacks
->finished
)
(*f
->callbacks
->finished
)(f
);
(*f
->callbacks
->down
)(f
);
/* Init restart counter, send Terminate-Request */
f
->retransmits
= f
->maxtermtransmits
;
fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
, NULL
, 0);
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
FSMDEBUG((LOG_INFO
, "%s: Protocol-reject event in state %d!",
PROTO_NAME(f
), f
->state
));
* fsm_sconfreq - Send a Configure-Request.
fsm_sconfreq(f
, retransmit
)
if( f
->state
!= REQSENT
&& f
->state
!= ACKRCVD
&& f
->state
!= ACKSENT
){
/* Not currently negotiating - reset options */
if( f
->callbacks
->resetci
)
(*f
->callbacks
->resetci
)(f
);
/* New request - reset retransmission counter, use new ID */
f
->retransmits
= f
->maxconfreqtransmits
;
* Make up the request packet
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
;
outp
= outpacket_buf
+ DLLHEADERLEN
+ HEADERLEN
;
(*f
->callbacks
->addci
)(f
, outp
, &cilen
);
/* send the request to our peer */
fsm_sdata(f
, CONFREQ
, f
->reqid
, outp
, cilen
);
/* start the retransmit timer */
TIMEOUT(fsm_timeout
, (caddr_t
) 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.
fsm_sdata(f
, code
, id
, data
, datalen
)
/* Adjust length to be smaller than MTU */
if (datalen
> peer_mru
[f
->unit
] - HEADERLEN
)
datalen
= peer_mru
[f
->unit
] - HEADERLEN
;
if (datalen
&& data
!= outp
+ DLLHEADERLEN
+ HEADERLEN
)
BCOPY(data
, outp
+ DLLHEADERLEN
+ HEADERLEN
, datalen
);
outlen
= datalen
+ HEADERLEN
;
MAKEHEADER(outp
, f
->protocol
);
output(f
->unit
, outpacket_buf
, outlen
+ DLLHEADERLEN
);
FSMDEBUG((LOG_INFO
, "fsm_sdata(%s): Sent code %d, id %d.",
PROTO_NAME(f
), code
, id
));