FreeBSD-5.3/usr.sbin/atm/scspd/scsp_cafsm.c

Compare this file to the similar file:
Show the results in this format:

/*
 *
 * ===================================
 * HARP  |  Host ATM Research Platform
 * ===================================
 *
 *
 * This Host ATM Research Platform ("HARP") file (the "Software") is
 * made available by Network Computing Services, Inc. ("NetworkCS")
 * "AS IS".  NetworkCS does not provide maintenance, improvements or
 * support of any kind.
 *
 * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
 * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
 * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
 * In no event shall NetworkCS be responsible for any damages, including
 * but not limited to consequential damages, arising from or relating to
 * any use of the Software or related support.
 *
 * Copyright 1994-1998 Network Computing Services, Inc.
 *
 * Copies of this Software may be made, however, the above copyright
 * notice must be reproduced on all copies.
 *
 *	@(#) $FreeBSD: src/usr.sbin/atm/scspd/scsp_cafsm.c,v 1.5 2002/10/10 00:32:55 alfred Exp $
 *
 */


/*
 * Server Cache Synchronization Protocol (SCSP) Support
 * ----------------------------------------------------
 *
 * Cache Alignment finite state machine
 *
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_ioctl.h>

#include <errno.h>
#include <libatm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include "scsp_msg.h"
#include "scsp_if.h"
#include "scsp_var.h"

#ifndef lint
__RCSID("@(#) $FreeBSD: src/usr.sbin/atm/scspd/scsp_cafsm.c,v 1.5 2002/10/10 00:32:55 alfred Exp $");
#endif


/*
 * CA FSM actions
 */
#define	CA_ACTION_CNT	20
int	scsp_ca_act_00(Scsp_dcs *, void *);
int	scsp_ca_act_01(Scsp_dcs *, void *);
int	scsp_ca_act_02(Scsp_dcs *, void *);
int	scsp_ca_act_03(Scsp_dcs *, void *);
int	scsp_ca_act_04(Scsp_dcs *, void *);
int	scsp_ca_act_05(Scsp_dcs *, void *);
int	scsp_ca_act_06(Scsp_dcs *, void *);
int	scsp_ca_act_07(Scsp_dcs *, void *);
int	scsp_ca_act_08(Scsp_dcs *, void *);
int	scsp_ca_act_09(Scsp_dcs *, void *);
int	scsp_ca_act_10(Scsp_dcs *, void *);
int	scsp_ca_act_11(Scsp_dcs *, void *);
int	scsp_ca_act_12(Scsp_dcs *, void *);
int	scsp_ca_act_13(Scsp_dcs *, void *);
int	scsp_ca_act_14(Scsp_dcs *, void *);
int	scsp_ca_act_15(Scsp_dcs *, void *);
int	scsp_ca_act_16(Scsp_dcs *, void *);
int	scsp_ca_act_17(Scsp_dcs *, void *);
int	scsp_ca_act_18(Scsp_dcs *, void *);
int	scsp_ca_act_19(Scsp_dcs *, void *);

static int (*scsp_ca_act_vec[CA_ACTION_CNT])() = {
	scsp_ca_act_00,
	scsp_ca_act_01,
	scsp_ca_act_02,
	scsp_ca_act_03,
	scsp_ca_act_04,
	scsp_ca_act_05,
	scsp_ca_act_06,
	scsp_ca_act_07,
	scsp_ca_act_08,
	scsp_ca_act_09,
	scsp_ca_act_10,
	scsp_ca_act_11,
	scsp_ca_act_12,
	scsp_ca_act_13,
	scsp_ca_act_14,
	scsp_ca_act_15,
	scsp_ca_act_16,
	scsp_ca_act_17,
	scsp_ca_act_18,
	scsp_ca_act_19
};

/*
 * CA FSM state table
 */
static int ca_state_table[SCSP_CAFSM_EVENT_CNT][SCSP_CAFSM_STATE_CNT] = {
	/* 0   1   2   3   4   5	      */
	{  1,  1,  1,  1,  1,  1 },	/*  0 */
	{  2,  2,  2,  2,  2,  2 },	/*  1 */
	{  0,  3,  4,  5, 15, 15 },	/*  2 */
	{  0, 17, 17, 17,  7,  7 },	/*  3 */
	{  0, 17, 17, 17,  8,  8 },	/*  4 */
	{  0, 17, 17, 17, 10, 10 },	/*  5 */
	{  0,  6,  6,  0,  9,  9 },	/*  6 */
	{  0,  0,  0,  0, 12, 12 },	/*  7 */
	{  0,  0,  0,  0, 13, 13 },	/*  8 */
	{ 18, 14, 14, 14, 11, 11 },	/*  9 */
	{  0, 19,  0,  0, 16, 16 },	/* 10 */
};


/*
 * Cache Alignment finite state machine
 *
 * Arguments:
 *	dcsp	pointer to a DCS control block for the neighbor
 *	event	the event which has occurred
 *	p	pointer to further parameter, if there is one
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_cafsm(dcsp, event, p)
	Scsp_dcs	*dcsp;
	int		event;
	void		*p;
{
	int	action, rc, state;

	/*
	 * Select an action from the state table
	 */
	state = dcsp->sd_ca_state;
	action = ca_state_table[event][state];
	if (scsp_trace_mode & SCSP_TRACE_CAFSM) {
		scsp_trace("CAFSM: state=%d, event=%d, action=%d\n",
				state, event, action);
	}
	if (action >= CA_ACTION_CNT || action < 0) {
		scsp_log(LOG_ERR, "CA FSM--invalid action state=%d, event=%d, action=%d",
				state, event, action);
		abort();
	}

	/*
	 * Perform the selected action
	 */
	rc = scsp_ca_act_vec[action](dcsp, p);

	return(rc);
}


/*
 * CA finite state machine action 0
 * Unexpected action -- log an error message and go to Master/Slave
 * Negotiation.  The unexpected action is probably from a protocol
 * error.
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	ignored
 *
 * Returns:
 *	EOPNOTSUPP	always returns EOPNOTSUPP
 *
 */
int
scsp_ca_act_00(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int	rc;

	/*
	 * Log an error message
	 */
	scsp_log(LOG_ERR, "CA FSM error--unexpected action, state=%d",
			dcsp->sd_ca_state);

	/*
	 * Set the new state
	 */
	dcsp->sd_ca_state = SCSP_CAFSM_NEG;

	/*
	 * Clear out the DCS block
	 */
	scsp_dcs_cleanup(dcsp);

	/*
	 * Notify the client I/F FSM
	 */
	rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_DOWN, (Scsp_msg *)0,
			(Scsp_if_msg *)0);

	return(rc);
}


/*
 * CA finite state machine action 1
 * Hello FSM has reached Bidirectional state -- go to Master/Slave
 * Negotiation state, make a copy of the client's cache, send first CA
 * message.
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	ignored
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_01(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		i, rc;
	Scsp_cse	*csep, *dupp;

	/*
	 * Set the new state
	 */
	dcsp->sd_ca_state = SCSP_CAFSM_NEG;

	/*
	 * Make a copy of client's cache entries for cache alignment
	 */
	for (i = 0; i < SCSP_HASHSZ; i++) {
		for (csep = dcsp->sd_server->ss_cache[i];
				csep; csep = csep->sc_next) {
			dupp = scsp_dup_cse(csep);
			LINK2TAIL(dupp, Scsp_cse, dcsp->sd_ca_csas,
						sc_next);
		}
	}

	/*
	 * Select an initial sequence number
	 */
	dcsp->sd_ca_seq = (int)time((time_t *)0);

	/*
	 * Send a CA message
	 */
	rc = scsp_send_ca(dcsp);
	if (rc == 0) {
		HARP_TIMER(&dcsp->sd_ca_rexmt_t, dcsp->sd_ca_rexmt_int,
				scsp_ca_retran_timeout);
	}

	return(rc);
}


/*
 * CA finite state machine action 2
 * Hello FSM has gone down -- go to Down state
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	ignored
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_02(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc;

	/*
	 * Set the new state
	 */
	dcsp->sd_ca_state = SCSP_CAFSM_DOWN;

	/*
	 * Clear out the DCS block
	 */
	scsp_dcs_cleanup(dcsp);

	/*
	 * Notify the client I/F FSM
	 */
	rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_DOWN, (Scsp_msg *)0,
			(Scsp_if_msg *)0);

	return(rc);
}


/*
 * CA finite state machine action 3
 * CA message received -- select Cache Summarize Master or Slave state
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to received message
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_03(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc = 0;
	Scsp_msg	*msg = (Scsp_msg *)p;

	/*
	 * Check for slave role for LS
	 */
	if (msg->sc_ca->ca_m &&
			msg->sc_ca->ca_i &&
			msg->sc_ca->ca_o &&
			msg->sc_ca->ca_mcp.rec_cnt == 0 &&
			scsp_cmp_id(&msg->sc_ca->ca_mcp.sid,
				&msg->sc_ca->ca_mcp.rid) > 0) {

		/*
		 * Stop the retransmit timer
		 */
		HARP_CANCEL(&dcsp->sd_ca_rexmt_t);

		/*
		 * Set the new state
		 */
		dcsp->sd_ca_state = SCSP_CAFSM_SLAVE;
		(void)scsp_cfsm(dcsp, SCSP_CIFSM_CA_SUMM,
				(Scsp_msg *)0, (Scsp_if_msg *)0);

		/*
		 * Save the master's sequence number
		 */
		dcsp->sd_ca_seq = msg->sc_ca->ca_seq;

		/*
		 * Send a CA message
		 */
		rc = scsp_send_ca(dcsp);
	} else
	/*
	 * Check for master role for LS
	 */
	if (!msg->sc_ca->ca_m &&
			!msg->sc_ca->ca_i &&
			scsp_cmp_id(&msg->sc_ca->ca_mcp.sid,
				&msg->sc_ca->ca_mcp.rid) < 0) {
		/*
		 * Stop the retransmit timer
		 */
		HARP_CANCEL(&dcsp->sd_ca_rexmt_t);

		/*
		 * Set the new state
		 */
		dcsp->sd_ca_state = SCSP_CAFSM_MASTER;
		rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_SUMM,
				(Scsp_msg *)0, (Scsp_if_msg *)0);

		/*
		 * Process the CA message
		 */
		scsp_process_ca(dcsp, msg->sc_ca);

		/*
		 * Increment the sequence number
		 */
		dcsp->sd_ca_seq++;

		/*
		 * Send a CA in reply
		 */
		rc = scsp_send_ca(dcsp);
		if (rc == 0) {
			HARP_TIMER(&dcsp->sd_ca_rexmt_t,
					dcsp->sd_ca_rexmt_int,
					scsp_ca_retran_timeout);
		}
	} else {
		/*
		 * Ignore the message, go to Master/Slave Negotiation
		 */
		dcsp->sd_ca_state = SCSP_CAFSM_NEG;
	}

	return(rc);
}


/*
 * CA finite state machine action 4
 * CA message received while in Cache Summarize Master state -- process
 * CA message
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to received message
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_04(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc = 0;
	Scsp_msg	*msg = (Scsp_msg *)p;

	/*
	 * If the other side thinks he's the master, or if the
	 * initialization bit is set, or if the message is out
	 * of sequence, go back to Master/Slave Negotiation state
	 */
	if (msg->sc_ca->ca_m || msg->sc_ca->ca_i ||
			msg->sc_ca->ca_seq < dcsp->sd_ca_seq - 1 ||
			msg->sc_ca->ca_seq > dcsp->sd_ca_seq) {
		HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
		dcsp->sd_ca_state = SCSP_CAFSM_NEG;
		scsp_dcs_cleanup(dcsp);
		return(scsp_ca_act_01(dcsp, (Scsp_msg *)0));
	}

	/*
	 * Ignore any duplicate messages
	 */
	if (msg->sc_ca->ca_seq == dcsp->sd_ca_seq - 1) {
		return(0);
	}

	/*
	 * Stop the retransmission timer
	 */
	HARP_CANCEL(&dcsp->sd_ca_rexmt_t);

	/*
	 * Process the CA message
	 */
	scsp_process_ca(dcsp, msg->sc_ca);

	/*
	 * Increment the CA sequence number
	 */
	dcsp->sd_ca_seq++;

	/*
	 * If we have no more CSAS records to send and the slave sent
	 * a message with the 'O' bit off, we're done with Summarize
	 * state
	 */
	if (!dcsp->sd_ca_csas && !msg->sc_ca->ca_o) {
		/*
		 * Free any CA message saved for retransmission
		 */
		if (dcsp->sd_ca_rexmt_msg) {
			scsp_free_msg(dcsp->sd_ca_rexmt_msg);
			dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
		}

		/*
		 * If the CRL is empty, we go directly to Aligned state;
		 * otherwise, we go to Update Cache and send a CSUS
		 */
		if (!dcsp->sd_crl) {
			/*
			 * Go to Aligned state
			 */
			dcsp->sd_ca_state = SCSP_CAFSM_ALIGNED;
			rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_ALIGN,
					(Scsp_msg *)0,
					(Scsp_if_msg *)0);
		} else {
			/*
			 * Go to Cache Update state
			 */
			dcsp->sd_ca_state = SCSP_CAFSM_UPDATE;
			(void)scsp_cfsm(dcsp, SCSP_CIFSM_CA_UPD,
					(Scsp_msg *)0,
					(Scsp_if_msg *)0);
			rc = scsp_send_csus(dcsp);
		}
	} else {
		/*
		 * There are more CSAS records to be exchanged--
		 * continue the cache exchange
		 */
		rc = scsp_send_ca(dcsp);
	}

	return(rc);
}


/*
 * CA finite state machine action 5
 * CA message received while in Cache Summarize Slave state -- process
 * CA message
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to received message
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_05(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc = 0;
	Scsp_msg	*msg = (Scsp_msg *)p;

	/*
	 * If the other side thinks we're the master, or if the
	 * initialization bit is set, or if the message is out
	 * of sequence, go back to Master/Slave Negotiation state
	 */
	if (!msg->sc_ca->ca_m || msg->sc_ca->ca_i ||
			msg->sc_ca->ca_seq < dcsp->sd_ca_seq ||
			msg->sc_ca->ca_seq > dcsp->sd_ca_seq + 1) {
		HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
		dcsp->sd_ca_state = SCSP_CAFSM_NEG;
		scsp_dcs_cleanup(dcsp);
		return(scsp_ca_act_01(dcsp, (Scsp_msg *)0));
	}

	/*
	 * If this is a duplicate, retransmit the last message
	 */
	if (msg->sc_ca->ca_seq == dcsp->sd_ca_seq) {
		if (dcsp->sd_ca_rexmt_msg) {
			rc = scsp_send_msg(dcsp, dcsp->sd_ca_rexmt_msg);
			if (rc == 0) {
				HARP_TIMER(&dcsp->sd_ca_rexmt_t,
						dcsp->sd_ca_rexmt_int,
						scsp_ca_retran_timeout);
			}
		}
		return(rc);
	}

	/*
	 * Free the last CA message
	 */
	if (dcsp->sd_ca_rexmt_msg) {
		scsp_free_msg(dcsp->sd_ca_rexmt_msg);
		dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
	}

	/*
	 * Process the CA message
	 */
	scsp_process_ca(dcsp, msg->sc_ca);

	/*
	 * Increment the CA sequence number
	 */
	dcsp->sd_ca_seq++;

	/*
	 * Answer the CA message
	 */
	rc = scsp_send_ca(dcsp);
	if (rc)
		return(rc);

	/*
	 * If we're done sending CSAS records and the other side is,
	 * too, we're done with Summarize state
	 */
	if (!dcsp->sd_ca_csas && !msg->sc_ca->ca_o) {
		/*
		 * If the CRL is empty, we go directly to Aligned state;
		 * otherwise, we go to Update Cache and send a CSUS
		 */
		if (!dcsp->sd_crl) {
			/*
			 * Go to Aligned state
			 */
			dcsp->sd_ca_state = SCSP_CAFSM_ALIGNED;
			rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_ALIGN,
					(Scsp_msg *)0,
					(Scsp_if_msg *)0);
		} else {
			/*
			 * Go to Cache Update state
			 */
			dcsp->sd_ca_state = SCSP_CAFSM_UPDATE;
			HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
			HARP_TIMER(&dcsp->sd_ca_rexmt_t,
					dcsp->sd_ca_rexmt_int,
					scsp_ca_retran_timeout);
			(void)scsp_cfsm(dcsp, SCSP_CIFSM_CA_UPD,
					(Scsp_msg *)0,
					(Scsp_if_msg *)0);
			rc = scsp_send_csus(dcsp);
		}
	}

	return(rc);
}


/*
 * CA finite state machine action 6
 * Retransmit timer expired -- retransmit last CA message
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	ignored
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_06(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int	rc;

	/*
	 * Resend the CA message
	 */
	rc = scsp_send_msg(dcsp, dcsp->sd_ca_rexmt_msg);

	/*
	 * Restart the retransmit timer
	 */
	if (rc == 0) {
		HARP_TIMER(&dcsp->sd_ca_rexmt_t, dcsp->sd_ca_rexmt_int,
				scsp_ca_retran_timeout);
	}

	return(rc);
}


/*
 * CA finite state machine action 7
 * CSU Solicit received -- send it to the client interface FSM
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to received message
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_07(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc;
	Scsp_msg	*msg = (Scsp_msg *)p;

	/*
	 * Cancel the CA retransmit timer and free any CA message
	 * saved for retransmission
	 */
	if (dcsp->sd_ca_rexmt_msg) {
		HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
		scsp_free_msg(dcsp->sd_ca_rexmt_msg);
		dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
	}

	/*
	 * Pass the CSUS to the client interface FSM
	 */
	rc = scsp_cfsm(dcsp, SCSP_CIFSM_CSU_SOL, msg,
			(Scsp_if_msg *)0);

	return(rc);
}


/*
 * CA finite state machine action 8
 * CSU Request received -- pass it to the client interface FSM
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to received message
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_08(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc;
	Scsp_msg	*msg = (Scsp_msg *)p;
	Scsp_csa	*csap;

	/*
	 * Check whether this messages answers a CSUS
	 */
	scsp_csus_ack(dcsp, msg);

	/*
	 * If all CSAs requestd in CSUS messages have been
	 * received, the cache is aligned, so go to Aligned State
	 */
	if (!dcsp->sd_csus_rexmt_msg && !dcsp->sd_crl &&
			dcsp->sd_ca_state != SCSP_CAFSM_ALIGNED) {
		dcsp->sd_ca_state = SCSP_CAFSM_ALIGNED;
		rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_ALIGN,
				(Scsp_msg *)0, (Scsp_if_msg *)0);
	}

	/*
	 * Pass the CSU Req to the client interface FSM
	 */
	rc = scsp_cfsm(dcsp, SCSP_CIFSM_CSU_REQ, msg,
			(Scsp_if_msg *)0);

	/*
	 * Move the CSA chain from the message to the list of
	 * requests that need acknowledgements
	 */
	for (csap = msg->sc_csu_msg->csu_csa_rec; csap;
			csap = csap->next) {
		LINK2TAIL(csap, Scsp_csa, dcsp->sd_csu_ack_pend, next);
	}
	msg->sc_csu_msg->csu_csa_rec = (Scsp_csa *)0;

	return(rc);
}


/*
 * CA finite state machine action 9
 * CA Retransmit timer expired in Update Cache or Aligned state--free
 * the saved CA message
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	ignored
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_09(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	/*
	 * Free any CA message saved for retransmission
	 */
	if (dcsp->sd_ca_rexmt_msg) {
		scsp_free_msg(dcsp->sd_ca_rexmt_msg);
		dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
	}

	return(0);
}


/*
 * CA finite state machine action 10
 * CSU Reply received -- Process the message
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to received message
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_10(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc = 0;
	Scsp_msg	*msg = (Scsp_msg *)p;
	Scsp_csu_rexmt	*rxp, *next_rxp;
	Scsp_csa	*csap, *next_csap, *mcp;

	/*
	 * Dequeue acknowledged CSAs.  For each CSAS in the received
	 * message, find the corresponding CSA on the CSU Request
	 * retransmit queue.  Remove the CSA from the queue;  if this
	 * results in the retransmit queue entry being empty, delete
	 * the entry.  If the DCS has a newer CSA, send a CSUS to
	 * request it.
	 *
	 * Caution--potentially confusing lack of indentation ahead.
	 */
	for (mcp = msg->sc_csu_msg->csu_csa_rec; mcp;
			mcp = mcp->next) {
	for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = next_rxp) {
	next_rxp = rxp->sr_next;
	for (csap = rxp->sr_csa; csap; csap = next_csap) {
		next_csap = csap->next;
		if (scsp_cmp_key(&csap->key, &mcp->key) ||
				scsp_cmp_id(&csap->oid, &mcp->oid))
			continue;
		/*
		 * Found a CSA whose key and ID are equal to
		 * those in the CSU Reply
		 */
		if (csap->seq == mcp->seq) {
			/*
			 * The queued seq no is equal to the
			 * received seq no--the CSA is acknowledged
			 */
			UNLINK(csap, Scsp_csa, rxp->sr_csa, next);
			SCSP_FREE_CSA(csap);
		} else if (csap->seq < mcp->seq) {
			/*
			 * Queued seq no is less than received.
			 * We must dequeue the CSA and send a
			 * CSUS to request the more-up-to-date
			 * cache entry.
			 */
			UNLINK(mcp, Scsp_csa,
					msg->sc_csu_msg->csu_csa_rec,
					next);
			LINK2TAIL(mcp, Scsp_csa, dcsp->sd_crl, next);
			UNLINK(csap, Scsp_csa, rxp->sr_csa, next);
			SCSP_FREE_CSA(csap);
			if (!dcsp->sd_csus_rexmt_msg) {
				rc = scsp_send_csus(dcsp);
				if (rc) {
					return(rc);
				}
			}
		}
			/*
			 * Queued seq no is greater than
			 * received.  Ignore the received CSAS.
			 */

		/*
		 * If the retransmission block is empty, stop the
		 * timer and free it
		 */
		if (!rxp->sr_csa) {
			HARP_CANCEL(&rxp->sr_t);
			UNLINK(rxp, Scsp_csu_rexmt,
					dcsp->sd_csu_rexmt, sr_next);
			free(rxp);
		}

		break;
	} /* for (csap = ... */
	} /* for (rxp = ... */
	} /* for (mcp = ... */

	return(rc);
}


/*
 * CA finite state machine action 11
 * Updated cache entry -- update the summary cache and send a
 * CSU Request
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to CSA describing new cache entry
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_11(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc, state;
	Scsp_csa	*csap =  (Scsp_csa *)p;
	Scsp_cse	*csep;

	/*
	 * Get the state of the CSA
	 */
	switch(dcsp->sd_server->ss_pid) {
	case SCSP_PROTO_ATMARP:
		state = csap->atmarp_data->sa_state;
		break;
	default:
		SCSP_FREE_CSA(csap);
		return(EINVAL);
	}

	if (state < SCSP_ASTATE_NEW || state > SCSP_ASTATE_DEL) {
		SCSP_FREE_CSA(csap);
		return(EINVAL);
	}

	/*
	 * Look up the cache summary entry for the CSA
	 */
	SCSP_LOOKUP(dcsp->sd_server, &csap->key, csep);

	/*
	 * Process ATMARP entries
	 */
	if (dcsp->sd_server->ss_pid == SCSP_PROTO_ATMARP) {
		switch(state) {
		case SCSP_ASTATE_NEW:
		case SCSP_ASTATE_UPD:
			/*
			 * Add the entry if we don't have it already
			 */
			if (!csep) {
				csep = calloc(1, sizeof(Scsp_cse));
				if (csep == NULL)
					scsp_mem_err("scsp_ca_act_11: sizeof(Scsp_cse)");
				
				csep->sc_key = csap->key;
				SCSP_ADD(dcsp->sd_server, csep);
			}

			/*
			 * Update the cache summary entry
			 */
			csep->sc_seq = csap->seq;
			csep->sc_oid = csap->oid;
			break;
		case SCSP_ASTATE_DEL:
			/*
			 * Delete any entry, but don't send the
			 * delete to the DCS
			 */
			if (csep) {
				SCSP_DELETE(dcsp->sd_server, csep);
				free(csep);
			}

			SCSP_FREE_CSA(csap);
			return(0);
		}
	}

	/*
	 * Send the CSA in a CSU Request
	 */
	csap->trans_ct = 0;
	rc = scsp_send_csu_req(dcsp, csap);

	return(rc);
}


/*
 * CA finite state machine action 12
 * CSUS retransmit timer expired--send a CSUS with any pending CSA
 * records
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	ignored
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_12(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int	rc;

	rc = scsp_send_csus(dcsp);

	return(rc);
}


/*
 * CA finite state machine action 13
 * CSU retransmit timer fired in Update or Aligned state--
 * retransmit CSU Req
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to retransmission block whose timer fired
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_13(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc = 0;
	Scsp_csu_rexmt	*rxp = (Scsp_csu_rexmt *)p;
	Scsp_csa	*csap, *csap1, *next_csap;

	/*
	 * Unlink and free the retransmit request block
	 */
	csap = rxp->sr_csa;
	UNLINK(rxp, Scsp_csu_rexmt, dcsp->sd_csu_rexmt, sr_next);
	free(rxp);

	/*
	 * Increment the transmission count for the CSAs in the request
	 */
	for (csap1 = csap; csap1; csap1 = next_csap) {
		next_csap = csap1->next;
		csap1->trans_ct++;
		if (csap1->trans_ct >= dcsp->sd_csu_rexmt_max) {
			/*
			 * We've already sent this as many times as
			 * the limit allows.  Drop this CSA.
			 */
			UNLINK(csap1, Scsp_csa, csap, next);
			SCSP_FREE_CSA(csap1);
		}
	}

	/*
	 * Send another CSU Request with the CSA list, if it isn't
	 * empty now
	 */
	if (csap) {
		rc = scsp_send_csu_req(dcsp, csap);
	}

	return(rc);
}


/*
 * CA finite state machine action 14
 * Updated cache entry in Master/Slave Negotiation, Master, or
 * Slave state--add entry to cache and CSA list
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to new cache summary entry
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_14(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	Scsp_csa        *csap =  (Scsp_csa *)p;
	Scsp_cse	*csep, *csep1;

	/*
	 * Check to see whether we already have this
	 */
	SCSP_LOOKUP(dcsp->sd_server, &csap->key, csep);

	/*
	 * If we don't already have it and it's not being deleted,
	 * build a new cache summary entry
	 */
	if (!csep && !csap->null) {
		/*
		 * Get memory for a new entry
		 */
		csep = calloc(1, sizeof(Scsp_cse));
		if (csep == NULL)
			scsp_mem_err("scsp_ca_act_14: sizeof(Scsp_cse)");

		/*
		 * Fill out the new cache entry
		 */
		csep->sc_seq = csap->seq;
		csep->sc_key = csap->key;
		csep->sc_oid = csap->oid;

		/*
		 * Duplicate the new cache entry
		 */
		csep1 = scsp_dup_cse(csep);

		/*
		 * Add entry to the summary cache and the CSAS list
		 */
		SCSP_ADD(dcsp->sd_server, csep);
		LINK2TAIL(csep1, Scsp_cse, dcsp->sd_ca_csas, sc_next);
	} else {
		/*
		 * We already have the entry.  Find it on the CSAS
		 * list.
		 */
		for (csep1 = dcsp->sd_ca_csas; csep1;
				csep1 = csep1->sc_next) {
			if (scsp_cmp_key(&csep->sc_key,
					&csep1->sc_key) == 0)
				break;
		}

		/*
		 * Update or delete the entry
		 */
		if (csap->null) {
			/*
			 * The null flag is set--delete the entry
			 */
			SCSP_DELETE(dcsp->sd_server, csep);
			free(csep);
			if (csep1) {
				UNLINK(csep1, Scsp_cse,
						dcsp->sd_ca_csas,
						sc_next);
				free(csep1);
			}
		} else {
			/*
			 * Update the entry
			 */
			csep->sc_seq = csap->seq;
			csep->sc_oid = csap->oid;
			if (!csep1) {
				csep1 = scsp_dup_cse(csep);
				LINK2TAIL(csep1, Scsp_cse,
					dcsp->sd_ca_csas, sc_next);
			} else {
				csep1->sc_seq = csap->seq;
				csep1->sc_oid = csap->oid;
			}
		}
	}

	return(0);
}


/*
 * CA finite state machine action 15
 * CA message received in Update Cache state--if we have a saved CA
 * message, retransmit it;  otherwise, go to Master/Slave Negotiation
 * state
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	ignored
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_15(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		rc;
	Scsp_msg	*msg = (Scsp_msg *)p;

	/*
	 * If we don't have a saved CA message, or the sequence no. in
	 * the received message isn't right, fall back to Master/Slave
	 * Negotiation state
	 */
	if (!dcsp->sd_ca_rexmt_msg ||
			msg->sc_ca->ca_seq != dcsp->sd_ca_seq) {
		dcsp->sd_ca_state = SCSP_CAFSM_NEG;
		scsp_dcs_cleanup(dcsp);
		rc = scsp_ca_act_01(dcsp, (Scsp_msg *)0);
	} else {
		/*
		 * Retransmit the saved CA message and reset the
		 * CA timer
		 */
		rc = scsp_send_msg(dcsp, dcsp->sd_ca_rexmt_msg);
		if (rc == 0) {
			HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
			HARP_TIMER(&dcsp->sd_ca_rexmt_t,
					dcsp->sd_ca_rexmt_int,
					scsp_ca_retran_timeout);
		}
	}

	return(rc);
}


/*
 * CA finite state machine action 16
 * Update Response received from client in Update Cache or Aligned
 * state.  Move the acknowledged CSA to the acknowledged queue.  If
 * the list of CSAs pending acknowledgement is empty, send a CSU
 * Reply.
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to message from client
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_16(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	int		found, rc = 0;
	Scsp_if_msg	*cmsg = (Scsp_if_msg *)p;
	Scsp_csa	*csap;

	/*
	 * Find the acknowledged CSA
	 */      
	for (csap = dcsp->sd_csu_ack_pend, found = 0; csap && !found;
			csap = csap->next) {
		switch (dcsp->sd_server->ss_pid) {
		case SCSP_PROTO_ATMARP:
			found = ((scsp_cmp_key(&csap->key,
				&cmsg->si_atmarp.sa_key) == 0) &&
				(scsp_cmp_id(&csap->oid,
				&cmsg->si_atmarp.sa_oid) == 0));
			break;
		default: 
			/*
			 * Protocol not implemented
			 */
			return(EPROTONOSUPPORT);
		}
		if (found)
			break;
	}

	if (!found) {
		if (scsp_trace_mode & SCSP_TRACE_CAFSM) {
			scsp_trace("scsp_ca_act_16: can't find CSA entry for Update Response\n");
		}
		return(0);
	}

	if (cmsg->si_rc == SCSP_RSP_OK) {
		/*
		 * The server accepted the cache entry
		 */

		/*
		 * Update SCSP's cache
		 */
		scsp_update_cache(dcsp, csap);

		/*
		 * Send this CSA to any other DCSs in the server group
		 */
		rc = scsp_propagate_csa(dcsp, csap);
	}

	/*
	 * Move the CSA from the ACK pending queue to the
	 * acknowledged queue
	 */
	UNLINK(csap, Scsp_csa, dcsp->sd_csu_ack_pend, next);
	LINK2TAIL(csap, Scsp_csa, dcsp->sd_csu_ack, next);
	if (!dcsp->sd_csu_ack_pend) {
		/*
		 * ACK pending list is empty--send a CSU Reply
		 */
		csap = dcsp->sd_csu_ack;
		dcsp->sd_csu_ack = (Scsp_csa *)0;
		rc = scsp_send_csu_reply(dcsp, csap);
	}

	return(rc);
}


/*
 * CA finite state machine action 17
 * Ignore an event.
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	ignored
 *
 * Returns:
 *	always returns 0
 *
 */
int
scsp_ca_act_17(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	return(0);
}


/*
 * CA finite state machine action 18
 * Updated cache entry in Down state--add entry to summary cache
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to new cache summary entry
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_18(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	Scsp_csa        *csap =  (Scsp_csa *)p;

	/*
	 * Update the cache as appropriate
	 */
	scsp_update_cache(dcsp, csap);

	return(0);
}


/*
 * CA finite state machine action 19
 * Update Response received from client in Master/Slave Negotiation
 * state.  Update the cache as appropriate.
 *
 * Arguments:
 *	dcsp	pointer to DCS control block
 *	p	pointer to message from client
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_ca_act_19(dcsp, p)
	Scsp_dcs	*dcsp;
	void		*p;
{
	Scsp_if_msg	*cmsg = (Scsp_if_msg *)p;
	Scsp_csa	*csap;

	/*
	 * Ignore the message if the client rejected the update
	 */
	if (cmsg->si_rc != SCSP_RSP_OK) {
		return(0);
	}

	/*
	 * Create a CSAS from the client's update
	 */      
	csap = calloc(1, sizeof(Scsp_csa));
	if (csap == NULL)
		scsp_mem_err("scsp_ca_act_19: sizeof(Scsp_csa)");
	csap->hops = 1;
	switch (dcsp->sd_server->ss_pid) {
	case SCSP_PROTO_ATMARP:
		csap->null = cmsg->si_atmarp.sa_state ==
				SCSP_ASTATE_DEL;
		csap->seq = cmsg->si_atmarp.sa_seq;
		csap->key = cmsg->si_atmarp.sa_key;
		csap->oid = cmsg->si_atmarp.sa_oid;
		break;
	default:
		return(EINVAL);
	}

	/*
	 * Update SCSP's cache
	 */
	scsp_update_cache(dcsp, csap);

	return(0);
}