freebsd-dev/sys/netatm/spans/spans_msg.c
2003-02-19 05:47:46 +00:00

1651 lines
37 KiB
C

/*
*
* ===================================
* 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 "spans_xdr.h"
#include <netatm/spans/spans_var.h>
#ifndef lint
__RCSID("@(#) $FreeBSD$");
#endif
/*
* 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);
}