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

/*
 * SPANS Signalling Manager
 * ---------------------------
 *
 * SPANS signalling message processing.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/netatm/spans/spans_msg.c,v 1.16 2005/01/07 01:45:38 imp Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_vc.h>
#include <netatm/atm_sigmgr.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>

#include "spans_xdr.h"
#include <netatm/spans/spans_var.h>

/*
 * External functions
 */
void		xdrmbuf_init(XDR *, KBuffer *, enum xdr_op);

/*
 * Local functions
 */
static void	spans_host_link(struct spans *, long);
static void	spans_status_ind(struct spans *, spans_msg *);
static void	spans_status_rsp(struct spans *, spans_msg *);
static void	spans_open_req(struct spans *, spans_msg *);
static void	spans_open_rsp(struct spans *, spans_msg *);
static void	spans_close_req(struct spans *, spans_msg *);
static void	spans_close_rsp(struct spans *, spans_msg *);
static void	spans_multi_req(struct spans *, spans_msg *);
static void	spans_add_req(struct spans *, spans_msg *);
static void	spans_join_req(struct spans *, spans_msg *);
static void	spans_leave_req(struct spans *, spans_msg *);
static void	spans_vcir_ind(struct spans *, spans_msg *);
static void	spans_query_req(struct spans *, spans_msg *);


/*
 * Called to set status when a status message comes in from a host
 * connected back-to-back with us.  Check the epoch and, if it has
 * changed, set the appropriate state and save updated state
 * information.
 *
 * Arguments:
 *	spp		pointer to SPANS protocol instance block
 *	host_epoch	epoch of host at far end of link
 *
 * Returns:
 *	0	message sent OK
 *	errno	error encountered
 *
 */
static void
spans_host_link(spp, host_epoch)
	struct spans	*spp;
	long	host_epoch;
{
	struct atm_pif	*pip = spp->sp_pif;

	/*
	 * There's a host at the other end of the link.  If its
	 * epoch has changed, clean up our state and save the
	 * new information.
	 */
	if (spp->sp_s_epoch != host_epoch) {
		spp->sp_s_epoch = host_epoch;
		spans_switch_reset(spp, SPANS_UNI_UP);
		spp->sp_addr.address_format = T_ATM_SPANS_ADDR;
		spp->sp_addr.address_length = sizeof(spans_addr);
		bcopy(&pip->pif_macaddr.ma_data[2],
				&spp->sp_addr.address[4],
				4);
		log(LOG_INFO,
		"spans: using SPANS address of %s on interface %s%d\n",
			spans_addr_print((spans_addr *)spp->sp_addr.address),
			pip->pif_name,
			pip->pif_unit);
	}
}

/*
 * Send a SPANS signalling message
 *
 * Called to send a SPANS message.  This routine gets a buffer, performs
 * XDR processing, and hands the message to the AAL for transmission.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to status message
 *
 * Returns:
 *	0	message sent OK
 *	errno	error encountered
 *
 */
int
spans_send_msg(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	int		err = 0;
	KBuffer		*m;
	XDR		xdrs;

#ifdef NOTDEF
	ATM_DEBUG2("spans_send_msg: msg=%p, type=%d\n", msg,
			msg->sm_type);
	if (msg->sm_type != SPANS_STAT_REQ &&
			msg->sm_type != SPANS_STAT_IND &&
			msg->sm_type != SPANS_STAT_RSP) {
		printf("spans_send_msg: sending ");
		spans_print_msg(msg);
	}
#endif

	/*
	 * If the signalling channel has been closed, don't do anything
	 */
	if (!spp->sp_conn)
		return(ECONNABORTED);

	/*
	 * Get a buffer
	 */
	KB_ALLOCPKT(m, sizeof(spans_msg), KB_F_NOWAIT, KB_T_DATA);
	if (m == NULL) {
		/* No buffer available */
		return(ENOBUFS);
	}

	/*
	 * Convert message to network order
	 */
	KB_LEN(m) = KB_BFRLEN(m);
	xdrmbuf_init(&xdrs, m, XDR_ENCODE);
	if (!xdr_spans_msg(&xdrs, msg)) {
		log(LOG_ERR, "spans_send_msg: XDR encode failed\n");
		KB_LEN(m) = XDR_GETPOS(&xdrs);
		spans_dump_buffer(m);
		KB_FREEALL(m);
		return(EIO);
	}
	KB_LEN(m) = XDR_GETPOS(&xdrs);

	/*
	 * Send the message
	 */
	err = atm_cm_cpcs_data(spp->sp_conn, m);
	if (err)
		KB_FREEALL(m);

	return(err);
}


/*
 * Send an open request
 *
 * Build and send an open request.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	svp	pointer to VCCB for which the request is being sent
 *
 * Returns:
 *	none
 *
 */
int
spans_send_open_req(spp, svp)
	struct	spans		*spp;
	struct	spans_vccb	*svp;
{
	spans_msg	*req;
	int		err = 0;

	ATM_DEBUG1("spans_send_open_req: svp=%p\n", svp);

	/*
	 * Get memory for a request message
	 */
	req = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (req == NULL) {
		err = ENOBUFS;
		goto done;
	}

	/*
	 * Fill in the request
	 */
	req->sm_vers = SPANS_VERS_1_0;
	req->sm_type = SPANS_OPEN_REQ;
	req->sm_open_req.opreq_conn = svp->sv_conn;
	req->sm_open_req.opreq_aal = svp->sv_spans_aal;
	req->sm_open_req.opreq_desrsrc = svp->sv_spans_qos;
	req->sm_open_req.opreq_minrsrc.rsc_peak = 0;
	req->sm_open_req.opreq_minrsrc.rsc_mean = 0;
	req->sm_open_req.opreq_minrsrc.rsc_burst = 0;
	req->sm_open_req.opreq_vpvc.vpf_valid = FALSE;

	/*
	 * Send the request
	 */
	err = spans_send_msg(spp, req);
	uma_zfree(spans_msg_zone, req);
done:
	return(err);
}


/*
 * Send an open response
 *
 * Build and send a response to an open request or open indication.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	svp	pointer to VCCB for which the response is being sent
 *	result	result code to include in the response
 *
 * Returns:
 *	none
 *
 */
int
spans_send_open_rsp(spp, svp, result)
	struct	spans		*spp;
	struct	spans_vccb	*svp;
	spans_result		result;
{
	spans_msg	*rsp;
	int		rc;

	ATM_DEBUG2("spans_send_open_rsp: svp=%p, result=%d\n", svp,
			result);

	/*
	 * Get memory for a response message
	 */
	rsp = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp == NULL)
		return(ENOBUFS);

	/*
	 * Fill in the response
	 */
	rsp->sm_vers = SPANS_VERS_1_0;
	rsp->sm_type = SPANS_OPEN_RSP;
	rsp->sm_open_rsp.oprsp_conn = svp->sv_conn;
	rsp->sm_open_rsp.oprsp_result = result;
	rsp->sm_open_rsp.oprsp_rsrc = svp->sv_spans_qos;
	rsp->sm_open_rsp.oprsp_vpvc =
			SPANS_PACK_VPIVCI(svp->sv_vpi, svp->sv_vci);

	/*
	 * Send the response
	 */
	rc = spans_send_msg(spp, rsp);
	uma_zfree(spans_msg_zone, rsp);
	return(rc);
}


/*
 * Send a close request
 *
 * Called to send a close request.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	svp	pointer to VCCB for which the close is being sent
 *
 * Returns:
 *	none
 *
 */
int
spans_send_close_req(spp, svp)
	struct spans		*spp;
	struct	spans_vccb	*svp;
{
	spans_msg	*req;
	int		err = 0;

	ATM_DEBUG1("spans_send_close_req: svp=%p\n", svp);

	/*
	 * Get memory for a close request
	 */
	req = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (req == NULL) {
		err = ENOBUFS;
		goto done;
	}

	/*
	 * Fill in the request
	 */
	req->sm_vers = SPANS_VERS_1_0;
	if (svp->sv_type & VCC_OUT) {
		req->sm_type = SPANS_CLOSE_REQ;
	} else if (svp->sv_type & VCC_IN) {
		req->sm_type = SPANS_RCLOSE_REQ;
	} else {
		err = EINVAL;
		ATM_DEBUG1(
		"spans_send_close_req: invalid VCCB type 0x%x\n",
				svp->sv_type);
		goto done;
	}
	req->sm_close_req.clreq_conn = svp->sv_conn;

	/*
	 * Send the close request
	 */
	err = spans_send_msg(spp, req);

done:
	if (req)
		uma_zfree(spans_msg_zone, req);

	return(err);
}



/*
 * Process a status indication or status request
 *
 * Called when a status indication or status request is received.
 * Processing will be based on the current SPANS state.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the status message
 *
 * Returns:
 *	none
 *
 */
static void
spans_status_ind(spp, msg)
	struct	spans	*spp;
	spans_msg	*msg;
{
	spans_msg	*rsp_msg;
	struct atm_pif	*pip = spp->sp_pif;

	/*
	 * Reset the probe count.
	 */
	spp->sp_probe_ct = 0;

	switch (spp->sp_state) {
	case SPANS_PROBE:
		/*
		 * Interface just came up, update signalling state
		 */
		spp->sp_state = SPANS_ACTIVE;
		break;

	case SPANS_ACTIVE:
		break;

	default:
		log(LOG_ERR, "spans: received status msg in state %d\n",
				spp->sp_state);
	}

	/*
	 * Process the message
	 */
	switch (msg->sm_type) {

	case SPANS_STAT_REQ:
		/*
		 * Handle a request from a host at the other end of
		 * the link.
		 */
		spans_host_link(spp, msg->sm_stat_req.streq_es_epoch);
		break;

	case SPANS_STAT_IND:

		/*
		 * There's a switch at the other end of the link.  If
		 * its epoch has changed, reset the SPANS state and save
		 * the new information.
		 */
		if (spp->sp_s_epoch !=
				msg->sm_stat_ind.stind_sw_epoch) {
			spans_switch_reset(spp, SPANS_UNI_UP);
			spp->sp_s_epoch =
				msg->sm_stat_ind.stind_sw_epoch;
			spp->sp_addr.address_format = T_ATM_SPANS_ADDR;
			spp->sp_addr.address_length =
					sizeof(spans_addr);
			spans_addr_copy(&msg->sm_stat_ind.stind_es_addr,
				spp->sp_addr.address);
			log(LOG_INFO,
		"spans: received SPANS address %s from switch for interface %s%d\n",
					spans_addr_print((spans_addr *)spp->sp_addr.address),
					pip->pif_name,
					pip->pif_unit);
		}
		break;

	default:
		ATM_DEBUG1("spans_status_ind: Invalid message type %d\n",
				msg->sm_type);
		return;
	}

	/*
	 * Respond to the status request or indication with a
	 * status response
	 */
	rsp_msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp_msg == NULL)
		return;
	rsp_msg->sm_vers = SPANS_VERS_1_0;
	rsp_msg->sm_type = SPANS_STAT_RSP;
	rsp_msg->sm_stat_rsp.strsp_es_epoch = spp->sp_h_epoch;
	spans_addr_copy(spp->sp_addr.address,
			&rsp_msg->sm_stat_rsp.strsp_es_addr);
	spans_send_msg(spp, rsp_msg);
	uma_zfree(spans_msg_zone, rsp_msg);
}



/*
 * Process a status response
 *
 * Called when a status response is received.
 * Processing will be based on the current SPANS state.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the status response message
 *
 * Returns:
 *	none
 *
 */
static void
spans_status_rsp(spp, msg)
	struct	spans	*spp;
	spans_msg	*msg;
{

	/*
	 * Reset the probe count.
	 */
	spp->sp_probe_ct = 0;

	switch (spp->sp_state) {
	case SPANS_PROBE:
		/*
		 * Interface just came up, update signalling state
		 */
		spp->sp_state = SPANS_ACTIVE;
		break;

	case SPANS_ACTIVE:
		break;

	default:
		log(LOG_ERR, "spans: received status msg in state %d\n",
				spp->sp_state);
	}

	/*
	 * Process the message
	 */
	spans_host_link(spp, msg->sm_stat_req.streq_es_epoch);
}


/*
 * Process an open indication or open request
 *
 * Called when an open indication or open request is received.
 * Processing will be based on the state of the requested connection.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the open message
 *
 * Returns:
 *	none
 *
 */
static void
spans_open_req(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	spans_result		result = SPANS_OK;
	spans_msg		*rsp_msg;
	struct spans_vccb	*svp = NULL;
	struct atm_pif		*pip;
	spans_vpvc		vpvc;
	int			err = 0, vpi, vci;
	Aal_t			aal;
	Atm_attributes		call_attrs;

	ATM_DEBUG2("spans_open_req: spp=%p, msg=%p\n", spp, msg);

	/*
	 * See if the connection is new
	 */
	if ((svp = spans_find_conn(spp, &msg->sm_open_req.opreq_conn)) != NULL) {
		/*
		 * We already have a VCCB that matches the connection in
		 * the request
		 */
		vpi = SPANS_EXTRACT_VPI(msg->sm_open_req.opreq_vpvc.vpf_vpvc);
		vci = SPANS_EXTRACT_VCI(msg->sm_open_req.opreq_vpvc.vpf_vpvc);
		if (msg->sm_open_req.opreq_aal == svp->sv_spans_aal &&
				(!msg->sm_open_req.opreq_vpvc.vpf_valid ||
					(vpi == svp->sv_vpi &&
					vci == svp->sv_vci))) {
			/*
			 * VCCB already exists, process depending on
			 * state
			 */
			switch (svp->sv_sstate) {
			case SPANS_VC_R_POPEN:
				/* I'm still thinking about it */
				return;
			case SPANS_VC_OPEN:
				/* Retransmit the open_rsp */
				break;
			case SPANS_VC_POPEN:
			case SPANS_VC_CLOSE:
			case SPANS_VC_ABORT:
				ATM_DEBUG0("spans_open_req: bad VCCB state\n");
				result = SPANS_FAIL;
				break;
			}
		} else {
			/*
			 * VCCB is for same connection, but other
			 * parameters don't match
			 */
			ATM_DEBUG0("spans_open_req: VCCB confusion\n");
			result = SPANS_FAIL;
		}
		svp = NULL;
		goto response;
	}

	/*
	 * Verify that the request is for our ATM addres
	 */
	if (spans_addr_cmp(spp->sp_addr.address,
			&msg->sm_open_req.opreq_conn.con_dst)) {
		ATM_DEBUG0("spans_open_req: bad destination\n");
		result = SPANS_BADDEST;
		goto response;
	}

	/*
	 * See if we recognize the specified AAL
	 */
	if (!spans_get_local_aal(msg->sm_open_req.opreq_aal, &aal)) {
		ATM_DEBUG0("spans_open_req: bad AAL\n");
		result = SPANS_FAIL;
		goto response;
	}

	/*
	 * Should verify that we can handle requested connection QOS
	 */

	/*
	 * Select a VPI/VCI for the new connection
	 */
	if (msg->sm_open_req.opreq_vpvc.vpf_valid) {
		/*
		 * Requestor asked for a certain VPI/VCI.  Make sure we
		 * aren't already using the pair that was asked for.
		 */
		vpi = SPANS_EXTRACT_VPI(msg->sm_open_req.opreq_vpvc.vpf_vpvc);
		vci = SPANS_EXTRACT_VCI(msg->sm_open_req.opreq_vpvc.vpf_vpvc);
		if (spans_find_vpvc(spp, vci, vpi, VCC_IN)) {
			ATM_DEBUG0("spans_open_req: VPI, VCI busy\n");
			result = SPANS_NOVPVC;
			goto response;
		}
		vpvc = msg->sm_open_req.opreq_vpvc.vpf_vpvc;
	} else {
		/*
		 * Allocate a VPI/VCI for this end of the VCC
		 */
		vpvc = spans_alloc_vpvc(spp);
		if (vpvc == 0) {
			ATM_DEBUG0("spans_open_req: no VPI, VCI available\n");
			result = SPANS_NOVPVC;
			goto response;
		}
	}

	/*
	 * Get a new VCCB for the connection
	 */
	svp = uma_zalloc(spans_vc_zone, M_WAITOK);
	if (svp == NULL) {
		ATM_DEBUG0("spans_open_req: VCCB pool empty\n");
		result = SPANS_NORSC;
		goto response;
	}

	/*
	 * Find the physical interface structure
	 */
	pip = spp->sp_pif;

	/*
	 * Fill in the VCCB fields that we can at this point
	 */
	svp->sv_type = VCC_SVC | VCC_IN;
	svp->sv_proto = ATM_SIG_SPANS;
	svp->sv_sstate = SPANS_VC_R_POPEN;
	svp->sv_ustate = VCCU_POPEN;
	svp->sv_pif = pip;
	svp->sv_nif = pip->pif_nif;
	svp->sv_conn = msg->sm_open_req.opreq_conn;
	svp->sv_spans_qos = msg->sm_open_req.opreq_desrsrc;
	svp->sv_spans_aal = msg->sm_open_req.opreq_aal;
	svp->sv_tstamp = time_second;

	svp->sv_vpi = SPANS_EXTRACT_VPI(vpvc);
	svp->sv_vci = SPANS_EXTRACT_VCI(vpvc);

	/*
	 * Put the VCCB on the SPANS queue
	 */
	ENQUEUE(svp, struct spans_vccb, sv_sigelem, spp->sp_vccq);

	/*
	 * Set up the ATM attributes block
	 */
	bzero(&call_attrs, sizeof(call_attrs));
	call_attrs.nif = svp->sv_nif;
	call_attrs.api = CMAPI_CPCS;

	call_attrs.aal.tag = T_ATM_PRESENT;
	call_attrs.aal.type = aal;
	switch(aal) {
	case ATM_AAL3_4:
		call_attrs.aal.v.aal4.forward_max_SDU_size =
				ATM_NIF_MTU;
		call_attrs.aal.v.aal4.backward_max_SDU_size =
				ATM_NIF_MTU;
		call_attrs.aal.v.aal4.SSCS_type =
				T_ATM_NULL;
		call_attrs.aal.v.aal4.mid_low = 0;
		call_attrs.aal.v.aal4.mid_high = 1023;
		break;
	case ATM_AAL5:
		call_attrs.aal.v.aal5.forward_max_SDU_size =
				ATM_NIF_MTU;
		call_attrs.aal.v.aal5.backward_max_SDU_size =
				ATM_NIF_MTU;
		call_attrs.aal.v.aal5.SSCS_type =
				T_ATM_NULL;
		break;
	}

	call_attrs.traffic.tag = T_ATM_PRESENT;
	call_attrs.traffic.v.forward.PCR_high_priority = T_ATM_ABSENT;
	call_attrs.traffic.v.forward.PCR_all_traffic = 
			msg->sm_open_req.opreq_desrsrc.rsc_peak *
			1000 / 53;
	call_attrs.traffic.v.forward.SCR_high_priority = T_ATM_ABSENT;
	call_attrs.traffic.v.forward.SCR_all_traffic = T_ATM_ABSENT;
	call_attrs.traffic.v.forward.MBS_high_priority = T_ATM_ABSENT;
	call_attrs.traffic.v.forward.MBS_all_traffic = T_ATM_ABSENT;
	call_attrs.traffic.v.forward.tagging = T_NO;
	call_attrs.traffic.v.backward.PCR_high_priority = T_ATM_ABSENT;
	call_attrs.traffic.v.backward.PCR_all_traffic = 
			call_attrs.traffic.v.forward.PCR_all_traffic;
	call_attrs.traffic.v.backward.SCR_high_priority = T_ATM_ABSENT;
	call_attrs.traffic.v.backward.SCR_all_traffic = T_ATM_ABSENT;
	call_attrs.traffic.v.backward.MBS_high_priority = T_ATM_ABSENT;
	call_attrs.traffic.v.backward.MBS_all_traffic = T_ATM_ABSENT;
	call_attrs.traffic.v.backward.tagging = T_NO;
	call_attrs.traffic.v.best_effort = T_YES;

	call_attrs.bearer.tag = T_ATM_PRESENT;
	call_attrs.bearer.v.bearer_class = T_ATM_CLASS_X;
	call_attrs.bearer.v.traffic_type = T_ATM_NULL;
	call_attrs.bearer.v.timing_requirements = T_ATM_NULL;
	call_attrs.bearer.v.clipping_susceptibility = T_NO;
	call_attrs.bearer.v.connection_configuration = T_ATM_1_TO_1;


	call_attrs.bhli.tag = T_ATM_ABSENT;
	call_attrs.blli.tag_l2 = T_ATM_ABSENT;
	call_attrs.blli.tag_l3 = T_ATM_ABSENT;
	call_attrs.llc.tag = T_ATM_ABSENT;

	call_attrs.called.tag = T_ATM_PRESENT;
	spans_addr_copy(&msg->sm_open_req.opreq_conn.con_dst,
		call_attrs.called.addr.address);
	call_attrs.called.addr.address_format = T_ATM_SPANS_ADDR;
	call_attrs.called.addr.address_length = sizeof(spans_addr);
	call_attrs.called.subaddr.address_format = T_ATM_ABSENT;
	call_attrs.called.subaddr.address_length = 0;

	call_attrs.calling.tag = T_ATM_PRESENT;
	spans_addr_copy(&msg->sm_open_req.opreq_conn.con_src,
		call_attrs.calling.addr.address);
	call_attrs.calling.addr.address_format = T_ATM_SPANS_ADDR;
	call_attrs.calling.addr.address_length = sizeof(spans_addr);
	call_attrs.calling.subaddr.address_format = T_ATM_ABSENT;
	call_attrs.calling.subaddr.address_length = 0;

	call_attrs.qos.tag = T_ATM_PRESENT;
	call_attrs.qos.v.coding_standard = T_ATM_NETWORK_CODING;
	call_attrs.qos.v.forward.qos_class = T_ATM_QOS_CLASS_0;
	call_attrs.qos.v.backward.qos_class = T_ATM_QOS_CLASS_0;

	call_attrs.transit.tag = T_ATM_ABSENT;
	call_attrs.cause.tag = T_ATM_ABSENT;

	/*
	 * Notify the connection manager that it has a new channel
	 */
	err = atm_cm_incoming((struct vccb *)svp, &call_attrs);
	if (err) {
		ATM_DEBUG0("spans_open_req: atm_cm_incoming returned error\n");
		result = SPANS_FAIL;
		goto response;
	}

	/*
	 * Wait for the connection recipient to issue an accept
	 */
	return;

response:
	/*
	 * Clean up the VCCB and the atm_conn block if we got them
	 */
	if (svp) {
		DEQUEUE(svp, struct spans_vccb, sv_sigelem,
				spp->sp_vccq);
		uma_zfree(spans_vc_zone, svp);
	}

	/*
	 * Some problem was detected with the request.  Send a SPANS
	 * message rejecting the connection.
	 */
	rsp_msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp_msg == NULL)
		return;

	/*
	 * Fill out the response
	 */
	rsp_msg->sm_vers = SPANS_VERS_1_0;
	rsp_msg->sm_type = SPANS_OPEN_RSP;
	rsp_msg->sm_open_rsp.oprsp_conn = msg->sm_open_req.opreq_conn;
	rsp_msg->sm_open_rsp.oprsp_result = result;
	rsp_msg->sm_open_rsp.oprsp_vpvc = 0;

	/*
	 * Send the Open Response
	 */
	spans_send_msg(spp, rsp_msg);
	uma_zfree(spans_msg_zone, rsp_msg);
}


/*
 * Process an open response or open confirmation
 *
 * Called when an open response or open confirmation is received.
 * Processing will be based on the state of the requested connection and
 * the status returned.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the open response or confirmation message
 *
 * Returns:
 *	none
 *
 */
static void
spans_open_rsp(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	struct spans_vccb	*svp;

	ATM_DEBUG2("spans_open_rsp: spp=%p, msg=%p\n", spp, msg);

	/*
	 * Locate the VCCB for the connection
	 */
	svp = spans_find_conn(spp, &msg->sm_open_rsp.oprsp_conn);
	if (svp == NULL)
		return;

	/*
	 * Check the connection state
	 */
	if ((svp->sv_sstate != SPANS_VC_POPEN &&
			svp->sv_sstate != SPANS_VC_R_POPEN) ||
			svp->sv_ustate != VCCU_POPEN) {
		ATM_DEBUG2(
	"spans_open_rsp: invalid VCCB state, sstate=%d, ustate=%d\n",
				svp->sv_sstate, svp->sv_ustate);
		return;
	}

	/*
	 * Cancel the retransmission timer
	 */
	SPANS_VC_CANCEL((struct vccb *) svp);

	/*
	 * Check the result
	 */
	switch (msg->sm_open_rsp.oprsp_result) {

	case SPANS_OK:
		/*
		 * Save the assigned VPI and VCI
		 */
		svp->sv_vpi = SPANS_EXTRACT_VPI(msg->sm_open_rsp.oprsp_vpvc);
		svp->sv_vci = SPANS_EXTRACT_VCI(msg->sm_open_rsp.oprsp_vpvc);

		/*
		 * Update the VCC state and notify the VCC owner
		 */
		svp->sv_sstate = SPANS_VC_OPEN;
		svp->sv_ustate = VCCU_OPEN;
		svp->sv_tstamp = time_second;
		atm_cm_connected(svp->sv_connvc);
		break;

	case SPANS_FAIL:
	case SPANS_NOVPVC:
	case SPANS_NORSC:
	case SPANS_BADDEST:
		/*
		 * Close out the VCCB and notify the user
		 */
		svp->sv_sstate = SPANS_VC_FREE;
		svp->sv_ustate = VCCU_CLOSED;
		svp->sv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT;
		svp->sv_connvc->cvc_attr.cause.v.coding_standard =
				T_ATM_ITU_CODING;
		svp->sv_connvc->cvc_attr.cause.v.location =
				T_ATM_LOC_USER;
		svp->sv_connvc->cvc_attr.cause.v.cause_value =
				T_ATM_CAUSE_CALL_REJECTED;
		bzero(svp->sv_connvc->cvc_attr.cause.v.diagnostics,
				sizeof(svp->sv_connvc->cvc_attr.cause.v.diagnostics));
		atm_cm_cleared(svp->sv_connvc);
		break;

	default:
		log(LOG_ERR, "spans: unknown result %d in open rsp\n",
				msg->sm_open_rsp.oprsp_result);
		break;
	}
}


/*
 * Process a close request from the network
 *
 * Called when a close request, close indication, rclose request, or
 * rclose indication is received.  Processing will be based on the
 * state of the connection.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the close request message
 *
 * Returns:
 *	none
 *
 */
static void
spans_close_req(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	struct spans_vccb	*svp;
	spans_result		result;
	spans_msg		*rsp_msg;
	u_char			outstate;
	Atm_connvc		*cvp;

	ATM_DEBUG2("spans_close_req: spp=%p, msg=%p\n", spp, msg);

	/*
	 * Locate the VCCB for the connection
	 */
	svp = spans_find_conn(spp, &msg->sm_close_req.clreq_conn);
	if (svp == NULL) {
		result = SPANS_BADDEST;
		goto response;
	}

	/*
	 * Check the connection type
	 */
	if (!(svp->sv_type & VCC_SVC)) {
		result = SPANS_FAIL;
		goto response;
	}

	/*
	 * Check the connection state
	 */
	switch (svp->sv_sstate) {
	case SPANS_VC_OPEN:
	case SPANS_VC_R_POPEN:
	case SPANS_VC_POPEN:
		/*
		 * VCC is open or opening--continue
		 */
		break;
	case SPANS_VC_CLOSE:
	case SPANS_VC_FREE:
	case SPANS_VC_ABORT:
		/*
		 * We're already closing--give a response, since this
		 * is probably a retransmission
		 */
		result = SPANS_OK;
		goto response;
	case SPANS_VC_NULL:
		result = SPANS_FAIL;
		goto response;
	}

	/*
	 * Cancel the retransmission timer
	 */
	SPANS_VC_CANCEL((struct vccb *) svp);

	/*
	 * Close out the VCCB and notify the user
	 */
	outstate = svp->sv_sstate;
	svp->sv_ustate = VCCU_CLOSED;
	svp->sv_sstate = SPANS_VC_FREE;
	cvp = svp->sv_connvc;
	switch (outstate) {
	case SPANS_VC_R_POPEN:
		spans_free((struct vccb *)svp);
		/* FALLTHRU */

	case SPANS_VC_POPEN:
	case SPANS_VC_OPEN:
		cvp->cvc_attr.cause.tag = T_ATM_PRESENT;
		cvp->cvc_attr.cause.v.coding_standard =
				T_ATM_ITU_CODING;
		cvp->cvc_attr.cause.v.location = T_ATM_LOC_USER;
		cvp->cvc_attr.cause.v.cause_value =
				T_ATM_CAUSE_NORMAL_CALL_CLEARING;
		bzero(cvp->cvc_attr.cause.v.diagnostics,
				sizeof(cvp->cvc_attr.cause.v.diagnostics));
		atm_cm_cleared(svp->sv_connvc);
		break;
	}

	result = SPANS_OK;

response:
	/*
	 * Respond to the SPANS_CLOSE_IND with a SPANS_CLOSE_RSP
	 */
	rsp_msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp_msg == NULL)
		return;
	rsp_msg->sm_vers = SPANS_VERS_1_0;
	if (msg->sm_type == SPANS_RCLOSE_REQ ||
			msg->sm_type == SPANS_RCLOSE_IND) {
		rsp_msg->sm_type = SPANS_RCLOSE_RSP;
	} else {
		rsp_msg->sm_type = SPANS_CLOSE_RSP;
	}
	rsp_msg->sm_close_rsp.clrsp_conn = msg->sm_close_req.clreq_conn;
	rsp_msg->sm_close_rsp.clrsp_result = result;
	spans_send_msg(spp, rsp_msg);
	uma_zfree(spans_msg_zone, rsp_msg);
}


/*
 * Process a close response or close confirmation
 *
 * Called when a close response or close confirmation is received.
 * Processing will be based on the state of the requested connection and
 * the returned status.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the close response or confirmation message
 *
 * Returns:
 *	none
 *
 */
static void
spans_close_rsp(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	struct spans_vccb	*svp;

	ATM_DEBUG2("spans_close_rsp: spp=%p, msg=%p\n", spp, msg);

	/*
	 * Locate the VCCB for the connection
	 */
	svp = spans_find_conn(spp, &msg->sm_close_rsp.clrsp_conn);
	if (svp == NULL) {
		return;
	}

	/*
	 * Check the VCCB state
	 */
	if (svp->sv_sstate != SPANS_VC_CLOSE) {
		return;
	}

	/*
	 * Cancel the retransmission timer
	 */
	SPANS_VC_CANCEL((struct vccb *) svp);

	/*
	 * Check the response from the remote end
	 */
	switch (msg->sm_close_rsp.clrsp_result) {

	case SPANS_OK:
		/*
		 * Mark the VCCB as closed and notify the owner
		 */
		svp->sv_sstate = SPANS_VC_FREE;
		svp->sv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT;
		svp->sv_connvc->cvc_attr.cause.v.coding_standard =
				T_ATM_ITU_CODING;
		svp->sv_connvc->cvc_attr.cause.v.location =
				T_ATM_LOC_USER;
		svp->sv_connvc->cvc_attr.cause.v.cause_value =
				T_ATM_CAUSE_NORMAL_CALL_CLEARING;
		bzero(svp->sv_connvc->cvc_attr.cause.v.diagnostics,
				sizeof(svp->sv_connvc->cvc_attr.cause.v.diagnostics));
		atm_cm_cleared(svp->sv_connvc);
		break;

	case SPANS_NOVPVC:
	case SPANS_BADDEST:
	case SPANS_FAIL:
	case SPANS_NORSC:
		/*
		 * Mark the VCCB as closed and notify the owner
		 */
		svp->sv_sstate = SPANS_VC_FREE;
		svp->sv_connvc->cvc_attr.cause.tag = T_ATM_PRESENT;
		svp->sv_connvc->cvc_attr.cause.v.coding_standard =
				T_ATM_ITU_CODING;
		svp->sv_connvc->cvc_attr.cause.v.location =
				T_ATM_LOC_USER;
		svp->sv_connvc->cvc_attr.cause.v.cause_value =
				T_ATM_CAUSE_UNSPECIFIED_NORMAL;
		bzero(svp->sv_connvc->cvc_attr.cause.v.diagnostics,
				sizeof(svp->sv_connvc->cvc_attr.cause.v.diagnostics));
		atm_cm_cleared(svp->sv_connvc);
		break;

	default:
		log(LOG_ERR, "spans: unknown result %d in close rsp\n",
				msg->sm_close_rsp.clrsp_result);
		break;
	}
}


/*
 * Process a multi request or multi indication
 *
 * Called when a multi response or multi confirmation is received.  We
 * don't support multicast channels, so we just reject the request.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the multi request or indication message
 *
 * Returns:
 *	none
 *
 */
static void
spans_multi_req(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	spans_msg	*rsp_msg;

	/*
	 * Get memory for a SPANS_MULTI_RSP message.
	 */
	rsp_msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp_msg == NULL)
		return;

	/*
	 * Fill out the response.
	 */
	rsp_msg->sm_vers = SPANS_VERS_1_0;
	rsp_msg->sm_type = SPANS_MULTI_RSP;
	rsp_msg->sm_multi_rsp.mursp_conn = msg->sm_multi_req.mureq_conn;
	rsp_msg->sm_multi_rsp.mursp_result = SPANS_FAIL;
	rsp_msg->sm_multi_rsp.mursp_rsrc = msg->sm_multi_req.mureq_desrsrc;
	rsp_msg->sm_multi_rsp.mursp_vpvc = 0;

	/*
	 * Send the response and free the message.
	 */
	(void) spans_send_msg(spp, rsp_msg);
	uma_zfree(spans_msg_zone, rsp_msg);
}


/*
 * Process an add request or add indication
 *
 * Called when an add response or add confirmation is received.  We
 * don't support multicast channels, so we just reject the request.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the add request or indication message
 *
 * Returns:
 *	none
 *
 */
static void
spans_add_req(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	spans_msg	*rsp_msg;

	/*
	 * Get memory for a SPANS_ADD_RSP message.
	 */
	rsp_msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp_msg == NULL)
		return;

	/*
	 * Fill out the response.
	 */
	rsp_msg->sm_vers = SPANS_VERS_1_0;
	rsp_msg->sm_type = SPANS_ADD_RSP;
	rsp_msg->sm_add_rsp.adrsp_conn = msg->sm_add_req.adreq_desconn;
	rsp_msg->sm_add_rsp.adrsp_result = SPANS_FAIL;
	rsp_msg->sm_add_rsp.adrsp_rsrc.rsc_peak = 0;
	rsp_msg->sm_add_rsp.adrsp_rsrc.rsc_mean = 0;
	rsp_msg->sm_add_rsp.adrsp_rsrc.rsc_burst = 0;

	/*
	 * Send the response and free the message.
	 */
	(void) spans_send_msg(spp, rsp_msg);
	uma_zfree(spans_msg_zone, rsp_msg);
}


/*
 * Process a join request
 *
 * Called when a join request is received.  We don't support group
 * addresses, so we just reject the request.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the join request message
 *
 * Returns:
 *	none
 *
 */
static void
spans_join_req(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	spans_msg	*rsp_msg;

	/*
	 * Get memory for a SPANS_JOIN_CNF message.
	 */
	rsp_msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp_msg == NULL)
		return;

	/*
	 * Fill out the response.
	 */
	rsp_msg->sm_vers = SPANS_VERS_1_0;
	rsp_msg->sm_type = SPANS_JOIN_CNF;
	spans_addr_copy(&msg->sm_join_req.jnreq_addr,
			&rsp_msg->sm_join_cnf.jncnf_addr);
	rsp_msg->sm_join_cnf.jncnf_result = SPANS_FAIL;

	/*
	 * Send the response and free the message.
	 */
	(void) spans_send_msg(spp, rsp_msg);
	uma_zfree(spans_msg_zone, rsp_msg);
}


/*
 * Process a leave request
 *
 * Called when a leave request is received.  We don't support group
 * addresses, so we just reject the request.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the leave request message
 *
 * Returns:
 *	none
 *
 */
static void
spans_leave_req(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	spans_msg	*rsp_msg;

	/*
	 * Get memory for a SPANS_LEAVE_CNF message.
	 */
	rsp_msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp_msg == NULL)
		return;

	/*
	 * Fill out the response.
	 */
	rsp_msg->sm_vers = SPANS_VERS_1_0;
	rsp_msg->sm_type = SPANS_LEAVE_CNF;
	spans_addr_copy(&msg->sm_leave_req.lvreq_addr,
			&rsp_msg->sm_leave_cnf.lvcnf_addr);
	rsp_msg->sm_leave_cnf.lvcnf_result = SPANS_FAIL;

	/*
	 * Send the response and free the message.
	 */
	(void) spans_send_msg(spp, rsp_msg);
	uma_zfree(spans_msg_zone, rsp_msg);
}


/*
 * Process a VCI range indication
 *
 * Called when a VCI range indication is received.  Adjust the VCI
 * bounds if they have changed.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the VCI range indication message
 *
 * Returns:
 *	none
 *
 */
static void
spans_vcir_ind(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	/*
	 * Adjust the limits if they have changed
	 */
	if (msg->sm_vcir_ind.vrind_min != spp->sp_min_vci) {
		spp->sp_min_vci =
				(msg->sm_vcir_ind.vrind_min <
				SPANS_MIN_VCI ?
				SPANS_MIN_VCI :
				msg->sm_vcir_ind.vrind_min);
	}
	if (msg->sm_vcir_ind.vrind_max != spp->sp_max_vci) {
		spp->sp_max_vci =
				(msg->sm_vcir_ind.vrind_max >
				SPANS_MAX_VCI ?
				SPANS_MAX_VCI :
				msg->sm_vcir_ind.vrind_max);
	}
}


/*
 * Process a query request
 *
 * Called when a query request is received.  Respond with the
 * appropriate query response.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	msg	pointer to the VCI range indication message
 *
 * Returns:
 *	none
 *
 */
static void
spans_query_req(spp, msg)
	struct spans	*spp;
	spans_msg	*msg;
{
	struct spans_vccb	*svp = NULL;
	spans_msg		*rsp_msg;

	ATM_DEBUG1("spans_query_req: msg=%p\n", msg);

	/*
	 * Ignore an end-to-end query
	 */
	if (msg->sm_query_req.qyreq_type == SPANS_QUERY_END_TO_END) {
		return;
	}

	/*
	 * Get memory for a SPANS_QUERY_RSP message.
	 */
	rsp_msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (rsp_msg == NULL)
		return;

	/*
	 * Fill out the response.
	 */
	rsp_msg->sm_vers = SPANS_VERS_1_0;
	rsp_msg->sm_type = SPANS_QUERY_RSP;
	rsp_msg->sm_query_rsp.qyrsp_conn = msg->sm_query_req.qyreq_conn;
	rsp_msg->sm_query_rsp.qyrsp_type = msg->sm_query_req.qyreq_type;
	rsp_msg->sm_query_rsp.qyrsp_data = 0;

	/*
	 * Get the state of the requested connection
	 */
	svp = spans_find_conn(spp, &msg->sm_query_req.qyreq_conn);
	if (svp) {
		switch(svp->sv_sstate) {
		case SPANS_VC_NULL:
		case SPANS_VC_FREE:
			rsp_msg->sm_query_rsp.qyrsp_state =
				SPANS_CONN_CLOSED;
			break;
		case SPANS_VC_OPEN:
			rsp_msg->sm_query_rsp.qyrsp_state =
				SPANS_CONN_OPEN;
			break;
		case SPANS_VC_POPEN:
		case SPANS_VC_R_POPEN:
			rsp_msg->sm_query_rsp.qyrsp_state =
				SPANS_CONN_OPEN_PEND;
			break;
		case SPANS_VC_CLOSE:
		case SPANS_VC_ABORT:
			rsp_msg->sm_query_rsp.qyrsp_state =
				SPANS_CONN_CLOSE_PEND;
			break;
		case SPANS_VC_ACTIVE:
		case SPANS_VC_ACT_DOWN:
			/*
			 * VCCB is for a PVC (shouldn't happen)
			 */
			uma_zfree(spans_msg_zone, rsp_msg);
			return;
		}
	} else {
		/*
		 * No VCCB found--connection doesn't exist
		 */
		rsp_msg->sm_query_rsp.qyrsp_state = SPANS_CONN_CLOSED;
	}

	/*
	 * Send the response and free the message.
	 */
	(void) spans_send_msg(spp, rsp_msg);
	uma_zfree(spans_msg_zone, rsp_msg);
}


/*
 * Process a SPANS signalling message
 *
 * Called when a SPANS message is received.  The message is converted
 * into internal format with XDR and decoded by calling the appropriate
 * mesage handling routine.  Unrecognized and unexpected messages are
 * logged.
 *
 * Arguments:
 *	spp	pointer to SPANS protocol instance block
 *	m	pointer to a buffer chain containing the SPANS message
 *
 * Returns:
 *	none
 *
 */
void
spans_rcv_msg(spp, m)
	struct spans	*spp;
	KBuffer		*m;
{
	XDR		xdrs;
	spans_msg	*msg;

	/*
	 * Get storage for the message
	 */
	msg = uma_zalloc(spans_msg_zone, M_WAITOK);
	if (msg == NULL)
		return;

	/*
	 * Convert the message from network order to internal format
	 */
	xdrmbuf_init(&xdrs, m, XDR_DECODE);
	if (!xdr_spans_msg(&xdrs, msg)) {
		log(LOG_ERR, "spans_rcv_msg: XDR decode failed\n");
		spans_dump_buffer(m);
		goto done;
	}

#ifdef NOTDEF
	/*
	 * Debug--print some information about the message
	 */
	if (msg->sm_type != SPANS_STAT_REQ &&
			msg->sm_type != SPANS_STAT_IND &&
			msg->sm_type != SPANS_STAT_RSP) {
		printf("spans_rcv_msg: got ");
		spans_print_msg(msg);
	}
#endif

	/*
	 * Verify the message sm_vers
	 */
	if (msg->sm_vers != SPANS_VERS_1_0) {
		log(LOG_ERR, "spans: invalid message version 0x%x\n",
				msg->sm_vers);
	}

	/*
	 * Ignore the message if SPANS isn't up yet
	 */
	if (spp->sp_state != SPANS_ACTIVE &&
			(spp->sp_state != SPANS_PROBE ||
			(msg->sm_type != SPANS_STAT_REQ &&
			msg->sm_type != SPANS_STAT_RSP &&
			msg->sm_type != SPANS_STAT_IND))) {
		goto done;
	}

	/*
	 * Process the message based on its type
	 */
	switch(msg->sm_type) {
	case SPANS_STAT_REQ:
		spans_status_ind(spp, msg);
		break;
	case SPANS_STAT_IND:
		spans_status_ind(spp, msg);
		break;
	case SPANS_STAT_RSP:
		spans_status_rsp(spp, msg);
		break;
	case SPANS_OPEN_REQ:
		spans_open_req(spp, msg);
		break;
	case SPANS_OPEN_IND:
		spans_open_req(spp, msg);
		break;
	case SPANS_OPEN_RSP:
		spans_open_rsp(spp, msg);
		break;
	case SPANS_OPEN_CNF:
		spans_open_rsp(spp, msg);
		break;
	case SPANS_CLOSE_REQ:
		spans_close_req(spp, msg);
		break;
	case SPANS_CLOSE_IND:
		spans_close_req(spp, msg);
		break;
	case SPANS_CLOSE_RSP:
		spans_close_rsp(spp, msg);
		break;
	case SPANS_CLOSE_CNF:
		spans_close_rsp(spp, msg);
		break;
	case SPANS_RCLOSE_REQ:
		spans_close_req(spp, msg);
		break;
	case SPANS_RCLOSE_IND:
		spans_close_req(spp, msg);
		break;
	case SPANS_RCLOSE_RSP:
		spans_close_rsp(spp, msg);
		break;
	case SPANS_RCLOSE_CNF:
		spans_close_rsp(spp, msg);
		break;
	case SPANS_MULTI_REQ:
		spans_multi_req(spp, msg);
		break;
	case SPANS_MULTI_IND:
		spans_multi_req(spp, msg);
		break;
	case SPANS_MULTI_RSP:
		log(LOG_ERR,
			"spans: unexpected message (multi_rsp)\n");
		break;
	case SPANS_MULTI_CNF:
		log(LOG_ERR,
			"spans: unexpected message (multi_conf)\n");
		break;
	case SPANS_ADD_REQ:
		spans_add_req(spp, msg);
		break;
	case SPANS_ADD_IND:
		spans_add_req(spp, msg);
		break;
	case SPANS_ADD_RSP:
		log(LOG_ERR,
			"spans: unexpected message (add_rsp)\n");
		break;
	case SPANS_ADD_CNF:
		log(LOG_ERR, "spans: unexpected message (add_conf)\n");
		break;
	case SPANS_JOIN_REQ:
		spans_join_req(spp, msg);
		break;
	case SPANS_JOIN_CNF:
		log(LOG_ERR, "spans: unexpected message (join_conf)\n");
		break;
	case SPANS_LEAVE_REQ:
		spans_leave_req(spp, msg);
		break;
	case SPANS_LEAVE_CNF:
		log(LOG_ERR,
			"spans: unexpected message (leave_conf)\n");
		break;
	case SPANS_VCIR_IND:
		spans_vcir_ind(spp, msg);
		break;
	case SPANS_QUERY_REQ:
		spans_query_req(spp, msg);
		break;
	case SPANS_QUERY_RSP:
		log(LOG_ERR,
			"spans: unexpected message (query_rsp)\n");
		break;
	default:
		log(LOG_ERR, "spans: unknown SPANS message type %d\n",
				msg->sm_type);
	}

done:
	/*
	 * Free the incoming message (both buffer and internal format) if
	 * necessary.
	 */
	if (msg)
		uma_zfree(spans_msg_zone, msg);
	if (m)
		KB_FREEALL(m);
}
