/* * * =================================== * 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-related subroutines. * */ #include #include "spans_xdr.h" #include #ifndef lint __RCSID("@(#) $FreeBSD$"); #endif /* * 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 = (struct spans_vccb *)atm_allocate(&spans_vcpool); 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; atm_free((caddr_t)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); }