FreeBSD-5.3/usr.sbin/atm/scspd/scsp_socket.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_socket.c,v 1.4 2002/05/16 23:57:37 arr Exp $
 *
 */


/*
 * Server Cache Synchronization Protocol (SCSP) Support
 * ----------------------------------------------------
 *
 * SCSP socket management routines
 *
 */

#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 <fcntl.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_socket.c,v 1.4 2002/05/16 23:57:37 arr Exp $");
#endif


/*
 * Local variables
 */
static struct t_atm_llc llc_scsp = {
	T_ATM_LLC_SHARING,
	8,
	{0xaa, 0xaa, 0x03, 0x00, 0x00, 0x5e, 0x00, 0x05}
};

static struct t_atm_aal5	aal5 = {
	0,			/* forward_max_SDU_size */
	0,			/* backward_max_SDU_size */
	0			/* SSCS_type */
};

static struct t_atm_traffic	traffic = {
	{			/* forward */
		T_ATM_ABSENT,	/* PCR_high_priority */
		0,		/* PCR_all_traffic */
		T_ATM_ABSENT,	/* SCR_high_priority */
		T_ATM_ABSENT,	/* SCR_all_traffic */
		T_ATM_ABSENT,	/* MBS_high_priority */
		T_ATM_ABSENT,	/* MBS_all_traffic */
		T_NO		/* tagging */
	},
	{			/* backward */
		T_ATM_ABSENT,	/* PCR_high_priority */
		0,		/* PCR_all_traffic */
		T_ATM_ABSENT,	/* SCR_high_priority */
		T_ATM_ABSENT,	/* SCR_all_traffic */
		T_ATM_ABSENT,	/* MBS_high_priority */
		T_ATM_ABSENT,	/* MBS_all_traffic */
		T_NO		/* tagging */
	},
        T_YES			/* best_effort */
};

static struct t_atm_bearer	bearer = {
	T_ATM_CLASS_X,		/* bearer_class */
	T_ATM_NULL,		/* traffic_type */
	T_ATM_NULL,		/* timing_requirements */
	T_NO,			/* clipping_susceptibility */
	T_ATM_1_TO_1		/* connection_configuration */
};

static struct t_atm_qos	qos = {
	T_ATM_NETWORK_CODING,		/* coding_standard */
	{				/* forward */
		T_ATM_QOS_CLASS_0	/* qos_class */
	},
	{				/* backward */
		T_ATM_QOS_CLASS_0	/* qos_class */
	}
};

static struct t_atm_app_name	appname = {
	"SCSP"
};


/*
 * Find a DCS, given its socket
 *
 * Arguments:
 *	sd	socket descriptor
 *
 * Returns:
 *	0	not found
 *	address of DCS block corresponding to socket
 *
 */
Scsp_dcs *
scsp_find_dcs(sd)
	int	sd;
{
	Scsp_server	*ssp;
	Scsp_dcs	*dcsp = NULL;

	/*
	 * Loop through the list of servers
	 */
	for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
		/*
		 * Check all the DCSs chained from each server
		 */
		for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
			if (dcsp->sd_sock == sd)
				break;
		}
	}

	return(dcsp);
}


/*
 * Find a server, given its socket
 *
 * Arguments:
 *	sd	socket descriptor
 *
 * Returns:
 *	0	not found
 *	address of server block corresponding to socket
 *
 */
Scsp_server *
scsp_find_server(sd)
	int	sd;
{
	Scsp_server	*ssp;

	/*
	 * Loop through the list of servers
	 */
	for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
		if (ssp->ss_sock == sd)
			break;
	}

	return(ssp);
}


/*
 * Connect to a directly connected server
 *
 * Arguments:
 *	dcsp	pointer to DCS block for server
 *
 * Returns:
 *	0	success (dcsp->sd_sock is set)
 *	else	errno indicating reason for failure
 *
 */
int
scsp_dcs_connect(dcsp)
	Scsp_dcs	*dcsp;

{
	int			rc, sd;
	struct sockaddr_atm	DCS_addr;

	/*
	 * If the DCS already has an open connection, just return
	 */
	if (dcsp->sd_sock != -1) {
		return(0);
	}

	/*
	 * Open an ATM socket
	 */
	sd = socket(PF_ATM, SOCK_SEQPACKET, ATM_PROTO_AAL5);
	if (sd == -1) {
		return(ESOCKTNOSUPPORT);
	}
	if (sd > scsp_max_socket) {
		scsp_max_socket = sd;
	}

	/*
	 * Set up connection parameters for SCSP connection
	 */
	bzero(&DCS_addr, sizeof(DCS_addr));
#if (defined(BSD) && (BSD >= 199103))
	DCS_addr.satm_len = sizeof(DCS_addr);
#endif
	DCS_addr.satm_family = AF_ATM;
	DCS_addr.satm_addr.t_atm_sap_addr.SVE_tag_addr =
			T_ATM_PRESENT;
	DCS_addr.satm_addr.t_atm_sap_addr.SVE_tag_selector =
			T_ATM_PRESENT;
	DCS_addr.satm_addr.t_atm_sap_addr.address_format =
			dcsp->sd_addr.address_format;
	DCS_addr.satm_addr.t_atm_sap_addr.address_length =
			dcsp->sd_addr.address_length;
	bcopy(dcsp->sd_addr.address,
			DCS_addr.satm_addr.t_atm_sap_addr.address,
			dcsp->sd_addr.address_length);

	DCS_addr.satm_addr.t_atm_sap_layer2.SVE_tag =
			T_ATM_PRESENT;
	DCS_addr.satm_addr.t_atm_sap_layer2.ID_type =
			T_ATM_SIMPLE_ID;
	DCS_addr.satm_addr.t_atm_sap_layer2.ID.simple_ID =
			T_ATM_BLLI2_I8802;

        DCS_addr.satm_addr.t_atm_sap_layer3.SVE_tag =
			T_ATM_ABSENT;
        DCS_addr.satm_addr.t_atm_sap_appl.SVE_tag =
			T_ATM_ABSENT;

	/*
	 * Bind the socket to our address
	 */
	if (bind(sd, (struct sockaddr *)&DCS_addr,
			sizeof(DCS_addr))) {
		rc = errno;
		goto connect_fail;
	}

	/*
	 * Set non-blocking operation
	 */
#ifdef sun
	rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
#else
	rc = fcntl(sd, F_SETFL, O_NONBLOCK);
#endif
	if (rc == -1) {
		scsp_log(LOG_ERR, "scsp_dcs_connect: fcntl failed");
		rc = errno;
		goto connect_fail;
	}

	/*
	 * Set AAL 5 options
	 */
	aal5.forward_max_SDU_size = dcsp->sd_server->ss_mtu;
	aal5.backward_max_SDU_size = dcsp->sd_server->ss_mtu;
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_AAL5, (caddr_t)&aal5,
			sizeof(aal5)) < 0) {
		rc = EOPNOTSUPP;
		goto connect_fail;
	}

	/*
	 * Set traffic options
	 */
	switch(dcsp->sd_server->ss_media) {
	case MEDIA_TAXI_100:
		traffic.forward.PCR_all_traffic = ATM_PCR_TAXI100;
		traffic.backward.PCR_all_traffic = ATM_PCR_TAXI100;
		break;
	case MEDIA_TAXI_140:
		traffic.forward.PCR_all_traffic = ATM_PCR_TAXI140;
		traffic.backward.PCR_all_traffic = ATM_PCR_TAXI140;
		break;
	case MEDIA_OC3C:
	case MEDIA_UTP155:
		traffic.forward.PCR_all_traffic = ATM_PCR_OC3C;
		traffic.backward.PCR_all_traffic = ATM_PCR_OC3C;
		break;
	case MEDIA_OC12C:
		traffic.forward.PCR_all_traffic = ATM_PCR_OC12C;
		traffic.backward.PCR_all_traffic = ATM_PCR_OC12C;
		break;
	case MEDIA_UNKNOWN:
		break;
	}

	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_TRAFFIC,
			(caddr_t)&traffic, sizeof(traffic)) < 0) {
		rc = EOPNOTSUPP;
		goto connect_fail;
	}

	/*
	 * Set bearer capability options
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_BEARER_CAP,
			(caddr_t)&bearer, sizeof(bearer)) < 0) {
		rc = EOPNOTSUPP;
		goto connect_fail;
	}

	/*
	 * Set QOS options
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_QOS,
			(caddr_t)&qos, sizeof(qos)) < 0) {
		rc = EOPNOTSUPP;
		goto connect_fail;
	}

	/*
	 * Set LLC identifier
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_LLC,
			(caddr_t)&llc_scsp, sizeof(llc_scsp)) < 0) {
		rc = EOPNOTSUPP;
		goto connect_fail;
	}

	/*
	 * Set application name
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
			(caddr_t)&appname, sizeof(appname)) < 0) {
		rc = EOPNOTSUPP;
		goto connect_fail;
	}

	/*
	 * Connect to DCS
	 */
	if (connect(sd, (struct sockaddr *)&DCS_addr,
			sizeof(DCS_addr)) < 0 &&
			errno != EINPROGRESS) {
		rc = errno;
		goto connect_fail;
	}

	/*
	 * Set return values
	 */
	dcsp->sd_sock = sd;
	return(0);

connect_fail:
	/*
	 * Close the socket if something didn't work
	 */
	(void)close(sd);
	dcsp->sd_sock = -1;
	if (rc == 0)
		rc = EFAULT;
	return(rc);
}


/*
 * Listen for ATM connections from DCSs
 *
 * Arguments:
 *	None
 *
 * Returns:
 *	sock	socket which is listening (also set in
		ssp->ss_dcs_lsock)
 *	-1	error encountered (reason in errno)
 *
 */
int
scsp_dcs_listen(ssp)
	Scsp_server	*ssp;
{
	int			rc, sd;
	struct sockaddr_atm	ls_addr;

	/*
	 * Open a socket
	 */
	sd = socket(PF_ATM, SOCK_SEQPACKET, ATM_PROTO_AAL5);
	if (sd == -1) {
		rc = errno;
		goto listen_fail;
	}
	if (sd > scsp_max_socket) {
		scsp_max_socket = sd;
	}

	/*
	 * Set up our address
	 */
	bzero(&ls_addr, sizeof(ls_addr));
#if (defined(BSD) && (BSD >= 199103))
	ls_addr.satm_len = sizeof(ls_addr);
#endif
	ls_addr.satm_family = AF_ATM;
	ls_addr.satm_addr.t_atm_sap_addr.SVE_tag_addr = T_ATM_PRESENT;
	ls_addr.satm_addr.t_atm_sap_addr.SVE_tag_selector =
			T_ATM_PRESENT;
	ls_addr.satm_addr.t_atm_sap_addr.address_format =
			ssp->ss_addr.address_format;
	ls_addr.satm_addr.t_atm_sap_addr.address_length =
			ssp->ss_addr.address_length;
	bcopy(ssp->ss_addr.address,
			ls_addr.satm_addr.t_atm_sap_addr.address,
			ssp->ss_addr.address_length);

	ls_addr.satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_PRESENT;
	ls_addr.satm_addr.t_atm_sap_layer2.ID_type = T_ATM_SIMPLE_ID;
	ls_addr.satm_addr.t_atm_sap_layer2.ID.simple_ID =
			T_ATM_BLLI2_I8802;

	ls_addr.satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT;
	ls_addr.satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT;

	/*
	 * Bind the socket to our address
	 */
	rc = bind(sd, (struct sockaddr *)&ls_addr, sizeof(ls_addr));
	if (rc == -1) {
		rc = errno;
		goto listen_fail;
	}

	/*
	 * Set non-blocking I/O
	 */
#ifdef sun
	rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
#else
	rc = fcntl(sd, F_SETFL, O_NONBLOCK);
#endif
	if (rc == -1) {
		scsp_log(LOG_ERR, "scsp_dcs_listen: fcntl failed");
		rc = errno;
		goto listen_fail;
	}

	/*
	 * Set AAL 5 options
	 */
	aal5.forward_max_SDU_size = ssp->ss_mtu;
	aal5.backward_max_SDU_size = ssp->ss_mtu;
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_AAL5, (caddr_t)&aal5,
			sizeof(aal5)) < 0) {
		rc = EOPNOTSUPP;
		goto listen_fail;
	}

	/*
	 * Set traffic options
	 */
	switch(ssp->ss_media) {
	case MEDIA_TAXI_100:
		traffic.forward.PCR_all_traffic = ATM_PCR_TAXI100;
		traffic.backward.PCR_all_traffic = ATM_PCR_TAXI100;
		break;
	case MEDIA_TAXI_140:
		traffic.forward.PCR_all_traffic = ATM_PCR_TAXI140;
		traffic.backward.PCR_all_traffic = ATM_PCR_TAXI140;
		break;
	case MEDIA_OC3C:
	case MEDIA_UTP155:
		traffic.forward.PCR_all_traffic = ATM_PCR_OC3C;
		traffic.backward.PCR_all_traffic = ATM_PCR_OC3C;
		break;
	case MEDIA_OC12C:
		traffic.forward.PCR_all_traffic = ATM_PCR_OC12C;
		traffic.backward.PCR_all_traffic = ATM_PCR_OC12C;
		break;
	case MEDIA_UNKNOWN:
		break;
	}

	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_TRAFFIC,
			(caddr_t)&traffic, sizeof(traffic)) < 0) {
		rc = EOPNOTSUPP;
		goto listen_fail;
	}

	/*
	 * Set bearer capability options
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_BEARER_CAP,
			(caddr_t)&bearer, sizeof(bearer)) < 0) {
		rc = EOPNOTSUPP;
		goto listen_fail;
	}

	/*
	 * Set QOS options
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_QOS,
			(caddr_t)&qos, sizeof(qos)) < 0) {
		rc = EOPNOTSUPP;
		goto listen_fail;
	}

	/*
	 * Set LLC identifier
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_LLC,
			(caddr_t)&llc_scsp, sizeof(llc_scsp)) < 0) {
		rc = EOPNOTSUPP;
		goto listen_fail;
	}

	/*
	 * Set application name
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
			(caddr_t)&appname, sizeof(appname)) < 0) {
		rc = EOPNOTSUPP;
		goto listen_fail;
	}

	/*
	 * Listen for new connections
	 */
	if (listen(sd, 5) < 0) {
		rc = errno;
		goto listen_fail;
	}

	ssp->ss_dcs_lsock = sd;
	return(sd);

listen_fail:
	/*
	 * Close the socket if anything didn't work
	 */
	(void)close(sd);
	if (rc == 0)
		errno = EFAULT;
	else
		errno = rc;
	ssp->ss_dcs_lsock = -1;
	return(-1);
}


/*
 * Accept a connection from a DCS
 *
 * Arguments:
 *	ssp	pointer to server block
 *
 * Returns:
 *	address of DCS with new connection
 *	0	failure (errno has reason)
 *
 */
Scsp_dcs *
scsp_dcs_accept(ssp)
	Scsp_server	*ssp;
{
	int			len, rc, sd;
	struct sockaddr_atm	dcs_sockaddr;
	struct t_atm_sap_addr	*dcs_addr = &dcs_sockaddr.satm_addr.t_atm_sap_addr;
	Atm_addr		dcs_atmaddr;
	Scsp_dcs		*dcsp;

	/*
	 * Accept the new connection
	 */
	len = sizeof(dcs_sockaddr);
	sd = accept(ssp->ss_dcs_lsock,
			(struct sockaddr *)&dcs_sockaddr, &len);
	if (sd < 0) {
		return((Scsp_dcs *)0);
	}
	if (sd > scsp_max_socket) {
		scsp_max_socket = sd;
	}

	/*
	 * Copy the DCS's address from the sockaddr to an Atm_addr
	 */
	if (dcs_addr->SVE_tag_addr != T_ATM_PRESENT) {
		dcs_atmaddr.address_format = T_ATM_ABSENT;
		dcs_atmaddr.address_length = 0;
	} else {
		dcs_atmaddr.address_format = dcs_addr->address_format;
		dcs_atmaddr.address_length = dcs_addr->address_length;
		bcopy(dcs_addr->address, dcs_atmaddr.address,
				dcs_addr->address_length);
	}

	/*
	 * Find out which DCS this connection is for
	 */
	for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
		/*
		 * Compare DCS's address to address
		 * configured by user
		 */
		if (ATM_ADDR_EQUAL(&dcsp->sd_addr,
				&dcs_atmaddr))
			break;
	}

	/*
	 * Make sure we have this DCS configured
	 */
	if (!dcsp) {
		errno = EINVAL;
		goto dcs_accept_fail;
	}

	/*
	 * Make sure we are in a state to accept the connection
	 */
	if (ssp->ss_state != SCSP_SS_ACTIVE) {
		errno = EACCES;
		goto dcs_accept_fail;
	}

	/*
	 * Make sure we don't already have a connection to this DCS
	 */
	if (dcsp->sd_sock != -1) {
		errno = EALREADY;
		goto dcs_accept_fail;
	}

	/*
	 * Set application name
	 */
	if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
			(caddr_t)&appname, sizeof(appname)) < 0) {
		rc = EOPNOTSUPP;
		goto dcs_accept_fail;
	}

	/*
	 * Set non-blocking I/O
	 */
#ifdef sun
		rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
#else
		rc = fcntl(sd, F_SETFL, O_NONBLOCK);
#endif
	if (rc == -1) {
		goto dcs_accept_fail;
	}

	/*
	 * Cancel the open retry timer
	 */
	HARP_CANCEL(&dcsp->sd_open_t);

	/*
	 * Save the socket address and return the
	 * address of the DCS
	 */
	dcsp->sd_sock = sd;
	return(dcsp);

dcs_accept_fail:
	/*
	 * An error has occured--clean up and return
	 */
	(void)close(sd);
	return((Scsp_dcs *)0);
}


/*
 * Read an SCSP message from a directly connected server
 *
 * Arguments:
 *	dcsp	pointer to DCS block that has data
 *
 * Returns:
 *	0	success
 *	else	errno indicating reason for failure
 *
 */
int
scsp_dcs_read(dcsp)
	Scsp_dcs	*dcsp;

{
	int			len, rc;
	char			*buff = (char *)0;
	Scsp_server		*ssp = dcsp->sd_server;
	Scsp_msg		*msg;

	/*
	 * Get a buffer to hold the entire message
	 */
	len = ssp->ss_mtu;
	buff = calloc(1, len);
	if (buff == NULL)
		scsp_mem_err("scsp_dcs_read: ssp->ss_mtu");

	/*
	 * Read the message
	 */
	len = read(dcsp->sd_sock, buff, len);
	if (len < 0) {
		goto dcs_read_fail;
	}

	/*
	 * Parse the input message and pass it to the Hello FSM
	 */
	msg = scsp_parse_msg(buff, len);
	if (msg) {
		/*
		 * Write the message to the trace file if
		 * it's of a type we're tracing
		 */
		if (((scsp_trace_mode & SCSP_TRACE_HELLO_MSG) &&
				msg->sc_msg_type == SCSP_HELLO_MSG) ||
				((scsp_trace_mode & SCSP_TRACE_CA_MSG) &&
				msg->sc_msg_type != SCSP_HELLO_MSG)) {
			scsp_trace_msg(dcsp, msg, 1);
			scsp_trace("\n");
		}

		/*
		 * Pass the message to the Hello FSM
		 */
		rc = scsp_hfsm(dcsp, SCSP_HFSM_RCVD, msg);
		scsp_free_msg(msg);
	} else {
		/*
		 * Message was invalid.  Write it to the trace file
		 * if we're tracing messages.
		 */
		if (scsp_trace_mode & (SCSP_TRACE_HELLO_MSG &
				SCSP_TRACE_CA_MSG)) {
			int	i;
			scsp_trace("Invalid message received:\n");
			scsp_trace("0x");
			for (i = 0; i < len; i++) {
				scsp_trace("%02x ", (u_char)buff[i]);
			}
			scsp_trace("\n");
		}
	}
	free(buff);

	return(0);

dcs_read_fail:
	/*
	 * Error on read--check for special conditions
	 */
	rc = errno;
	if (errno == ECONNRESET) {
		/*
		 * VCC has been closed--pass the event to
		 * the Hello FSM
		 */
		rc = scsp_hfsm(dcsp, SCSP_HFSM_VC_CLOSED,
				(Scsp_msg *)0);
	}
	if (errno == ECONNREFUSED) {
		/*
		 * VCC open failed--set a timer and try
		 * again when it fires
		 */
		HARP_TIMER(&dcsp->sd_open_t,
				SCSP_Open_Interval,
				scsp_open_timeout);
		rc = 0;
	}

	if (buff)
		free(buff);
	return(rc);
}


/*
 * Listen for Unix connections from SCSP client servers
 *
 * Arguments:
 *	None
 *
 * Returns:
 *	sock	socket which is listening
 *	-1	error (reason in errno)
 *
 */
int
scsp_server_listen()
{
	int	rc, sd;

	static struct sockaddr	scsp_addr = {
#if (defined(BSD) && (BSD >= 199103))
		sizeof(struct sockaddr),	/* sa_len */
#endif
		AF_UNIX,			/* sa_family */
		SCSPD_SOCK_NAME			/* sa_data */
	};

	/*
	 * Unlink any old socket
	 */
	rc = unlink(SCSPD_SOCK_NAME);
	if (rc < 0 && errno != ENOENT)
		return(-1);

	/*
	 * Open a socket
	 */
	sd = socket(PF_UNIX, SOCK_STREAM, 0);
	if (sd == -1) {
		return(-1);
	}
	if (sd > scsp_max_socket) {
		scsp_max_socket = sd;
	}

	/*
	 * Bind the socket's address
	 */
	rc = bind(sd, &scsp_addr, sizeof(scsp_addr));
	if (rc == -1) {
		(void)close(sd);
		return(-1);
	}

	/*
	 * Set non-blocking I/O
	 */
#ifdef sun
		rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
#else
		rc = fcntl(sd, F_SETFL, O_NONBLOCK);
#endif
	if (rc == -1) {
		(void)close(sd);
		return(-1);
	}

	/*
	 * Listen for new connections
	 */
	if (listen(sd, 5) < 0) {
		(void)close(sd);
		return(-1);
	}

	return(sd);
}


/*
 * Accept a connection from a server
 *
 * We accept a connection, but we won't know which server it is
 * from until we get the configuration data from the server.  We
 * put the connection on a 'pending' queue and will assign it to
 * a server when the config data arrives.
 *
 * Arguments:
 *	ls	listening socket to accept from
 *
 * Returns:
 *	0	success
 *	errno	reason for failure
 *
 */
int
scsp_server_accept(ls)
	int	ls;

{
	int		len, rc, sd;
	struct sockaddr	server_addr;
	Scsp_pending	*psp;

	/*
	 * Accept the new connection
	 */
	len = sizeof(server_addr);
	sd = accept(ls, (struct sockaddr *)&server_addr, &len);
	if (sd < 0) {
		return(errno);
	}
	if (sd > scsp_max_socket) {
		scsp_max_socket = sd;
	}

	/*
	 * Set non-blocking operation
	 */
#ifdef sun
	rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
#else
	rc = fcntl(sd, F_SETFL, O_NONBLOCK);
#endif
	if (rc == -1) {
		(void)close(sd);
		rc = errno;
	}

	/*
	 * Put the new socket on the 'pending' queue
	 */
	psp = calloc(1, sizeof(Scsp_pending));
	if (!psp)
		scsp_mem_err("scsp_server_accept: sizeof(Scsp_pending)");
	psp->sp_sock = sd;
	LINK2TAIL(psp, Scsp_pending, scsp_pending_head, sp_next);

	return(0);
}


/*
 * Read a server interface message from a socket
 *
 * Arguments:
 *	sd	socket to read from
 *
 * Returns:
 *	msg	pointer to message read
 *	0	failure (errno has reason)
 *
 */
Scsp_if_msg *
scsp_if_sock_read(sd)
	int	sd;

{
	int		len;
	char		*buff = (char *)0;
	Scsp_if_msg	*msg;
	Scsp_if_msg_hdr	msg_hdr;

	/*
	 * Read the message header from the socket
	 */
	len = read(sd, (char *)&msg_hdr, sizeof(msg_hdr));
	if (len != sizeof(msg_hdr)) {
		if (len >= 0)
			errno = EINVAL;
		goto socket_read_fail;
	}

	/*
	 * Get a buffer and read the rest of the message into it
	 */
	buff = malloc(msg_hdr.sh_len);
	if (buff == NULL)
		scsp_mem_err("scsp_if_sock_read: msg_hdr.sh_len");
	msg = (Scsp_if_msg *)buff;
	msg->si_hdr = msg_hdr;
	len = read(sd, &buff[sizeof(Scsp_if_msg_hdr)],
			msg->si_len - sizeof(Scsp_if_msg_hdr));
	if (len != msg->si_len - sizeof(Scsp_if_msg_hdr)) {
		if (len >= 0) {
			errno = EINVAL;
		}
		goto socket_read_fail;
	}

	/*
	 * Trace the message
	 */
	if (scsp_trace_mode & SCSP_TRACE_IF_MSG) {
		scsp_trace("Received server I/F message:\n");
		print_scsp_if_msg(scsp_trace_file, msg);
		scsp_trace("\n");
	}

	return(msg);

socket_read_fail:
	if (buff)
		free(buff);
	return((Scsp_if_msg *)0);
}


/*
 * Write a server interface message to a socket
 *
 * Arguments:
 *	sd	socket to write to
 *	msg	pointer to message to write
 *
 * Returns:
 *	0	success
 *	errno	reason for failure
 *
 */
int
scsp_if_sock_write(sd, msg)
	int		sd;
	Scsp_if_msg	*msg;
{
	int	len, rc;

	/*
	 * Trace the message
	 */
	if (scsp_trace_mode & SCSP_TRACE_IF_MSG) {
		scsp_trace("Writing server I/F message:\n");
		print_scsp_if_msg(scsp_trace_file, msg);
		scsp_trace("\n");
	}

	/*
	 * Write the message to the indicated socket
	 */
	len = write(sd, (char *)msg, msg->si_len);
	if (len != msg->si_len) {
		if (len < 0)
			rc = errno;
		else
			rc = EINVAL;
	} else {
		rc = 0;
	}

	return(rc);
}


/*
 * Read data from a local server
 *
 * Arguments:
 *	ssp	pointer to server block that has data
 *
 * Returns:
 *	0	success
 *	else	errno indicating reason for failure
 *
 */
int
scsp_server_read(ssp)
	Scsp_server	*ssp;
{
	int		rc;
	Scsp_dcs	*dcsp;
	Scsp_if_msg	*msg;

	/*
	 * Read the message
	 */
	msg = scsp_if_sock_read(ssp->ss_sock);
	if (!msg) {
		if (errno == EWOULDBLOCK) {
			/*
			 * Nothing to read--just return
			 */
			return(0);
		} else {
			/*
			 * Error--shut down the server entry
			 */
			scsp_server_shutdown(ssp);
		}
		return(errno);
	}

	/*
	 * Process the received message
	 */
	switch(msg->si_type) {
	case SCSP_NOP_REQ:
		/*
		 * Ignore a NOP
		 */
		break;
	case SCSP_CACHE_RSP:
		/*
		 * Summarize the server's cache and try to open
		 * connections to all of its DCSs
		 */
		scsp_process_cache_rsp(ssp, msg);
		ssp->ss_state = SCSP_SS_ACTIVE;
		for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
			if (scsp_dcs_connect(dcsp)) {
				/*
				 * Connect failed -- the DCS may not
				 * be up yet, so we'll try again later
				 */
				HARP_TIMER(&dcsp->sd_open_t,
						SCSP_Open_Interval,
						scsp_open_timeout);
			}
		}
		ssp->ss_state = SCSP_SS_ACTIVE;
		break;
	case SCSP_SOLICIT_RSP:
		/*
		 * The server has answered our request for a particular
		 * entry from its cache
		 */
		dcsp = (Scsp_dcs *)msg->si_tok;
		rc = scsp_cfsm(dcsp, SCSP_CIFSM_SOL_RSP, (Scsp_msg *)0,
				msg);
		break;
	case SCSP_UPDATE_REQ:
		/*
		 * Pass the update request to the FSMs for all
		 * DCSs associated with the server
		 */
		if (ssp->ss_state == SCSP_SS_ACTIVE) {
			for (dcsp = ssp->ss_dcs; dcsp;
					dcsp = dcsp->sd_next) {
				rc = scsp_cfsm(dcsp, SCSP_CIFSM_UPD_REQ,
						(Scsp_msg *)0, msg);
			}
		}
		break;
	case SCSP_UPDATE_RSP:
		/*
		 * Pass the update response to the FSM for the
		 * DCS associated with the request
		 */
		dcsp = (Scsp_dcs *)msg->si_tok;
		rc = scsp_cfsm(dcsp, SCSP_CIFSM_UPD_RSP,
				(Scsp_msg *)0, msg);
		break;
	default:
		scsp_log(LOG_ERR, "invalid message type %d from server",
				msg->si_type);
		return(EINVAL);
	}

	free(msg);
	return(0);
}


/*
 * Send a Cache Indication to a server
 *
 * Arguments:
 *	ssp	pointer to server block block
 *
 * Returns:
 *	0	success
 *	else	errno indicating reason for failure
 *
 */
int
scsp_send_cache_ind(ssp)
	Scsp_server	*ssp;
{
	int		rc;
	Scsp_if_msg	*msg;

	/*
	 * Get storage for a server interface message
	 */
	msg = calloc(1, sizeof(Scsp_if_msg));
	if (msg == NULL)
		scsp_mem_err("scsp_send_cache_ind: sizeof(Scsp_if_msg)");
	/*
	 * Fill out the message
	 */
	msg->si_type = SCSP_CACHE_IND;
	msg->si_proto = ssp->ss_pid;
	msg->si_len = sizeof(Scsp_if_msg_hdr);
	msg->si_tok = (u_long)ssp;

	/*
	 * Send the message
	 */
	rc = scsp_if_sock_write(ssp->ss_sock, msg);
	free(msg);
	return(rc);
}


/*
 * Read data from a pending server connection
 *
 * Arguments:
 *	psp	pointer to pending block that has data
 *
 * Returns:
 *	0	success
 *	else	errno indicating reason for failure
 *
 */
int
scsp_pending_read(psp)
	Scsp_pending	*psp;

{
	int		rc;
	Scsp_server	*ssp;
	Scsp_if_msg	*msg;

	/*
	 * Read the message from the pending socket
	 */
	msg = scsp_if_sock_read(psp->sp_sock);
	if (!msg) {
		rc = errno;
		goto pending_read_fail;
	}

	/*
	 * Make sure this is configuration data
	 */
	if (msg->si_type != SCSP_CFG_REQ) {
		scsp_log(LOG_ERR, "invalid message type %d from pending server",
				msg->si_type);
		rc = EINVAL;
		goto pending_read_fail;
	}

	/*
	 * Find the server this message is for
	 */
	for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
		if (strcmp(ssp->ss_intf, msg->si_cfg.atmarp_netif) == 0)
			break;
	}
	if (!ssp) {
		scsp_log(LOG_ERR, "refused connection from server for %s",
				msg->si_cfg.atmarp_netif);
		rc = EINVAL;
		goto config_reject;
	}

	/*
	 * Make sure the server is ready to go
	 */
	rc = scsp_get_server_info(ssp);
	if (rc) {
		goto config_reject;
	}

	/*
	 * Save the socket
	 */
	ssp->ss_sock = psp->sp_sock;
	ssp->ss_state = SCSP_SS_CFG;
	UNLINK(psp, Scsp_pending, scsp_pending_head, sp_next);
	free(psp);

	/*
	 * Listen for connections from the server's DCSs
	 */
	rc = scsp_dcs_listen(ssp);
	if (rc < 0) {
		rc = errno;
		goto config_reject;
	}

	/*
	 * Respond to the configuration message
	 */
	msg->si_type = SCSP_CFG_RSP;
	msg->si_rc = SCSP_RSP_OK;
	msg->si_len = sizeof(Scsp_if_msg_hdr);
	rc = scsp_if_sock_write(ssp->ss_sock, msg);
	if (rc) {
		goto config_error;;
	}

	/*
	 * Ask the server to send us its cache
	 */
	rc = scsp_send_cache_ind(ssp);
	if (rc) {
		goto config_error;
	}

	free(msg);
	return(0);

config_reject:
	/*
	 * Respond to the configuration message
	 */
	msg->si_type = SCSP_CFG_RSP;
	msg->si_rc = SCSP_RSP_REJ;
	msg->si_len = sizeof(Scsp_if_msg_hdr);
	(void)scsp_if_sock_write(ssp->ss_sock, msg);

config_error:
	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_sock = -1;
	}
	ssp->ss_state = SCSP_SS_NULL;
	free(msg);

	return(rc);

pending_read_fail:
	/*
	 * Close the socket and free the pending read block
	 */
	(void)close(psp->sp_sock);
	UNLINK(psp, Scsp_pending, scsp_pending_head, sp_next);
	free(psp);
	if (msg)
		free(msg);
	return(rc);
}