* 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.
* Mechanism to exit() and/or drop DTR.
* 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_rconfack
__ARGS((fsm
*, u_char
*, int, int));
static void fsm_rconfnak
__ARGS((fsm
*, u_char
*, int, int));
static void fsm_rconfrej
__ARGS((fsm
*, u_char
*, int, 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_rprotrej
__ARGS((fsm
*, u_char
*, int));
static void fsm_sconfreq
__ARGS((fsm
*));
* fsm_init - Initialize fsm.
f
->id
= 0; /* XXX Start with random id? */
* fsm_activeopen - Actively open connection.
* Set new state, reset desired options and send requests.
f
->flags
&= ~(AOPENDING
|POPENDING
); /* Clear pending flags */
if (f
->state
== REQSENT
|| /* Already actively open(ing)? */
if (f
->state
== TERMSENT
|| /* Closing or */
!(f
->flags
& LOWERUP
)) { /* lower layer down? */
f
->flags
|= AOPENDING
; /* Wait for desired event */
if (f
->callbacks
->resetci
)
(*f
->callbacks
->resetci
)(f
); /* Reset options */
fsm_sconfreq(f
); /* Send Configure-Request */
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
f
->retransmits
= 0; /* Reset retransmits count */
f
->nakloops
= 0; /* Reset nakloops count */
* fsm_passiveopen - Passively open connection.
* Set new state and reset desired options.
f
->flags
&= ~(AOPENDING
|POPENDING
); /* Clear pending flags */
if (f
->state
== LISTEN
|| /* Already passively open(ing)? */
if (f
->state
== REQSENT
|| /* Active-Opening or */
f
->state
== TERMSENT
|| /* closing or */
!(f
->flags
& LOWERUP
)) { /* lower layer down? */
f
->flags
|= POPENDING
; /* Wait for desired event */
if (f
->callbacks
->resetci
)
(*f
->callbacks
->resetci
)(f
); /* Reset options */
f
->retransmits
= 0; /* Reset retransmits count */
f
->nakloops
= 0; /* Reset nakloops count */
* fsm_close - Start closing connection.
* Cancel timeouts and either initiate close or possibly go directly to
f
->flags
&= ~(AOPENDING
|POPENDING
); /* Clear pending flags */
if (f
->state
== CLOSED
|| /* Already CLOSED or Closing? */
if (f
->state
== REQSENT
|| /* Timeout pending for Open? */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
if (f
->state
== OPEN
&& /* Open? */
(*f
->callbacks
->down
)(f
); /* Inform upper layers we're down */
if (f
->state
== ACKSENT
|| /* Could peer be OPEN? */
fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
, NULL
, 0);
/* Send Terminate-Request */
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
f
->retransmits
= 0; /* Reset retransmits count */
if (f
->callbacks
->closed
)
(*f
->callbacks
->closed
)(f
); /* Exit/restart/etc. */
* fsm_timeout - Timeout expired.
if (f
->flags
& POPENDING
) { /* Go passive? */
f
->state
= CLOSED
; /* Pretend for a moment... */
if (f
->retransmits
> f
->maxconfreqtransmits
) {
if (f
->nakloops
> f
->maxnakloops
) {
syslog(LOG_INFO
, "%s: timeout sending Config-Requests",
proto_name(f
->protocol
));
syslog(LOG_INFO
, "%s: timed out. Config-Requests not accepted",
proto_name(f
->protocol
));
/* timeout sending config-requests */
if (f
->callbacks
->retransmit
) /* If there is a retransmit rtn? */
(*f
->callbacks
->retransmit
)(f
);
fsm_sconfreq(f
); /* Send Configure-Request */
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
if (f
->flags
& POPENDING
) { /* Go passive? */
f
->state
= CLOSED
; /* Pretend for a moment... */
if (++f
->retransmits
> f
->maxtermtransmits
) {
* We've waited for an ack long enough. Peer probably heard us.
if (f
->callbacks
->closed
)
(*f
->callbacks
->closed
)(f
); /* Exit/restart/etc. */
if (f
->callbacks
->retransmit
) /* If there is a retransmit rtn? */
(*f
->callbacks
->retransmit
)(f
);
fsm_sdata(f
, TERMREQ
, f
->reqid
= ++f
->id
, NULL
, 0);
/* Send Terminate-Request */
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
* fsm_lowerup - The lower layer is up.
* Start Active or Passive Open if pending.
if (f
->flags
& AOPENDING
) /* Attempting Active-Open? */
fsm_activeopen(f
); /* Try it now */
else if (f
->flags
& POPENDING
) /* Attempting Passive-Open? */
fsm_passiveopen(f
); /* Try it now */
* fsm_lowerdown - The lower layer is down.
* Cancel all timeouts and inform upper layers.
if (f
->state
== REQSENT
|| /* Timeout pending? */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
if (f
->state
== OPEN
&& /* OPEN? */
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
if (f
->callbacks
->closed
)
(*f
->callbacks
->closed
)(f
); /* Exit/restart/etc. */
* fsm_protreject - Peer doesn't speak this protocol.
* Pretend that the lower layer went down.
* 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.", f
->protocol
))
FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd illegal length.",
FSMDEBUG((LOG_INFO
, "fsm_input(%x): Rcvd short packet.",
len
-= HEADERLEN
; /* subtract header length */
* Action depends on code.
FSMDEBUG((LOG_INFO
, "fsm_rconfreq(%x): Rcvd id %d.",
if (f
->state
== TERMSENT
)
if (f
->state
== CLOSED
) {
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
if (f
->state
== OPEN
&& f
->callbacks
->down
)
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
if (f
->state
== OPEN
|| f
->state
== LISTEN
) {
/* XXX Possibly need hold-down on OPEN? */
fsm_sconfreq(f
); /* Send Configure-Request */
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
if (f
->callbacks
->reqci
) /* Check CI */
code
= (*f
->callbacks
->reqci
)(f
, inp
, &len
);
code
= CONFREJ
; /* Reject all CI */
len
+= HEADERLEN
; /* add header length back on */
inp
= inpacket
; /* Reset to header */
outp
= outpacket_buf
; /* get pointer to output buffer */
MAKEHEADER(outp
, f
->protocol
); /* paste in DLL header */
BCOPY(inp
, outp
, len
); /* copy input packet */
PUTCHAR(code
, outp
); /* put in the code, id, and length*/
output(f
->unit
, outpacket_buf
, len
+ DLLHEADERLEN
); /* send it out */
if (f
->state
== ACKRCVD
) {
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
(*f
->callbacks
->up
)(f
); /* Inform upper layers */
fsm_rconfack(f
, inp
, id
, len
);
fsm_rconfnak(f
, inp
, id
, len
);
fsm_rconfrej(f
, inp
, id
, len
);
fsm_rcoderej(f
, inp
, len
);
fsm_rprotrej(f
, inp
, len
);
FSMDEBUG((LOG_INFO
, "lcp: Echo-Request, Rcvd id %d", id
));
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
inp
= inpacket
; /* Reset to header */
outp
= outpacket_buf
; /* get pointer to output buffer */
MAKEHEADER(outp
, f
->protocol
); /* add DLL header */
len
+= HEADERLEN
; /* add header length */
BCOPY(inp
, outp
, len
); /* copy input packet to output buffer */
PUTCHAR(ECHOREP
, outp
); /* set code to echo reply */
PUTCHAR(id
, outp
); /* add in id */
PUTSHORT(len
, outp
); /* and length */
output(f
->unit
, outpacket_buf
, len
+ DLLHEADERLEN
); /* send it */
/* XXX Deliver to ECHOREQ sender? */
fsm_sdata(f
, CODEREJ
, ++f
->id
, inpacket
, len
+ HEADERLEN
);
* fsm_rconfack - Receive Configure-Ack.
fsm_rconfack(f
, inp
, id
, len
)
FSMDEBUG((LOG_INFO
, "fsm_rconfack(%x): Rcvd id %d.",
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
if (id
!= f
->reqid
) /* Expected id? */
break; /* Nope, toss... */
if (f
->callbacks
->ackci
&&
(*f
->callbacks
->ackci
)(f
, inp
, len
)) /* Good ack? */
f
->state
= REQSENT
; /* Wait for timeout to retransmit */
if (id
!= f
->reqid
) /* Expected id? */
break; /* Nope, toss... */
if (f
->callbacks
->ackci
&&
(*f
->callbacks
->ackci
)(f
, inp
, len
)) { /* Good ack? */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
(*f
->callbacks
->up
)(f
); /* Inform upper layers */
f
->state
= REQSENT
; /* Wait for timeout to retransmit */
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
f
->state
= CLOSED
; /* Only for a moment... */
fsm_activeopen(f
); /* Restart */
* fsm_rconfnak - Receive Configure-Nak.
fsm_rconfnak(f
, inp
, id
, len
)
FSMDEBUG((LOG_INFO
, "fsm_rconfnak(%x): Rcvd id %d.",
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
if (id
!= f
->reqid
) /* Expected id? */
break; /* Nope, toss... */
if (++f
->nakloops
> f
->maxnakloops
) {
"fsm_rconfnak(%x): Possible CONFNAK loop!",
break; /* Break the loop */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
(*f
->callbacks
->nakci
)(f
, inp
, len
);
fsm_sconfreq(f
); /* Send Configure-Request */
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
f
->state
= REQSENT
; /* Wait for timeout to retransmit */
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
f
->state
= CLOSED
; /* Only for a moment... */
fsm_activeopen(f
); /* Restart */
* fsm_rconfrej - Receive Configure-Rej.
fsm_rconfrej(f
, inp
, id
, len
)
FSMDEBUG((LOG_INFO
, "fsm_rconfrej(%x): Rcvd id %d.",
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
if (id
!= f
->reqid
) /* Expected id? */
break; /* Nope, toss... */
if (++f
->nakloops
> f
->maxnakloops
)
break; /* Break the loop */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
(*f
->callbacks
->rejci
)(f
, inp
, len
);
fsm_sconfreq(f
); /* Send Configure-Request */
TIMEOUT(fsm_timeout
, (caddr_t
) f
, f
->timeouttime
);
f
->state
= REQSENT
; /* Wait for timeout to retransmit */
f
->state
= CLOSED
; /* Only for a moment... */
fsm_activeopen(f
); /* Restart */
* fsm_rtermreq - Receive Terminate-Req.
FSMDEBUG((LOG_INFO
, "fsm_rtermreq(%x): Rcvd id %d.",
fsm_sdata(f
, TERMACK
, id
, NULL
, 0);
f
->state
= REQSENT
; /* Start over but keep trying */
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
if (f
->callbacks
->closed
)
(*f
->callbacks
->closed
)(f
); /* Exit/restart/etc. */
* fsm_rtermack - Receive Terminate-Ack.
FSMDEBUG((LOG_INFO
, "fsm_rtermack(%x).", f
->protocol
))
(*f
->callbacks
->down
)(f
); /* Inform upper layers */
if (f
->callbacks
->closed
)
(*f
->callbacks
->closed
)(f
); /* Exit/restart/etc. */
UNTIMEOUT(fsm_timeout
, (caddr_t
) f
); /* Cancel timeout */
if (f
->callbacks
->closed
)
(*f
->callbacks
->closed
)(f
); /* Exit/restart/etc. */
* fsm_rcoderej - Receive an Code-Reject.
fsm_rcoderej(f
, inp
, len
)
FSMDEBUG((LOG_INFO
, "fsm_rcoderej(%x).", f
->protocol
))
if (len
< sizeof (u_char
)) {
"fsm_rcoderej: Rcvd short Code-Reject packet!"))
"fsm_rcoderej: Rcvd Code-Reject for code %d!",
* fsm_rprotrej - Receive an Protocol-Reject.
* Figure out which protocol is rejected and inform it.
fsm_rprotrej(f
, inp
, len
)
FSMDEBUG((LOG_INFO
, "fsm_rprotrej."))
if (len
< sizeof (u_short
)) {
"fsm_rprotrej: Rcvd short Protocol-Reject packet!"))
if (f
->protocol
!= LCP
) { /* Only valid for LCP */
"fsm_rprotrej: Rcvd non-LCP Protocol-Reject!"))
"fsm_rprotrej: Rcvd Protocol-Reject packet for %x!",
DEMUXPROTREJ(f
->unit
, prot
); /* Inform protocol */
* fsm_sconfreq - Send a Configure-Request.
outlen
= HEADERLEN
+ (f
->callbacks
->cilen
? (*f
->callbacks
->cilen
)(f
) : 0);
/* XXX Adjust outlen to MTU */
MAKEHEADER(outp
, f
->protocol
);
PUTCHAR(f
->reqid
= ++f
->id
, outp
);
if (f
->callbacks
->cilen
&& f
->callbacks
->addci
)
(*f
->callbacks
->addci
)(f
, outp
);
output(f
->unit
, outpacket_buf
, outlen
+ DLLHEADERLEN
);
FSMDEBUG((LOG_INFO
, "%s: sending Configure-Request, id %d",
proto_name(f
->protocol
), f
->reqid
))
* fsm_sdata - Send some data.
* Used for Terminate-Request, Terminate-Ack, Code-Reject, Protocol-Reject,
* Echo-Request, and Discard-Request.
fsm_sdata(f
, code
, id
, data
, datalen
)
/* Adjust length to be smaller than MTU */
if (datalen
> MTU
- HEADERLEN
)
datalen
= MTU
- HEADERLEN
;
outlen
= datalen
+ HEADERLEN
;
MAKEHEADER(outp
, f
->protocol
);
BCOPY(data
, outp
, datalen
);
output(f
->unit
, outpacket_buf
, outlen
+ DLLHEADERLEN
);
FSMDEBUG((LOG_INFO
, "fsm_sdata(%x): Sent code %d, id %d.",