ftp.nice.ch/pub/next/unix/communication/Alby.2.PPP.0.3.N.bs.tar.gz#/source/fsm.c

This is fsm.c in view mode; [Download] [Up]

/*
 * 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.
 */

/*
 * TODO:
 * Mechanism to exit() and/or drop DTR.
 * Hold-down on open?
 * Randomize fsm id on link/init.
 * Deal with variable outgoing MTU.
 */

#include <stdio.h>
#include <sys/types.h>

#ifdef STREAMS
#include	<sys/stream.h>
#include	<sys/socket.h>
#include	<net/if.h>
#endif
#include "ppp.h"
#include "fsm.h"


void fsm_timeout(), fsm_rconfreq(), fsm_rconfack(), fsm_rconfnak();
void fsm_rconfrej(), fsm_rtermreq(), fsm_rtermack(), fsm_rcoderej();
void fsm_rprotrej(), fsm_rechoreq(), fsm_sconfreq(), fsm_sdata();


/*
 * fsm_init - Initialize fsm.
 *
 * Initialize fsm state.
 */
void fsm_init(f)
    fsm *f;
{
    f->state = CLOSED;
    f->flags = 0;
    f->id = 0;				/* XXX Start with random id? */
}


/*
 * fsm_activeopen - Actively open connection.
 *
 * Set new state, reset desired options and send requests.
 */
void fsm_activeopen(f)
    fsm *f;
{
    f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */
    if (f->state == REQSENT ||		/* Already actively open(ing)? */
	f->state == ACKRCVD ||
	f->state == ACKSENT ||
	f->state == OPEN)
	return;
    if (f->state == TERMSENT ||		/* Closing or */
	!(f->flags & LOWERUP)) {	/*  lower layer down? */
	f->flags |= AOPENDING;		/* Wait for desired event */
	return;
    }
    if (f->callbacks->resetci)
	(*f->callbacks->resetci)(f);	/* Reset options */
    fsm_sconfreq(f);			/* Send Configure-Request */
    TIMEOUT(fsm_timeout, f, f->timeouttime);
    f->state = REQSENT;
    f->retransmits = 0;			/* Reset retransmits count */
    f->nakloops = 0;			/* Reset nakloops count */
}


/*
 * fsm_passiveopen - Passively open connection.
 *
 * Set new state and reset desired options.
 */
void fsm_passiveopen(f)
    fsm *f;
{
    f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */
    if (f->state == LISTEN ||		/* Already passively open(ing)? */
	f->state == OPEN)
	return;
    if (f->state == REQSENT ||		/* Active-Opening or */
	f->state == ACKRCVD ||
	f->state == ACKSENT ||
	f->state == TERMSENT ||		/*  closing or */
	!(f->flags & LOWERUP)) {	/*  lower layer down? */
	f->flags |= POPENDING;		/* Wait for desired event */
	return;
    }
    if (f->callbacks->resetci)
	(*f->callbacks->resetci)(f);	/* Reset options */
    f->state = LISTEN;
    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
 * the CLOSED state.
 */
void fsm_close(f)
    fsm *f;
{
    f->flags &= ~(AOPENDING|POPENDING); /* Clear pending flags */
    if (f->state == CLOSED ||		/* Already CLOSED or Closing? */
	f->state == TERMSENT)
	return;
    if (f->state == REQSENT ||		/* Timeout pending for Open? */
	f->state == ACKRCVD ||
	f->state == ACKSENT)
	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
    if (f->state == OPEN &&		/* Open? */
	f->callbacks->down)
	(*f->callbacks->down)(f);	/* Inform upper layers we're down */
    if (f->state == ACKSENT ||		/* Could peer be OPEN? */
	f->state == OPEN) {
	fsm_sdata(f, TERMREQ, f->reqid = ++f->id, NULL, 0);
					/* Send Terminate-Request */
	TIMEOUT(fsm_timeout, f, f->timeouttime);
	f->state = TERMSENT;
	f->retransmits = 0;		/* Reset retransmits count */
    }
    else {
	f->state = CLOSED;
	if (f->callbacks->closed)
	    (*f->callbacks->closed)(f);	/* Exit/restart/etc. */
    }
}


/*
 * fsm_timeout - Timeout expired.
 */
void fsm_timeout(f)
    fsm *f;
{
    switch (f->state) {
      case REQSENT:
      case ACKRCVD:
      case ACKSENT:
	if (f->flags & POPENDING) {	/* Go passive? */
	    f->state = CLOSED;		/* Pretend for a moment... */
	    fsm_passiveopen(f);
	    return;
	}
	if (f->callbacks->retransmit)	/* If there is a retransmit rtn? */
	    (*f->callbacks->retransmit)(f);
	fsm_sconfreq(f);		/* Send Configure-Request */
	TIMEOUT(fsm_timeout, f, f->timeouttime);
	f->state = REQSENT;
	++f->retransmits;
	f->nakloops = 0;
	break;

      case TERMSENT:
	if (f->flags & POPENDING) {	/* Go passive? */
	    f->state = CLOSED;		/* Pretend for a moment... */
	    fsm_passiveopen(f);
	    return;
	}
	if (++f->retransmits > f->maxtermtransmits) {
	    /*
	     * We've waited for an ack long enough.  Peer probably heard us.
	     */
	    f->state = CLOSED;
	    if (f->callbacks->closed)
		(*f->callbacks->closed)(f); /* Exit/restart/etc. */
	    return;
	}
	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, f, f->timeouttime);
	++f->retransmits;
    }
}


/*
 * fsm_lowerup - The lower layer is up.
 *
 * Start Active or Passive Open if pending.
 */
void fsm_lowerup(f)
    fsm *f;
{
    f->flags |= LOWERUP;
    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.
 */
void fsm_lowerdown(f)
    fsm *f;
{
#if 0					/* XXX */
    f->flags &= ~(LOWERUP|AOPENDING|POPENDING);
					/* Clear pending flags */
#endif
    f->flags &= ~LOWERUP;
    if (f->state == REQSENT ||		/* Timeout pending? */
	f->state == ACKRCVD ||
	f->state == ACKSENT ||
	f->state == TERMSENT)
	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
    if (f->state == OPEN &&		/* OPEN? */
	f->callbacks->down)
	(*f->callbacks->down)(f);	/* Inform upper layers */
    f->state = CLOSED;
    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.
 */
void fsm_protreject(f)
    fsm *f;
{
    fsm_lowerdown(f);
}


/*
 * fsm_input - Input packet.
 */
void fsm_input(f, inpacket, l)
    fsm *f;
    PACKET *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 = PACKET_DATA(inpacket);
    if (l < HEADERLEN) {
	FSMDEBUG((stderr, "ppp: fsm_input(%x): Rcvd short header.\n",
		  f->protocol));
	goto freepacket;
    }
    GETCHAR(code, inp);
    GETCHAR(id, inp);
    GETSHORT(len, inp);
    if (len < HEADERLEN) {
	FSMDEBUG((stderr, "ppp: fsm_input(%x): Rcvd illegal length.\n",
		  f->protocol));
	goto freepacket;
    }
    if (len > l) {
	FSMDEBUG((stderr, "ppp: fsm_input(%x): Rcvd short packet.\n",
		  f->protocol));
	goto freepacket;
    }
    len -= HEADERLEN;

    /*
     * Action depends on code.
     */
    switch (code) {
      case CONFREQ:
	FSMDEBUG((stderr, "ppp: fsm_rconfreq(%x): Rcvd id %d.\n",
		  f->protocol, id));

	if (f->state == TERMSENT)
	    goto freepacket;
	if (f->state == CLOSED) {
	    fsm_sdata(f, TERMACK, id, NULL, 0);
	    goto freepacket;
	}
	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, f, f->timeouttime);
	}
	if (f->callbacks->reqci)	/* Check CI */
	    code = (*f->callbacks->reqci)(f, inp, &len);
	else if (len)
	    code = CONFREJ;		/* Reject all CI */

	inp = PACKET_DATA(inpacket);	/* Reset to header */
	PUTCHAR(code, inp);
	PUTCHAR(id, inp);
	len += HEADERLEN;
	PUTSHORT(len, inp);
	OUTPUT(f->unit, inpacket, len, f->protocol);

	if (code == CONFACK) {
	    if (f->state == ACKRCVD) {
		UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
		if (f->callbacks->up)
		    (*f->callbacks->up)(f); /* Inform upper layers */
		f->state = OPEN;
	    }
	    else
		f->state = ACKSENT;
	}
	else {
	    if (f->state != ACKRCVD)
		f->state = REQSENT;
	}
	return;

      case CONFACK:
	fsm_rconfack(f, inp, id, len);
	break;

      case CONFNAK:
	fsm_rconfnak(f, inp, id, len);
	break;

      case CONFREJ:
	fsm_rconfrej(f, inp, id, len);
	break;

      case TERMREQ:
	fsm_rtermreq(f, id);
	break;

      case TERMACK:
	fsm_rtermack(f);
	break;

      case CODEREJ:
	fsm_rcoderej(f, inp, len);
	break;

      case PROTREJ:
	fsm_rprotrej(f, inp, len);
	break;

      case ECHOREQ:
	FSMDEBUG((stderr, "ppp: fsm_rechoreq(%x): Rcvd id %d.\n",
		  f->protocol, id));

	switch (f->state) {
	  case CLOSED:
	  case LISTEN:
	    fsm_sdata(f, TERMACK, id, NULL, 0);
	    break;

	  case OPEN:
	    inp = PACKET_DATA(inpacket); /* Reset to header */
	    PUTCHAR(ECHOREP, inp);
	    PUTCHAR(id, inp);
	    len += HEADERLEN;
	    PUTSHORT(len, inp);
	    OUTPUT(f->unit, inpacket, len, f->protocol);
	    return;
	}
	break;

      case ECHOREP:
      case DISCREQ:
	/* XXX Deliver to ECHOREQ sender? */
	break;

      default:
	fsm_sdata(f, CODEREJ, ++f->id, PACKET_DATA(inpacket), len + HEADERLEN);
	break;
    }

freepacket:
    PACKET_FREE(inpacket);
}


/*
 * fsm_rconfack - Receive Configure-Ack.
 */
void fsm_rconfack(f, inp, id, len)
    fsm *f;
    u_char *inp;
    u_char id;
    int len;
{
    FSMDEBUG((stderr, "ppp: fsm_rconfack(%x): Rcvd id %d.\n",
	      f->protocol, id));

    switch (f->state) {
      case LISTEN:
      case CLOSED:
	fsm_sdata(f, TERMACK, id, NULL, 0);
	break;

      case ACKRCVD:
      case REQSENT:
	if (id != f->reqid)		/* Expected id? */
	    break;			/* Nope, toss... */
	if (f->callbacks->ackci &&
	    (*f->callbacks->ackci)(f, inp, len)) /* Good ack? */
	    f->state = ACKRCVD;
	else
	    f->state = REQSENT;		/* Wait for timeout to retransmit */
	break;

      case ACKSENT:
	if (id != f->reqid)		/* Expected id? */
	    break;			/* Nope, toss... */
	if (f->callbacks->ackci &&
	    (*f->callbacks->ackci)(f, inp, len)) { /* Good ack? */
	    UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
	    if (f->callbacks->up)
		(*f->callbacks->up)(f);	/* Inform upper layers */
	    f->state = OPEN;
	}
	else
	    f->state = REQSENT;		/* Wait for timeout to retransmit */
	break;

      case OPEN:
	if (f->callbacks->down)
	    (*f->callbacks->down)(f);	/* Inform upper layers */
	f->state = CLOSED;		/* Only for a moment... */
	fsm_activeopen(f);		/* Restart */
	break;
    }
}


/*
 * fsm_rconfnak - Receive Configure-Nak.
 */
void fsm_rconfnak(f, inp, id, len)
    fsm *f;
    u_char *inp;
    u_char id;
    int len;
{
    FSMDEBUG((stderr, "ppp: fsm_rconfnak(%x): Rcvd id %d.\n",
	      f->protocol, id));

    switch (f->state) {
      case LISTEN:
      case CLOSED:
	fsm_sdata(f, TERMACK, id, NULL, 0);
	break;

      case REQSENT:
      case ACKSENT:
	if (id != f->reqid)		/* Expected id? */
	    break;			/* Nope, toss... */
	if (++f->nakloops > f->maxnakloops) {
	    FSMDEBUG((stderr,
		      "ppp: fsm_rconfnak(%x): Possible CONFNAK loop!\n",
		      f->protocol));
	    break;			/* Break the loop */
	}
	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
	if (f->callbacks->nakci)
	    (*f->callbacks->nakci)(f, inp, len);
	fsm_sconfreq(f);		/* Send Configure-Request */
	TIMEOUT(fsm_timeout, f, f->timeouttime);
	++f->retransmits;
	break;

      case ACKRCVD:
	f->state = REQSENT;		/* Wait for timeout to retransmit */
	break;

      case OPEN:
	if (f->callbacks->down)
	    (*f->callbacks->down)(f);	/* Inform upper layers */
	f->state = CLOSED;		/* Only for a moment... */
	fsm_activeopen(f);		/* Restart */
	break;
    }
}


/*
 * fsm_rconfrej - Receive Configure-Rej.
 */
void fsm_rconfrej(f, inp, id, len)
    fsm *f;
    u_char *inp;
    u_char id;
    int len;
{
    FSMDEBUG((stderr, "ppp: fsm_rconfrej(%x): Rcvd id %d.\n",
	      f->protocol, id));

    switch (f->state) {
      case LISTEN:
      case CLOSED:
	fsm_sdata(f, TERMACK, id, NULL, 0);
	break;

      case REQSENT:
      case ACKSENT:
	if (id != f->reqid)		/* Expected id? */
	    break;			/* Nope, toss... */
	if (++f->nakloops > f->maxnakloops)
	    break;			/* Break the loop */
	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
	if (f->callbacks->rejci)
	    (*f->callbacks->rejci)(f, inp, len);
	fsm_sconfreq(f);		/* Send Configure-Request */
	TIMEOUT(fsm_timeout, f, f->timeouttime);
	++f->retransmits;
	break;

      case ACKRCVD:
	f->state = REQSENT;		/* Wait for timeout to retransmit */
	break;

      case OPEN:
	f->state = CLOSED;		/* Only for a moment... */
	fsm_activeopen(f);		/* Restart */
	break;
    }
}


/*
 * fsm_rtermreq - Receive Terminate-Req.
 */
void fsm_rtermreq(f, id)
    fsm *f;
    u_char id;
{
    FSMDEBUG((stderr, "ppp: fsm_rtermreq(%x): Rcvd id %d.\n",
	      f->protocol, id));

    fsm_sdata(f, TERMACK, id, NULL, 0);
    switch (f->state) {
      case ACKRCVD:
      case ACKSENT:
	f->state = REQSENT;		/* Start over but keep trying */
	break;

      case OPEN:
	if (f->callbacks->down)
	    (*f->callbacks->down)(f);	/* Inform upper layers */
	f->state = CLOSED;
	if (f->callbacks->closed)
	    (*f->callbacks->closed)(f);	/* Exit/restart/etc. */
	break;
    }
}


/*
 * fsm_rtermack - Receive Terminate-Ack.
 */
void fsm_rtermack(f)
    fsm *f;
{
    FSMDEBUG((stderr, "ppp: fsm_rtermack(%x).\n", f->protocol));

    switch (f->state) {
      case OPEN:
	if (f->callbacks->down)
	    (*f->callbacks->down)(f);	/* Inform upper layers */
	f->state = CLOSED;
	if (f->callbacks->closed)
	    (*f->callbacks->closed)(f);	/* Exit/restart/etc. */
	break;

      case TERMSENT:
	UNTIMEOUT(fsm_timeout, f);	/* Cancel timeout */
	f->state = CLOSED;
	if (f->callbacks->closed)
	    (*f->callbacks->closed)(f);	/* Exit/restart/etc. */
	break;
    }
}


/*
 * fsm_rcoderej - Receive an Code-Reject.
 */
void fsm_rcoderej(f, inp, len)
    fsm *f;
    u_char *inp;
    int len;
{
    u_char code;

    FSMDEBUG((stderr, "ppp: fsm_rcoderej(%x).\n", f->protocol));

    if (len < sizeof (u_char)) {
	FSMDEBUG((stderr,
		  "ppp: fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
	return;
    }
    GETCHAR(code, inp);
    FSMDEBUG((stderr,
	      "ppp: fsm_rcoderej: Rcvd Code-Reject for code %d!\n",
	      code));
}


/*
 * fsm_rprotrej - Receive an Protocol-Reject.
 *
 * Figure out which protocol is rejected and inform it.
 */
void fsm_rprotrej(f, inp, len)
    fsm *f;
    u_char *inp;
    int len;
{
    u_short prot;

    FSMDEBUG((stderr, "ppp: fsm_rprotrej.\n"));

    if (len < sizeof (u_short)) {
	FSMDEBUG((stderr,
		  "ppp: fsm_rprotrej: Rcvd short Protocol-Reject packet!\n"));
	return;
    }
    if (f->protocol != LCP) {		/* Only valid for LCP */
	FSMDEBUG((stderr,
		  "ppp: fsm_rprotrej: Rcvd non-LCP Protocol-Reject!\n"));
	return;
    }

    GETSHORT(prot, inp);

    FSMDEBUG((stderr,
	      "ppp: fsm_rprotrej: Rcvd Protocol-Reject packet for %x!\n",
	      prot));
    DEMUXPROTREJ(f->unit, prot);	/* Inform protocol */
}


/*
 * fsm_sconfreq - Send a Configure-Request.
 */
void fsm_sconfreq(f)
    fsm *f;
{
    PACKET *outpacket;
    u_char *outp;
    int outlen;

    outlen = HEADERLEN + (f->callbacks->cilen ? (*f->callbacks->cilen)(f) : 0);
    /* XXX Adjust outlen to MTU */
    outpacket = PACKET_ALLOC(outlen);
    outp = PACKET_DATA(outpacket);

    PUTCHAR(CONFREQ, outp);
    PUTCHAR(f->reqid = ++f->id, outp);
    PUTSHORT(outlen, outp);
    if (f->callbacks->cilen && f->callbacks->addci)
	(*f->callbacks->addci)(f, outp);
    OUTPUT(f->unit, outpacket, outlen, f->protocol);

    FSMDEBUG((stderr, "ppp: fsm_sconfreq(%x): Sent id %d.\n",
	      f->protocol, f->reqid));
}


/*
 * fsm_sdata - Send some data.
 *
 * Used for Terminate-Request, Terminate-Ack, Code-Reject, Protocol-Reject,
 * Echo-Request, and Discard-Request.
 */
void fsm_sdata(f, code, id, data, datalen)
    fsm *f;
    u_char code, id;
    u_char *data;
    int datalen;
{
    PACKET *outpacket;
    u_char *outp;
    int outlen;

    /* Adjust length to be smaller than MTU */
    if (datalen > MTU - HEADERLEN)
	datalen = MTU - HEADERLEN;
    outlen = datalen + HEADERLEN;
    outpacket = PACKET_ALLOC(outlen);
    outp = PACKET_DATA(outpacket);

    PUTCHAR(code, outp);
    PUTCHAR(id, outp);
    PUTSHORT(outlen, outp);
    if (datalen)
	BCOPY(data, outp, datalen);
    OUTPUT(f->unit, outpacket, outlen, f->protocol);

    FSMDEBUG((stderr, "ppp: fsm_sdata(%x): Sent code %d, id %d.\n",
	      f->protocol, code, id));
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.