aboutsummaryrefslogblamecommitdiff
path: root/sys/netatm/spans/spans_msg.c
blob: 686eb537b7616a7e7c3c32d5eb7c81c161216a2a (plain) (tree)
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
























                                                                        
                      










                                       







                          













                              




                                   
            
                          

      


























































































                                                                             
                                                            





































































                                                                       
                                                         
























































                                                                    
                                                                   


















































                                                                    
                                                          





























































































































































































































                                                                                             
                                                                 



                                       
                                                                                 






























































































































































































































































































                                                                               
                                                                 




































































































                                                                                      
                                                                  






















































































































                                                                           
                                                                  


























































































































































































































































































































                                                                                      
                                                     


































































































































































































































































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

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

#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 <rpc/rpc.h>
#include "spans_xdr.h"
#include <netatm/spans/spans_var.h>

#ifndef lint
__RCSID("@(#) $FreeBSD$");
#endif

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

/*
 * Local functions
 */
static void	spans_host_link __P((struct spans *, long));
static void	spans_status_ind __P((struct spans *, spans_msg *));
static void	spans_status_rsp __P((struct spans *, spans_msg *));
static void	spans_open_req __P((struct spans *, spans_msg *));
static void	spans_open_rsp __P((struct spans *, spans_msg *));
static void	spans_close_req __P((struct spans *, spans_msg *));
static void	spans_close_rsp __P((struct spans *, spans_msg *));
static void	spans_multi_req __P((struct spans *, spans_msg *));
static void	spans_add_req __P((struct spans *, spans_msg *));
static void	spans_join_req __P((struct spans *, spans_msg *));
static void	spans_leave_req __P((struct spans *, spans_msg *));
static void	spans_vcir_ind __P((struct spans *, spans_msg *));
static void	spans_query_req __P((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);
		KM_COPY(&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 = (spans_msg *)atm_allocate(&spans_msgpool);
	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);
	atm_free(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 = (spans_msg *)atm_allocate(&spans_msgpool);
	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);
	atm_free(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 = (spans_msg *)atm_allocate(&spans_msgpool);
	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)
		atm_free(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 = (spans_msg *)atm_allocate(&spans_msgpool);
	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);
	atm_free(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 = (struct spans_vccb *)atm_allocate(&spans_vcpool);
	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
	 */
	KM_ZERO(&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);
		atm_free(svp);
	}

	/*
	 * Some problem was detected with the request.  Send a SPANS
	 * message rejecting the connection.
	 */
	rsp_msg = (spans_msg *) atm_allocate(&spans_msgpool);
	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);
	atm_free(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;
		KM_ZERO(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;
		KM_ZERO(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 = (spans_msg *)atm_allocate(&spans_msgpool);
	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);
	atm_free(rsp_msg);
}


/*
 * Process a close response or close confirmation
 *
 * Called when an 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;
		KM_ZERO(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;
		KM_ZERO(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 = (spans_msg *) atm_allocate(&spans_msgpool);
	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);
	atm_free(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 = (spans_msg *) atm_allocate(&spans_msgpool);
	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);
	atm_free(rsp_msg);
}


/*
 * Process a join request
 *
 * Called when an 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 = (spans_msg *) atm_allocate(&spans_msgpool);
	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);
	atm_free(rsp_msg);
}


/*
 * Process a leave request
 *
 * Called when an 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 = (spans_msg *) atm_allocate(&spans_msgpool);
	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);
	atm_free(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 = (spans_msg *) atm_allocate(&spans_msgpool);
	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)
			 */
			atm_free(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);
	atm_free(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 = (spans_msg *)atm_allocate(&spans_msgpool);
	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)
		atm_free(msg);
	if (m)
		KB_FREEALL(m);
}