FreeBSD-5.3/usr.sbin/atm/scspd/scsp_subr.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_subr.c,v 1.5 2003/07/29 13:40:52 harti Exp $
 *
 */


/*
 * Server Cache Synchronization Protocol (SCSP) Support
 * ----------------------------------------------------
 *
 * SCSP subroutines
 *
 */

#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_sigmgr.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_ioctl.h>
#include <netatm/uni/unisig_var.h>

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

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

#ifndef lint
__RCSID("@(#) $FreeBSD: src/usr.sbin/atm/scspd/scsp_subr.c,v 1.5 2003/07/29 13:40:52 harti Exp $");
#endif


/*
 * Hash an SCSP cache key
 *
 * Arguments:
 *	ckp	pointer to an SCSP cache key structure
 *
 * Returns:
 *	hashed value
 *
 */
int
scsp_hash(ckp)
	Scsp_ckey	*ckp;
{
	int	i, j, h;

	/*
	 * Turn cache key into a positive integer
	 */
	h = 0;
	for (i = ckp->key_len-1, j = 0;
			i > 0 && j < sizeof(int);
			i--, j++)
		h = (h << 8) + ckp->key[i];
	h = abs(h);

	/*
	 * Return the hashed value
	 */
	return(h % SCSP_HASHSZ);
}


/*
 * Compare two SCSP IDs
 *
 * Arguments:
 *	id1p	pointer to an SCSP ID structure
 *	id2p	pointer to an SCSP ID structure
 *
 * Returns:
 *	< 0	id1 is less than id2
 *	0	id1 and id2 are equal
 *	> 0	id1 is greater than id2
 *
 */
int
scsp_cmp_id(id1p, id2p)
	Scsp_id	*id1p;
	Scsp_id	*id2p;
{
	int	diff, i;

	/*
	 * Compare the two IDs, byte for byte
	 */
	for (i = 0; i < id1p->id_len && i < id2p->id_len; i++) {
		diff = id1p->id[i] - id2p->id[i];
		if (diff) {
			return(diff);
		}
	}

	/*
	 * IDs are equal.  If lengths differ, the longer ID is
	 * greater than the shorter.
	 */
	return(id1p->id_len - id2p->id_len);
}


/*
 * Compare two SCSP cache keys
 *
 * Arguments:
 *	ck1p	pointer to an SCSP cache key structure
 *	ck2p	pointer to an SCSP cache key structure
 *
 * Returns:
 *	< 0	ck1 is less than ck2
 *	0	ck1 and ck2 are equal
 *	> 0	ck1 is greater than ck2
 *
 */
int
scsp_cmp_key(ck1p, ck2p)
	Scsp_ckey	*ck1p;
	Scsp_ckey	*ck2p;
{
	int	diff, i;

	/*
	 * Compare the two keys, byte for byte
	 */
	for (i = 0; i < ck1p->key_len && i < ck2p->key_len; i++) {
		diff = ck1p->key[i] - ck2p->key[i];
		if (diff)
			return(diff);
	}

	/*
	 * Keys are equal.  If lengths differ, the longer key is
	 * greater than the shorter.
	 */
	return(ck1p->key_len - ck2p->key_len);
}


/*
 * Check whether the host system is an ATMARP server for
 * the LIS associated with a given interface
 *
 * Arguments:
 *	netif	pointer to the network interface name
 *
 * Returns:
 *	1	host is a server
 *	0	host is not a server
 *
 */
int
scsp_is_atmarp_server(netif)
	char	*netif;
{
	int			rc;
	size_t buf_len;
	struct atminfreq	air;
	struct air_asrv_rsp	*asrv_info;

	/*
	 * Get interface information from the kernel
	 */
	strcpy(air.air_int_intf, netif);
	air.air_opcode = AIOCS_INF_ASV;
	buf_len = do_info_ioctl(&air, sizeof(struct air_asrv_rsp));
	if ((ssize_t)buf_len == -1)
		return(0);

	/*
	 * Check the interface's ATMARP server address
	 */
	asrv_info = (struct air_asrv_rsp *) air.air_buf_addr;
	rc = (asrv_info->asp_addr.address_format == T_ATM_ABSENT) &&
			(asrv_info->asp_subaddr.address_format ==
				T_ATM_ABSENT);
	free(asrv_info);
	return(rc);
}


/*
 * Make a copy of a cache summary entry
 *
 * Arguments:
 *	csep	pointer to CSE entry to copy
 *
 * Returns:
 *	0	copy failed
 *	else	pointer to new CSE entry
 *
 */
Scsp_cse *
scsp_dup_cse(csep)
	Scsp_cse	*csep;
{
	Scsp_cse	*dupp;

	/*
	 * Allocate memory for the duplicate
	 */
	dupp = malloc(sizeof(Scsp_cse));
	if (dupp == NULL)
		scsp_mem_err("scsp_dup_cse: sizeof(Scsp_cse)");

	/*
	 * Copy data to the duplicate
	 */
	bcopy(csep, dupp, sizeof(Scsp_cse));
	dupp->sc_next = (Scsp_cse *)0;
	return(dupp);
}


/*
 * Make a copy of a CSA or CSAS record
 *
 * Arguments:
 *	csap	pointer to CSE entry to copy
 *
 * Returns:
 *	0	copy failed
 *	else	pointer to new CSA or CSAS record
 *
 */
Scsp_csa *
scsp_dup_csa(csap)
	Scsp_csa	*csap;
{
	Scsp_csa	*dupp;
	Scsp_atmarp_csa	*adp;

	/*
	 * Allocate memory for the duplicate
	 */
	dupp = malloc(sizeof(Scsp_csa));
	if (dupp == NULL)
		scsp_mem_err("scsp_dup_csa: sizeof(Scsp_csa)");

	/*
	 * Copy data to the duplicate
	 */
	bcopy(csap, dupp, sizeof(Scsp_csa));
	dupp->next = (Scsp_csa *)0;

	/*
	 * Copy protocol-specific data, if it's present
	 */
	if (csap->atmarp_data) {
		adp = malloc(sizeof(Scsp_atmarp_csa));
		if (adp == NULL)
			scsp_mem_err("scsp_dup_csa: sizeof(Scsp_atmarp_csa)");
		bcopy(csap->atmarp_data, adp, sizeof(Scsp_atmarp_csa));
		dupp->atmarp_data  = adp;
	}
	return(dupp);
}


/*
 * Copy a cache summary entry into a CSAS
 *
 * Arguments:
 *	csep	pointer to CSE entry to copy
 *
 * Returns:
 *	0	copy failed
 *	else	pointer to CSAS record summarizing the entry
 *
 */
Scsp_csa *
scsp_cse2csas(csep)
	Scsp_cse	*csep;
{
	Scsp_csa	*csap;

	/*
	 * Allocate memory for the duplicate
	 */
	csap = calloc(1, sizeof(Scsp_csa));
	if (csap == NULL)
		scsp_mem_err("scsp_cse2csas: sizeof(Scsp_csa)");

	/*
	 * Copy data to the CSAS entry
	 */
	csap->seq = csep->sc_seq;
	csap->key = csep->sc_key;
	csap->oid = csep->sc_oid;

	return(csap);
}


/*
 * Copy an ATMARP cache entry into a cache summary entry
 *
 * Arguments:
 *	aap	pointer to ATMARP cache entry to copy
 *
 * Returns:
 *	0	copy failed
 *	else	pointer to CSE record summarizing the entry
 *
 */
Scsp_cse *
scsp_atmarp2cse(aap)
	Scsp_atmarp_msg	*aap;
{
	Scsp_cse	*csep;

	/*
	 * Allocate memory for the duplicate
	 */
	csep = calloc(1, sizeof(Scsp_cse));
	if (csep == NULL)
		scsp_mem_err("scsp_atmarp2cse: sizeof(Scsp_cse)");

	/*
	 * Copy data to the CSE entry
	 */
	csep->sc_seq = aap->sa_seq;
	csep->sc_key = aap->sa_key;
	csep->sc_oid = aap->sa_oid;

	return(csep);
}


/*
 * Clean up a DCS block.  This routine is called to clear out any
 * lingering state information when the CA FSM reverts to an 'earlier'
 * state (Down or Master/Slave Negotiation).
 *
 * Arguments:
 *	dcsp	pointer to a DCS control block for the neighbor
 *
 * Returns:
 *	none
 *
 */
void
scsp_dcs_cleanup(dcsp)
	Scsp_dcs	*dcsp;
{
	Scsp_cse	*csep, *ncsep;
	Scsp_csa	*csap, *next_csap;
	Scsp_csu_rexmt	*rxp, *rx_next;

	/*
	 * Free any CSAS entries waiting to be sent
	 */
	for (csep = dcsp->sd_ca_csas; csep; csep = ncsep) {
		ncsep = csep->sc_next;
		UNLINK(csep, Scsp_cse, dcsp->sd_ca_csas, sc_next);
		free(csep);
	}

	/*
	 * Free any entries on the CRL
	 */
	for (csap = dcsp->sd_crl; csap; csap = next_csap) {
		next_csap = csap->next;
		UNLINK(csap, Scsp_csa, dcsp->sd_crl, next);
		SCSP_FREE_CSA(csap);
	}

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

	/*
	 * Free any saved CSU Solicit message and cancel the CSUS
	 * retransmission timer
	 */
	if (dcsp->sd_csus_rexmt_msg) {
		scsp_free_msg(dcsp->sd_csus_rexmt_msg);
		dcsp->sd_csus_rexmt_msg = (Scsp_msg *)0;
	}
	HARP_CANCEL(&dcsp->sd_csus_rexmt_t);

	/*
	 * Free any entries on the CSU Request retransmission queue
	 */
	for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = rx_next) {
		rx_next = rxp->sr_next;
		HARP_CANCEL(&rxp->sr_t);
		for (csap = rxp->sr_csa; csap; csap = next_csap) {
			next_csap = csap->next;
			SCSP_FREE_CSA(csap);
		}
		UNLINK(rxp, Scsp_csu_rexmt, dcsp->sd_csu_rexmt,
				sr_next);
		free(rxp);
	}
}


/*
 * Delete an SCSP DCS block and any associated information
 *
 * Arguments:
 *	dcsp	pointer to a DCS control block to delete
 *
 * Returns:
 *	none
 *
 */
void
scsp_dcs_delete(dcsp)
	Scsp_dcs	*dcsp;
{
	Scsp_cse	*csep, *next_cse;
	Scsp_csu_rexmt	*rxp, *next_rxp;
	Scsp_csa	*csap, *next_csa;

	/*
	 * Cancel any pending DCS timers
	 */
	HARP_CANCEL(&dcsp->sd_open_t);
	HARP_CANCEL(&dcsp->sd_hello_h_t);
	HARP_CANCEL(&dcsp->sd_hello_rcv_t);
	HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
	HARP_CANCEL(&dcsp->sd_csus_rexmt_t);

	/*
	 * Unlink the DCS block from the server block
	 */
	UNLINK(dcsp, Scsp_dcs, dcsp->sd_server->ss_dcs, sd_next);

	/*
	 * Close the VCC to the DCS, if one is open
	 */
	if (dcsp->sd_sock != -1) {
		(void)close(dcsp->sd_sock);
	}

	/*
	 * Free any saved CA message
	 */
	if (dcsp->sd_ca_rexmt_msg) {
		scsp_free_msg(dcsp->sd_ca_rexmt_msg);
	}

	/*
	 * Free any pending CSAs waiting for cache alignment
	 */
	for (csep = dcsp->sd_ca_csas; csep; csep = next_cse) {
		next_cse = csep->sc_next;
		free(csep);
	}

	/*
	 * Free anything on the cache request list
	 */
	for (csap = dcsp->sd_crl; csap; csap = next_csa) {
		next_csa = csap->next;
		SCSP_FREE_CSA(csap);
	}

	/*
	 * Free any saved CSUS message
	 */
	if (dcsp->sd_csus_rexmt_msg) {
		scsp_free_msg(dcsp->sd_csus_rexmt_msg);
	}

	/*
	 * Free anything on the CSU Request retransmit queue
	 */
	for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = next_rxp) {
		/*
		 * Cancel the retransmit timer
		 */
		HARP_CANCEL(&rxp->sr_t);

		/*
		 * Free the CSAs to be retransmitted
		 */
		for (csap = rxp->sr_csa; csap; csap = next_csa) {
			next_csa = csap->next;
			SCSP_FREE_CSA(csap);
		}

		/*
		 * Free the CSU Req retransmission control block
		 */
		next_rxp = rxp->sr_next;
		free(rxp);
	}

	/*
	 * Free the DCS block
	 */
	free(dcsp);
}


/*
 * Shut down a server.  This routine is called when a connection to
 * a server is lost.  It will clear the server's state without deleting
 * the server.
 *
 * Arguments:
 *	ssp	pointer to a server control block
 *
 * Returns:
 *	none
 *
 */
void
scsp_server_shutdown(ssp)
	Scsp_server	*ssp;
{
	int		i;
	Scsp_dcs	*dcsp;
	Scsp_cse	*csep;

	/*
	 * Trace the shutdown
	 */
	if (scsp_trace_mode & (SCSP_TRACE_IF_MSG | SCSP_TRACE_CFSM)) {
		scsp_trace("Server %s being shut down\n",
				ssp->ss_name);
	}

	/*
	 * Terminate up all the DCS connections and clean
	 * up the control blocks
	 */
	for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
		if (dcsp->sd_sock != -1) {
			(void)close(dcsp->sd_sock);
			dcsp->sd_sock = -1;
		}
		HARP_CANCEL(&dcsp->sd_open_t);
		HARP_CANCEL(&dcsp->sd_hello_h_t);
		HARP_CANCEL(&dcsp->sd_hello_rcv_t);
		scsp_dcs_cleanup(dcsp);
		dcsp->sd_hello_state = SCSP_HFSM_DOWN;
		dcsp->sd_ca_state = SCSP_CAFSM_DOWN;
		dcsp->sd_client_state = SCSP_CIFSM_NULL;
	}

	/*
	 * Clean up the server control block
	 */
	if (ssp->ss_sock != -1) {
		(void)close(ssp->ss_sock);
		ssp->ss_sock = -1;
	}
	if (ssp->ss_dcs_lsock != -1) {
		(void)close(ssp->ss_dcs_lsock);
		ssp->ss_dcs_lsock = -1;
	}
	ssp->ss_state = SCSP_SS_NULL;

	/*
	 * Free the entries in the server's summary cache
	 */
	for (i = 0; i < SCSP_HASHSZ; i++) {
		while (ssp->ss_cache[i]) {
			csep = ssp->ss_cache[i];
			UNLINK(csep, Scsp_cse, ssp->ss_cache[i],
					sc_next);
			free(csep);
		}
	}
}


/*
 * Delete an SCSP server block and any associated information
 *
 * Arguments:
 *	ssp	pointer to a server control block to delete
 *
 * Returns:
 *	none
 *
 */
void
scsp_server_delete(ssp)
	Scsp_server	*ssp;
{
	int		i;
	Scsp_dcs	*dcsp, *next_dcs;
	Scsp_cse	*csep, *next_cse;

	/*
	 * Unlink the server block from the chain
	 */
	UNLINK(ssp, Scsp_server, scsp_server_head, ss_next);

	/*
	 * Free the DCS blocks associated with the server
	 */
	for (dcsp = ssp->ss_dcs; dcsp; dcsp = next_dcs) {
		next_dcs = dcsp->sd_next;
		scsp_dcs_delete(dcsp);
	}

	/*
	 * Free the entries in the server's summary cache
	 */
	for (i = 0; i < SCSP_HASHSZ; i++) {
		for (csep = ssp->ss_cache[i]; csep; csep = next_cse) {
			next_cse = csep->sc_next;
			free(csep);
		}
	}

	/*
	 * Free the server block
	 */
	free(ssp->ss_name);
	free(ssp);
}


/*
 * Get informtion about a server from the kernel
 *
 * Arguments:
 *	ssp	pointer to the server block
 *
 * Returns:
 *	0	server info is OK
 *	errno	server is not ready
 *
 */
int
scsp_get_server_info(ssp)
	Scsp_server	*ssp;
{
	int			i, mtu, rc, sel;
	size_t len;
	struct atminfreq	air;
	struct air_netif_rsp	*netif_rsp = (struct air_netif_rsp *)0;
	struct air_int_rsp	*intf_rsp = (struct air_int_rsp *)0;
	struct air_cfg_rsp	*cfg_rsp = (struct air_cfg_rsp *)0;
	struct sockaddr_in	*ip_addr;
	Atm_addr_nsap		*anp;

	/*
	 * Make sure we're the server for the interface
	 */
	if (!scsp_is_atmarp_server(ssp->ss_intf)) {
		rc = EINVAL;
		goto server_info_done;
	}

	/*
	 * Get the IP address and physical interface name
	 * associated with the network interface
	 */
	bzero(&air, sizeof(struct atminfreq));
	air.air_opcode = AIOCS_INF_NIF;
	strcpy(air.air_netif_intf, ssp->ss_intf);
	len = do_info_ioctl(&air, sizeof(struct air_netif_rsp));
	if ((ssize_t)len == -1 || len == 0) {
		rc = EIO;
		goto server_info_done;
	}
	netif_rsp = (struct air_netif_rsp *)air.air_buf_addr;

	ip_addr = (struct sockaddr_in *)&netif_rsp->anp_proto_addr;
	if (ip_addr->sin_family != AF_INET ||
			ip_addr->sin_addr.s_addr == 0) {
		rc = EADDRNOTAVAIL;
		goto server_info_done;
	}

	/*
	 * Get the MTU for the network interface
	 */
	mtu = get_mtu(ssp->ss_intf);
	if (mtu < 0) {
		rc = EIO;
		goto server_info_done;
	}

	/*
	 * Get the ATM address associated with the
	 * physical interface
	 */
	bzero(&air, sizeof(struct atminfreq));
	air.air_opcode = AIOCS_INF_INT;
	strcpy(air.air_int_intf, netif_rsp->anp_phy_intf);
	len = do_info_ioctl(&air, sizeof(struct air_int_rsp));
	if ((ssize_t)len == -1 || len == 0) {
		rc = EIO;
		goto server_info_done;
	}
	intf_rsp = (struct air_int_rsp *)air.air_buf_addr;

	/*
	 * Make sure we're running UNI signalling
	 */
	if (intf_rsp->anp_sig_proto != ATM_SIG_UNI30 &&
			intf_rsp->anp_sig_proto != ATM_SIG_UNI31 &&
			intf_rsp->anp_sig_proto != ATM_SIG_UNI40) {
		rc = EINVAL;
		goto server_info_done;
	}

	/*
	 * Check the physical interface's state
	 */
	if (intf_rsp->anp_sig_state != UNISIG_ACTIVE) {
		rc = EHOSTDOWN;
		goto server_info_done;
	}

	/*
	 * Make sure the interface's address is valid
	 */
	if (intf_rsp->anp_addr.address_format != T_ATM_ENDSYS_ADDR &&
			!(intf_rsp->anp_addr.address_format ==
				T_ATM_E164_ADDR &&
			intf_rsp->anp_subaddr.address_format ==
				T_ATM_ENDSYS_ADDR)) {
		rc = EINVAL;
		goto server_info_done;
	}

	/*
	 * Find the selector byte value for the interface
	 */
	for (i=0; i<strlen(ssp->ss_intf); i++) {
		if (ssp->ss_intf[i] >= '0' &&
				ssp->ss_intf[i] <= '9')
			break;
	}
	sel = atoi(&ssp->ss_intf[i]);

	/*
	 * Get configuration information associated with the
	 * physical interface
	 */
	bzero(&air, sizeof(struct atminfreq));
	air.air_opcode = AIOCS_INF_CFG;
	strcpy(air.air_int_intf, netif_rsp->anp_phy_intf);
	len = do_info_ioctl(&air, sizeof(struct air_cfg_rsp));
	if ((ssize_t)len == -1 || len == 0) {
		rc = EIO;
		goto server_info_done;
	}
	cfg_rsp = (struct air_cfg_rsp *)air.air_buf_addr;

	/*
	 * Update the server entry
	 */
	bcopy(&ip_addr->sin_addr, ssp->ss_lsid.id, ssp->ss_id_len);
	ssp->ss_lsid.id_len = ssp->ss_id_len;
	ssp->ss_mtu = mtu + 8;
	ATM_ADDR_COPY(&intf_rsp->anp_addr, &ssp->ss_addr);
	ATM_ADDR_COPY(&intf_rsp->anp_subaddr, &ssp->ss_subaddr);
	if (ssp->ss_addr.address_format == T_ATM_ENDSYS_ADDR) {
		anp = (Atm_addr_nsap *)ssp->ss_addr.address;
		anp->aan_sel = sel;
	} else if (ssp->ss_addr.address_format == T_ATM_E164_ADDR &&
			ssp->ss_subaddr.address_format ==
				T_ATM_ENDSYS_ADDR) {
		anp = (Atm_addr_nsap *)ssp->ss_subaddr.address;
		anp->aan_sel = sel;
	}
	ssp->ss_media = cfg_rsp->acp_cfg.ac_media;
	rc = 0;

	/*
	 * Free dynamic data
	 */
server_info_done:
	if (netif_rsp)
		free(netif_rsp);
	if (intf_rsp)
		free(intf_rsp);
	if (cfg_rsp)
		free(cfg_rsp);

	return(rc);
}


/*
 * Process a CA message
 *
 * Arguments:
 *	dcsp	pointer to a DCS control block for the neighbor
 *	cap	pointer to the CA part of the received message
 *
 * Returns:
 *	none
 *
 */
void
scsp_process_ca(dcsp, cap)
	Scsp_dcs	*dcsp;
	Scsp_ca		*cap;
{
	Scsp_csa	*csap, *next_csap;
	Scsp_cse	*csep;
	Scsp_server	*ssp = dcsp->sd_server;

	/*
	 * Process CSAS records from the CA message
	 */
	for (csap = cap->ca_csa_rec; csap; csap = next_csap) {
		next_csap = csap->next;
		SCSP_LOOKUP(ssp, &csap->key, csep);
		if (!csep || (scsp_cmp_id(&csap->oid,
					&csep->sc_oid) == 0 &&
				csap->seq > csep->sc_seq)) {
			/*
			 * CSAS entry not in cache or more
			 * up to date than cache, add it to CRL
			 */
			UNLINK(csap, Scsp_csa, cap->ca_csa_rec, next);
			LINK2TAIL(csap, Scsp_csa, dcsp->sd_crl, next);
		}
	}
}


/*
 * Process a Cache Response message from a server
 *
 * Arguments:
 *	ssp	pointer to the server block
 *	smp	pointer to the message
 *
 * Returns:
 *	none
 *
 */
void
scsp_process_cache_rsp(ssp, smp)
	Scsp_server	*ssp;
	Scsp_if_msg	*smp;
{
	int		len;
	Scsp_atmarp_msg	*aap;
	Scsp_cse	*csep;

	/*
	 * Loop through the message, processing each cache entry
	 */
	len = smp->si_len;
	len -= sizeof(Scsp_if_msg_hdr);
	aap = &smp->si_atmarp;
	while (len > 0) {
		switch(smp->si_proto) {
		case SCSP_ATMARP_PROTO:
			/*
			 * If we already have an entry with this key,
			 * delete it
			 */
			SCSP_LOOKUP(ssp, &aap->sa_key, csep);
			if (csep) {
				SCSP_DELETE(ssp, csep);
				free(csep);
			}

			/*
			 * Copy the data from the server to a cache
			 * summary entry
			 */
			csep = scsp_atmarp2cse(aap);

			/*
			 * Point past this entry
			 */
			len -= sizeof(Scsp_atmarp_msg);
			aap++;
			break;
		case SCSP_NHRP_PROTO:
		default:
			/*
			 * Not implemented yet
			 */
			return;
		}

		/*
		 * Add the new summary entry to the cache
		 */
		SCSP_ADD(ssp, csep);
	}
}


/*
 * Propagate a CSA to all the DCSs in the server group except
 * the one the CSA was received from
 *
 * Arguments:
 *	dcsp	pointer to a the DCS the CSA came from
 *	csap	pointer to a the CSA
 *
 * Returns:
 *	0	success
 *	errno	error encountered
 *
 */
int
scsp_propagate_csa(dcsp, csap)
	Scsp_dcs	*dcsp;
	Scsp_csa	*csap;
{
	int		rc, ret_rc = 0;
	Scsp_server	*ssp = dcsp->sd_server;
	Scsp_dcs	*dcsp1;
	Scsp_csa	*csap1;

	/*
	 * Check the hop count in the CSA
	 */
	if (csap->hops <= 1)
		return(0);

	/*
	 * Pass the cache entry on to the server's other DCSs
	 */
	for (dcsp1 = ssp->ss_dcs; dcsp1; dcsp1 = dcsp1->sd_next) {
		/*
		 * Skip this DCS if it's the one we got
		 * the entry from
		 */
		if (dcsp1 == dcsp)
			continue;

		/*
		 * Copy the  CSA
		 */
		csap1 = scsp_dup_csa(csap);

		/*
		 * Decrement the hop count
		 */
		csap1->hops--;

		/*
		 * Send the copy of the CSA to the CA FSM for the DCS
		 */
		rc = scsp_cafsm(dcsp1, SCSP_CAFSM_CACHE_UPD,
				(void *) csap1);
		if (rc)
			ret_rc = rc;
	}

	return(ret_rc);
}


/*
 * Update SCSP's cache given a CSA or CSAS
 *
 * Arguments:
 *	dcsp	pointer to a DCS 
 *	csap	pointer to a CSA
 *
 * Returns:
 *	none
 *
 */
void
scsp_update_cache(dcsp, csap)
	Scsp_dcs	*dcsp;
	Scsp_csa	*csap;
{
	Scsp_cse	*csep;

	/*
	 * Check whether we already have this in the cache
	 */
	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_update_cache: sizeof(Scsp_cse)");

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

		/*
		 * Add the new entry to the cache
		 */
		SCSP_ADD(dcsp->sd_server, csep);
	} 

	/*
	 * Update or delete the entry
	 */
	if (csap->null) {
		/*
		 * The null flag is set--delete the entry
		 */
		if (csep) {
			SCSP_DELETE(dcsp->sd_server, csep);
			free(csep);
		}
	} else {
		/*
		 * Update the existing entry
		 */
		csep->sc_seq = csap->seq;
		csep->sc_oid = csap->oid;
	}
}


/*
 * Reconfigure SCSP
 *
 * Called as the result of a SIGHUP interrupt.  Reread the
 * configuration file and solicit the cache from the server.
 *
 * Arguments:
 *	none
 *
 * Returns:
 *	none
 *
 */
void
scsp_reconfigure()
{
	int		rc;
	Scsp_server	*ssp;

	/*
	 * Log a message saying we're reconfiguring
	 */
	scsp_log(LOG_ERR, "Reconfiguring ...");

	/*
	 * Re-read the configuration file
	 */
	rc = scsp_config(scsp_config_file);
	if (rc) {
		scsp_log(LOG_ERR, "Found %d error%s in configuration file",
				rc, ((rc == 1) ? "" : "s"));
		exit(1);
	}

	/*
	 * If a connection to a server is open, get the cache from
	 * the server
	 */
	for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
		if (ssp->ss_sock != -1) {
			rc = scsp_send_cache_ind(ssp);
		}
	}
}