509 lines
10 KiB
C
509 lines
10 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.
|
|
*/
|
|
|
|
/*
|
|
* SPANS Signalling Manager
|
|
* ---------------------------
|
|
*
|
|
* SPANS-related subroutines.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/systm.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>
|
|
|
|
/*
|
|
* Open a SPANS VCC
|
|
*
|
|
* Called when a user wants to open a VC. This function will construct
|
|
* a VCCB, create the stack requested by the user, and, if we are
|
|
* opening an SVC, start the SPANS signalling message exchange. The
|
|
* user will have to wait for a notify event to be sure the SVC is fully
|
|
* open.
|
|
*
|
|
* Must be called at splnet.
|
|
*
|
|
* Arguments:
|
|
* spp pointer to SPANS protocol instance
|
|
* acp pointer to PVC's connection parameters
|
|
*
|
|
* Returns:
|
|
* 0 VCC creation successful
|
|
* errno VCC setup failed - reason indicated
|
|
*
|
|
*/
|
|
int
|
|
spans_open_vcc(spp, cvp)
|
|
struct spans *spp;
|
|
Atm_connvc *cvp;
|
|
|
|
{
|
|
struct atm_pif *pip = spp->sp_pif;
|
|
struct spans_vccb *svp;
|
|
Atm_addr_pvc *pvp;
|
|
spans_aal aal;
|
|
int err, pvc, vpi, vci;
|
|
|
|
ATM_DEBUG2("spans_open_vcc: spp=%p, cvp=%p\n", spp, cvp);
|
|
|
|
/*
|
|
* Validate user parameters. AAL and encapsulation are
|
|
* checked by the connection manager.
|
|
*/
|
|
|
|
/*
|
|
* Check called party address(es)
|
|
*/
|
|
if (cvp->cvc_attr.called.tag != T_ATM_PRESENT ||
|
|
cvp->cvc_attr.called.addr.address_format ==
|
|
T_ATM_ABSENT ||
|
|
cvp->cvc_attr.called.subaddr.address_format !=
|
|
T_ATM_ABSENT) {
|
|
return(EINVAL);
|
|
}
|
|
switch (cvp->cvc_attr.called.addr.address_format) {
|
|
case T_ATM_PVC_ADDR:
|
|
/*
|
|
* Make sure VPI/VCI is valid
|
|
*/
|
|
pvc = 1;
|
|
pvp = (Atm_addr_pvc *)cvp->cvc_attr.called.addr.address;
|
|
vpi = ATM_PVC_GET_VPI(pvp);
|
|
vci = ATM_PVC_GET_VCI(pvp);
|
|
if ((vpi > pip->pif_maxvpi) ||
|
|
(vci == 0) ||
|
|
(vci > pip->pif_maxvci)) {
|
|
return(ERANGE);
|
|
}
|
|
|
|
/*
|
|
* Make sure VPI/VCI is not already in use
|
|
*/
|
|
if (spans_find_vpvc(spp, vpi, vci, 0)) {
|
|
return(EADDRINUSE);
|
|
}
|
|
ATM_DEBUG2("spans_open_vcc: VPI.VCI=%d.%d\n",
|
|
vpi, vci);
|
|
break;
|
|
|
|
case T_ATM_SPANS_ADDR:
|
|
pvc = 0;
|
|
vpi = vci = 0;
|
|
|
|
/*
|
|
* Check signalling state
|
|
*/
|
|
if (spp->sp_state != SPANS_ACTIVE) {
|
|
return(ENETDOWN);
|
|
}
|
|
|
|
/*
|
|
*Check destination address length
|
|
*/
|
|
if (cvp->cvc_attr.called.addr.address_length !=
|
|
sizeof(spans_addr)) {
|
|
return(EINVAL);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return(EINVAL);
|
|
}
|
|
|
|
/*
|
|
* Check that this is for the same interface SPANS uses
|
|
*/
|
|
if (!cvp->cvc_attr.nif ||
|
|
cvp->cvc_attr.nif->nif_pif != spp->sp_pif) {
|
|
return(EINVAL);
|
|
}
|
|
|
|
/*
|
|
* Check AAL
|
|
*/
|
|
if (!spans_get_spans_aal(cvp->cvc_attr.aal.type, &aal)) {
|
|
return(EINVAL);
|
|
}
|
|
|
|
#ifdef NOTDEF
|
|
/*
|
|
* Check encapsulation
|
|
*/
|
|
/* XXX -- How do we check encapsulation? */
|
|
if (cvp->ac_encaps != ATM_ENC_NULL) {
|
|
return(EINVAL);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Allocate control block for VCC
|
|
*/
|
|
svp = uma_zalloc(spans_vc_zone, M_WAITOK);
|
|
if (svp == NULL) {
|
|
return(ENOMEM);
|
|
}
|
|
|
|
/*
|
|
* Fill in VCCB
|
|
*/
|
|
if (pvc) {
|
|
svp->sv_type = VCC_PVC | VCC_IN | VCC_OUT;
|
|
svp->sv_vpi = vpi;
|
|
svp->sv_vci = vci;
|
|
svp->sv_sstate = (spp->sp_state == SPANS_ACTIVE ?
|
|
SPANS_VC_ACTIVE : SPANS_VC_ACT_DOWN);
|
|
svp->sv_ustate = VCCU_OPEN;
|
|
} else {
|
|
svp->sv_type = VCC_SVC | VCC_OUT;
|
|
spans_addr_copy(cvp->cvc_attr.called.addr.address,
|
|
&svp->sv_conn.con_dst);
|
|
spans_addr_copy(spp->sp_addr.address,
|
|
&svp->sv_conn.con_src);
|
|
svp->sv_conn.con_dsap = SPANS_SAP_IP;
|
|
svp->sv_conn.con_ssap = spans_ephemeral_sap(spp);
|
|
svp->sv_sstate = SPANS_VC_POPEN;
|
|
svp->sv_ustate = VCCU_POPEN;
|
|
}
|
|
svp->sv_proto = ATM_SIG_SPANS;
|
|
svp->sv_pif = spp->sp_pif;
|
|
svp->sv_nif = cvp->cvc_attr.nif;
|
|
svp->sv_connvc = cvp;
|
|
svp->sv_spans_aal = aal;
|
|
svp->sv_tstamp = time_second;
|
|
|
|
/*
|
|
* Put VCCB on SPANS queue
|
|
*/
|
|
ENQUEUE(svp, struct spans_vccb, sv_sigelem, spp->sp_vccq);
|
|
|
|
/*
|
|
* Link VCCB to VCC connection block
|
|
*/
|
|
cvp->cvc_vcc = (struct vccb *) svp;
|
|
|
|
/*
|
|
* Start the SPANS message exchange if this is an SVC
|
|
*/
|
|
if (!pvc) {
|
|
svp->sv_retry = 0;
|
|
svp->sv_spans_qos.rsc_peak = 1;
|
|
svp->sv_spans_qos.rsc_mean = 1;
|
|
svp->sv_spans_qos.rsc_burst = 1;
|
|
err = spans_send_open_req(spp, svp);
|
|
if (err) {
|
|
/*
|
|
* On error, delete the VCCB
|
|
*/
|
|
DEQUEUE(svp, struct spans_vccb, sv_sigelem,
|
|
spp->sp_vccq);
|
|
cvp->cvc_vcc = (struct vccb *)0;
|
|
uma_zfree(spans_vc_zone, svp);
|
|
return(err);
|
|
} else {
|
|
/*
|
|
* VCCB is opening--set the retransmit timer
|
|
*/
|
|
SPANS_VC_TIMER((struct vccb *) svp, SV_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Close a SPANS VCC
|
|
*
|
|
* Called when a user wants to close a VCC. This function will clean
|
|
* up the VCCB and, for an SVC, send a close request.
|
|
*
|
|
* Must be called at splnet.
|
|
*
|
|
* Arguments:
|
|
* spp pointer to SPANS protocol instance
|
|
* svp pointer to VCCB for the VCC to be closed
|
|
*
|
|
* Returns:
|
|
* 0 VCC is now closed
|
|
* errno error encountered
|
|
*/
|
|
int
|
|
spans_close_vcc(spp, svp, force)
|
|
struct spans *spp;
|
|
struct spans_vccb *svp;
|
|
int force;
|
|
|
|
{
|
|
int err = 0;
|
|
|
|
ATM_DEBUG2("spans_close_vcc: svp=%p, state=%d\n", svp,
|
|
svp->sv_sstate);
|
|
|
|
/*
|
|
* Check that this is for the same interface SPANS uses
|
|
*/
|
|
if (svp->sv_pif != spp->sp_pif) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/*
|
|
* Kill any possible timer
|
|
*/
|
|
SPANS_VC_CANCEL((struct vccb *) svp);
|
|
|
|
/*
|
|
* Mark the close time.
|
|
*/
|
|
svp->sv_tstamp = time_second;
|
|
|
|
/*
|
|
* Process based on the connection type
|
|
*/
|
|
if (svp->sv_type & VCC_PVC) {
|
|
svp->sv_sstate = SPANS_VC_FREE;
|
|
svp->sv_ustate = VCCU_CLOSED;
|
|
} else if (svp->sv_type & VCC_SVC) {
|
|
/*
|
|
* Update VCCB states
|
|
*/
|
|
svp->sv_ustate = VCCU_CLOSED;
|
|
|
|
/*
|
|
* Send the appropriate SPANS close message
|
|
*/
|
|
switch (svp->sv_sstate) {
|
|
case SPANS_VC_R_POPEN:
|
|
err = spans_send_open_rsp(spp, svp, SPANS_FAIL);
|
|
svp->sv_sstate = SPANS_VC_FREE;
|
|
break;
|
|
case SPANS_VC_OPEN:
|
|
case SPANS_VC_POPEN:
|
|
case SPANS_VC_ABORT:
|
|
svp->sv_retry = 0;
|
|
err = spans_send_close_req(spp, svp);
|
|
if (force) {
|
|
svp->sv_sstate = SPANS_VC_FREE;
|
|
} else {
|
|
svp->sv_sstate = SPANS_VC_CLOSE;
|
|
SPANS_VC_TIMER((struct vccb *) svp,
|
|
SV_TIMEOUT);
|
|
}
|
|
break;
|
|
case SPANS_VC_CLOSE:
|
|
if (force) {
|
|
svp->sv_sstate = SPANS_VC_FREE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Wait for user to free resources
|
|
*/
|
|
return(err);
|
|
}
|
|
|
|
|
|
/*
|
|
* Clear a SPANS VCC
|
|
*
|
|
* Called when the signalling manager wants to close a VCC immediately.
|
|
* This function will clean up the VCCB and notify the owner.
|
|
*
|
|
* Must be called at splnet.
|
|
*
|
|
* Arguments:
|
|
* spp pointer to SPANS protocol instance
|
|
* svp pointer to VCCB for the VCC to be closed
|
|
*
|
|
* Returns:
|
|
* 0 VCC is now closed
|
|
* errno error encountered
|
|
*/
|
|
int
|
|
spans_clear_vcc(spp, svp)
|
|
struct spans *spp;
|
|
struct spans_vccb *svp;
|
|
|
|
{
|
|
u_char outstate;
|
|
|
|
ATM_DEBUG2("spans_clear_vcc: svp=%p, state=%d\n", svp,
|
|
svp->sv_sstate);
|
|
|
|
/*
|
|
* Check that this is for the same interface SPANS uses
|
|
*/
|
|
if (svp->sv_pif != spp->sp_pif) {
|
|
return (EINVAL);
|
|
}
|
|
|
|
/*
|
|
* Kill any possible timer
|
|
*/
|
|
SPANS_VC_CANCEL((struct vccb *) svp);
|
|
|
|
/*
|
|
* Mark the close time
|
|
*/
|
|
svp->sv_tstamp = time_second;
|
|
|
|
/*
|
|
* Mark the VCCB closed
|
|
*/
|
|
outstate = svp->sv_sstate;
|
|
svp->sv_sstate = SPANS_VC_FREE;
|
|
svp->sv_ustate = VCCU_CLOSED;
|
|
|
|
/*
|
|
* Notify the user if old state indicates.
|
|
*/
|
|
switch (outstate) {
|
|
case SPANS_VC_ACTIVE:
|
|
case SPANS_VC_ACT_DOWN:
|
|
case SPANS_VC_POPEN:
|
|
case SPANS_VC_OPEN:
|
|
case SPANS_VC_CLOSE:
|
|
case SPANS_VC_ABORT:
|
|
/* XXX -- set cause */
|
|
atm_cm_cleared(svp->sv_connvc);
|
|
break;
|
|
case SPANS_VC_NULL:
|
|
case SPANS_VC_R_POPEN:
|
|
case SPANS_VC_FREE:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Wait for user to free resources
|
|
*/
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset the switch state
|
|
*
|
|
* Called when the switch or host at the far end of the ATM link has
|
|
* gone away. This can be deteched either by a number of SPANS_STAT_REQ
|
|
* messages going unanswered or by the host epoch changing in a SPANS
|
|
* SPANS_STAT_IND or SPANS_STAT_REQ message.
|
|
*
|
|
* Arguments:
|
|
* spp pointer to SPANS protocol instance
|
|
*
|
|
* Returns:
|
|
* none
|
|
*
|
|
*/
|
|
void
|
|
spans_switch_reset(spp, cause)
|
|
struct spans *spp;
|
|
int cause;
|
|
|
|
{
|
|
int s;
|
|
struct vccb *vcp, *vnext;
|
|
|
|
ATM_DEBUG2("spans_switch_reset: spp=%p, cause=%d\n",
|
|
spp, cause);
|
|
|
|
/*
|
|
* Log the event
|
|
*/
|
|
log(LOG_INFO, "spans: signalling %s on interface %s%d\n",
|
|
(cause == SPANS_UNI_DOWN ? "down" : "up"),
|
|
spp->sp_pif->pif_name,
|
|
spp->sp_pif->pif_unit);
|
|
|
|
/*
|
|
* Terminate all of our VCCs
|
|
*/
|
|
s = splnet();
|
|
for (vcp = Q_HEAD(spp->sp_vccq, struct vccb); vcp;
|
|
vcp = vnext) {
|
|
|
|
u_char outstate;
|
|
|
|
vnext = Q_NEXT(vcp, struct vccb, vc_sigelem);
|
|
|
|
if (vcp->vc_type & VCC_SVC) {
|
|
/*
|
|
* Close the SVC and notify the owner
|
|
*/
|
|
outstate = vcp->vc_sstate;
|
|
SPANS_VC_CANCEL((struct vccb *) vcp);
|
|
vcp->vc_ustate = VCCU_CLOSED;
|
|
vcp->vc_sstate = SPANS_VC_FREE;
|
|
if (outstate == SPANS_VC_OPEN ||
|
|
outstate == SPANS_VC_POPEN) {
|
|
/* XXX -- set cause */
|
|
atm_cm_cleared(vcp->vc_connvc);
|
|
}
|
|
} else if (vcp->vc_type & VCC_PVC) {
|
|
/*
|
|
* Note new state
|
|
*/
|
|
switch(cause) {
|
|
case SPANS_UNI_DOWN:
|
|
vcp->vc_sstate = SPANS_VC_ACT_DOWN;
|
|
break;
|
|
case SPANS_UNI_UP:
|
|
vcp->vc_sstate = SPANS_VC_ACTIVE;
|
|
break;
|
|
}
|
|
} else {
|
|
log(LOG_ERR, "spans: invalid VCC type: vccb=%p, type=%d\n",
|
|
vcp, vcp->vc_type);
|
|
}
|
|
}
|
|
(void) splx(s);
|
|
}
|