/* * Send a RELEASE COMPLETE message * * Arguments: * usp pointer to UNISIG protocol instance block * uvp pointer to VCCB for which the RELEASE is being sent. * NULL indicates that a VCCB wasn't found for a call * reference value. * msg pointer to the message which triggered the send * cause the cause code for the message; a value of * T_ATM_ABSENT indicates that the cause code is * in the VCC's ATM attributes block * * Returns: * 0 success * errno error encountered * */ int unisig_send_release_complete(struct unisig *usp, struct unisig_vccb *uvp, struct unisig_msg *msg, int cause) { int err = 0; struct unisig_msg *rls_cmp; struct ie_generic *cause_ie; ATM_DEBUG4("unisig_send_release_complete usp=%p, uvp=%p, msg=%p, cause=%d\n", usp, uvp, msg, cause); /* * Get memory for a RELEASE COMPLETE message */ rls_cmp = (struct unisig_msg *) atm_allocate(&unisig_msgpool); if (rls_cmp == NULL) { return(ENOMEM); } cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); if (cause_ie == NULL) { atm_free(rls_cmp); return(ENOMEM); } /* * Fill in the RELEASE COMPLETE message */ if (uvp) { rls_cmp->msg_call_ref = uvp->uv_call_ref; } else if (msg) { rls_cmp->msg_call_ref = EXTRACT_CREF(msg->msg_call_ref); } else { rls_cmp->msg_call_ref = UNI_MSG_CALL_REF_GLOBAL; } rls_cmp->msg_type = UNI_MSG_RLSC; rls_cmp->msg_type_flag = 0; rls_cmp->msg_type_action = 0; rls_cmp->msg_ie_caus = cause_ie; /* * Fill out the cause IE */ cause_ie->ie_ident = UNI_IE_CAUS; if (cause == T_ATM_ABSENT) { unisig_cause_from_attr(cause_ie, &uvp->uv_connvc->cvc_attr); } else { cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER; unisig_cause_from_msg(cause_ie, msg, cause); } /* * Send the RELEASE COMPLETE */ err = unisig_send_msg(usp, rls_cmp); unisig_free_msg(rls_cmp); return(err); }
/* * Send a RELEASE message * * Arguments: * usp pointer to UNISIG protocol instance block * uvp pointer to VCCB for which the RELEASE is being sent * msg pointer to UNI signalling message that the RELEASE * responds to (may be NULL) * cause the reason for the RELEASE; a value of * T_ATM_ABSENT indicates that the cause code is * in the VCC's ATM attributes block * * Returns: * none * */ int unisig_send_release(struct unisig *usp, struct unisig_vccb *uvp, struct unisig_msg *msg, int cause) { int err = 0; struct unisig_msg *rls_msg; struct ie_generic *cause_ie; ATM_DEBUG2("unisig_send_release: usp=%p, uvp=%p\n", usp, uvp); /* * Get memory for a RELEASE message */ rls_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); if (rls_msg == NULL) { return(ENOMEM); } cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); if (cause_ie == NULL) { atm_free(rls_msg); return(ENOMEM); } /* * Fill in the RELEASE message */ rls_msg->msg_call_ref = uvp->uv_call_ref; rls_msg->msg_type = UNI_MSG_RLSE; rls_msg->msg_type_flag = 0; rls_msg->msg_type_action = 0; rls_msg->msg_ie_caus = cause_ie; /* * Fill out the cause IE */ cause_ie->ie_ident = UNI_IE_CAUS; if (cause == T_ATM_ABSENT) { unisig_cause_from_attr(cause_ie, &uvp->uv_connvc->cvc_attr); } else { cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER; unisig_cause_from_msg(cause_ie, msg, cause); } /* * Send the RELEASE */ err = unisig_send_msg(usp, rls_msg); unisig_free_msg(rls_msg); return(err); }
/* * Queue a Stack Call * * Queues a stack call which must be deferred to the global stack queue. * The call parameters are stored in entries which are allocated from the * stack queue storage pool. * * Arguments: * cmd stack command * func destination function * token destination layer's token * cvp pointer to connection vcc * arg1 command argument * arg2 command argument * * Returns: * 0 call queued * errno call not queued - reason indicated * */ int atm_stack_enq(int cmd, void (*func)(int, void *, int, int), void *token, Atm_connvc *cvp, int arg1, int arg2) { struct stackq_entry *sqp; crit_enter(); /* * Get a new queue entry for this call */ sqp = (struct stackq_entry *)atm_allocate(&atm_stackq_pool); if (sqp == NULL) { crit_exit(); return (ENOMEM); } /* * Fill in new entry */ sqp->sq_next = NULL; sqp->sq_cmd = cmd; sqp->sq_func = func; sqp->sq_token = token; sqp->sq_arg1 = arg1; sqp->sq_arg2 = arg2; sqp->sq_connvc = cvp; /* * Put new entry at end of queue */ if (atm_stackq_head == NULL) atm_stackq_head = sqp; else atm_stackq_tail->sq_next = sqp; atm_stackq_tail = sqp; crit_exit(); return (0); }
/* * 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 from a critical section. * * 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(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 = NULL; atm_free((caddr_t)svp); return(err); } else {
/* * Allocate the memory for an atm_range structure. * * Allocates the memory for the enclosing structure and for the components. * * Return: Pointer to the new instance. */ atm_range *atm_range_allocate() { atm_range *range = calloc(1, sizeof(atm_range)); range->begin = atm_allocate(); range->end = atm_allocate(); return range; }
/* * Process a SPANS timeout * * Called when a previously scheduled spans control block timer expires. * Processing will based on the current SPANS state. * * Called at splnet. * * Arguments: * tip pointer to spans timer control block * * Returns: * none * */ void spans_timer(struct atm_time *tip) { struct spans *spp; spans_msg *msg; Atm_addr_pvc *pvcp; int err; /* * Back-off to SPANS control block */ spp = (struct spans *) ((caddr_t)tip - (int)(&((struct spans *)0)->sp_time)); ATM_DEBUG2("spans_timer: spp=%p,state=%d\n", spp, spp->sp_state); /* * Process timeout based on protocol state */ switch (spp->sp_state) { case SPANS_INIT: /* * Open signalling channel */ spans_attr.nif = spp->sp_pif->pif_nif; spans_attr.aal.v.aal4.forward_max_SDU_size = ATM_NIF_MTU; spans_attr.aal.v.aal4.backward_max_SDU_size = ATM_NIF_MTU; spans_attr.aal.v.aal4.SSCS_type = T_ATM_SSCS_SSCOP_UNREL; spans_attr.aal.v.aal4.mid_low = 0; spans_attr.aal.v.aal4.mid_high = 0; spans_attr.called.tag = T_ATM_PRESENT; spans_attr.called.addr.address_format = T_ATM_PVC_ADDR; spans_attr.called.addr.address_length = sizeof(Atm_addr_pvc); pvcp = (Atm_addr_pvc *)spans_attr.called.addr.address; ATM_PVC_SET_VPI(pvcp, SPANS_SIG_VPI); ATM_PVC_SET_VCI(pvcp, SPANS_SIG_VCI); spans_attr.called.subaddr.address_format = T_ATM_ABSENT; spans_attr.called.subaddr.address_length = 0; spans_attr.traffic.v.forward.PCR_all_traffic = spp->sp_pif->pif_pcr; spans_attr.traffic.v.backward.PCR_all_traffic = spp->sp_pif->pif_pcr; err = atm_cm_connect(&spans_endpt, spp, &spans_attr, &spp->sp_conn); if (err) { log(LOG_CRIT, "spans: signalling channel setup failed\n"); return; } /* * Signalling channel open, start probing */ spp->sp_state = SPANS_PROBE; /* FALLTHRU */ case SPANS_PROBE: case SPANS_ACTIVE: /* * Send out SPANS_STAT_REQ message */ msg = (spans_msg *)atm_allocate(&spans_msgpool); if (msg == NULL) { /* Retry later if no memory */ SPANS_TIMER(spp, SPANS_PROBE_ERR_WAIT); break; } msg->sm_vers = SPANS_VERS_1_0; msg->sm_type = SPANS_STAT_REQ; msg->sm_stat_req.streq_es_epoch = spp->sp_h_epoch; if (spans_send_msg(spp, msg)) { /* Retry later if send fails */ SPANS_TIMER(spp, SPANS_PROBE_ERR_WAIT); atm_free(msg); break; } atm_free(msg); spp->sp_probe_ct++; /* * Check whether we're getting an answer to our probes */ if (spp->sp_state == SPANS_ACTIVE && spp->sp_probe_ct > SPANS_PROBE_THRESH) { /* * Interface is down, notify VCC owners */ spans_switch_reset(spp, SPANS_UNI_DOWN); /* * Set new state and increment host epoch so * switch knows we reset everyting. */ spp->sp_state = SPANS_PROBE; spp->sp_h_epoch++; spp->sp_s_epoch = 0; } /* * Keep sending status requests */ SPANS_TIMER(spp, SPANS_PROBE_INTERVAL); break; case SPANS_DETACH: /* * Try to terminate the SPANS signalling PVC */ err = atm_cm_release(spp->sp_conn, &spans_cause); if (err) { log(LOG_ERR, "spans: can't close signalling channel\n"); } break; default: log(LOG_ERR, "spans: timer state: spp=%p, state=%d\n", spp, spp->sp_state); } }
/* * Process a new outgoing SVC requiring SPANS ARP support * * This function is called by an endpoint wishing to resolve a destination * IP address to an ATM address in order to open an SVC to that destination. * If a valid mapping is already in our cache, then we just tell the caller * about it and that's that. Otherwise, we have to allocate a new arp entry * and issue a query for the mapping. * * Arguments: * ivp pointer to SVC's IPVCC control block * dst pointer to destination IP address * * Returns: * MAP_VALID - Got the answer, returned via iv_arpent field. * MAP_PROCEEDING - OK so far, querying for peer's mapping * MAP_FAILED - error, unable to allocate resources * */ int spansarp_svcout(struct ipvcc *ivp, struct in_addr *dst) { struct spanscls *clp; struct spansarp *sap; ivp->iv_arpent = NULL; /* * Lookup destination address */ crit_enter(); SPANSARP_LOOKUP(dst->s_addr, sap); if (sap) { /* * Link this vcc to entry queue */ LINK2TAIL(ivp, struct ipvcc, sap->sa_ivp, iv_arpnext); /* * If entry is valid, we're done */ if (sap->sa_flags & SAF_VALID) { ivp->iv_arpent = (struct arpmap *)sap; crit_exit(); return (MAP_VALID); } /* * We're already looking for this address */ crit_exit(); return (MAP_PROCEEDING); } /* * Need a new arp entry - first, find the cls instance * corresponding to the requestor's IP interface. */ for (clp = spanscls_head; clp; clp = clp->cls_next) { if (clp->cls_ipnif == ivp->iv_ipnif) break; } if (clp == NULL) { crit_exit(); return (MAP_FAILED); } /* * Now get the new arp entry */ sap = (struct spansarp *)atm_allocate(&spansarp_pool); if (sap == NULL) { crit_exit(); return (MAP_FAILED); } /* * Get entry set up */ sap->sa_dstip.s_addr = dst->s_addr; sap->sa_dstatm.address_format = T_ATM_ABSENT; sap->sa_dstatm.address_length = 0; sap->sa_dstatmsub.address_format = T_ATM_ABSENT; sap->sa_dstatmsub.address_length = 0; sap->sa_cls = clp; sap->sa_origin = SAO_LOOKUP; /* * Link ipvcc to arp entry for later notification */ LINK2TAIL(ivp, struct ipvcc, sap->sa_ivp, iv_arpnext); /* * Add arp entry to table */ SPANSARP_ADD(sap); /* * Add arp entry to retry list and start retry timer if needed */ LINK2TAIL(sap, struct spansarp, spansarp_retry_head, sa_rnext); if ((spansarp_rtimer.ti_flag & TIF_QUEUED) == 0) atm_timeout(&spansarp_rtimer, SPANSARP_RETRY, spansarp_retry); /* * Issue arp request for this address */ spansarp_request(sap); crit_exit(); return (MAP_PROCEEDING); }
/* * SPANS ARP IOCTL support * * Function will be called from a critical section. * * Arguments: * code PF_ATM sub-operation code * data pointer to code specific parameter data area * arg1 pointer to code specific argument * * Returns: * 0 request procesed * errno error processing request - reason indicated * */ int spansarp_ioctl(int code, caddr_t data, caddr_t arg1) { struct atmaddreq *aap; struct atmdelreq *adp; struct atminfreq *aip; struct spans *spp; struct spanscls *clp; struct spansarp *sap; struct air_arp_rsp aar; struct ip_nif *inp; struct ipvcc *ivp, *inext; struct in_addr ip; u_long dst; int err = 0, i, buf_len; caddr_t buf_addr; switch (code) { case AIOCS_ADD_ARP: /* * Add a permanent ARP mapping */ aap = (struct atmaddreq *)data; clp = (struct spanscls *)arg1; inp = clp->cls_ipnif; if ((aap->aar_arp_addr.address_format != T_ATM_SPANS_ADDR) || (aap->aar_arp_origin != ARP_ORIG_PERM)) { err = EINVAL; break; } ip = SATOSIN(&aap->aar_arp_dst)->sin_addr; /* * See if we already have an entry for this IP address */ SPANSARP_LOOKUP(ip.s_addr, sap); if (sap == NULL) { /* * No, get a new arp entry */ sap = (struct spansarp *)atm_allocate(&spansarp_pool); if (sap == NULL) { err = ENOMEM; break; } /* * Get entry set up */ sap->sa_dstip = ip; ATM_ADDR_COPY(&aap->aar_arp_addr, &sap->sa_dstatm); sap->sa_dstatmsub.address_format = T_ATM_ABSENT; sap->sa_dstatmsub.address_length = 0; sap->sa_cls = clp; sap->sa_flags |= SAF_VALID; sap->sa_origin = SAO_PERM; /* * Add entry to table */ SPANSARP_ADD(sap); break; } /* * See if we're attempting to change the ATM address for * this cached entry */ if ((sap->sa_dstatm.address_format != T_ATM_ABSENT) && (!ATM_ADDR_EQUAL(&aap->aar_arp_addr, &sap->sa_dstatm) || (clp != sap->sa_cls))) { /* * Yes, notify IP/ATM that a mapping change has * occurred. IP/ATM will close any VCC's which * aren't waiting for this map. */ sap->sa_flags |= SAF_LOCKED; for (ivp = sap->sa_ivp; ivp; ivp = inext) { inext = ivp->iv_arpnext; (*inp->inf_arpnotify)(ivp, MAP_CHANGED); } sap->sa_flags &= ~SAF_LOCKED; } /* * Update the cached entry with the new data */ ATM_ADDR_COPY(&aap->aar_arp_addr, &sap->sa_dstatm); sap->sa_cls = clp; /* * If this entry isn't valid, notify anyone who might * be interested */ if ((sap->sa_flags & SAF_VALID) == 0) { sap->sa_flags |= SAF_LOCKED; for (ivp = sap->sa_ivp; ivp; ivp = inext) { inext = ivp->iv_arpnext; (*inp->inf_arpnotify)(ivp, MAP_VALID); } sap->sa_flags &= ~SAF_LOCKED; } /* * Remove this entry from the retry chain */ UNLINK(sap, struct spansarp, spansarp_retry_head, sa_rnext); /* * Mark the entry as permanent */ sap->sa_flags |= SAF_VALID; sap->sa_origin = SAO_PERM; break; case AIOCS_DEL_ARP: /* * Delete an ARP mapping */ adp = (struct atmdelreq *)data; clp = (struct spanscls *)arg1; ip = SATOSIN(&adp->adr_arp_dst)->sin_addr; /* * Now find the entry to be deleted */ SPANSARP_LOOKUP(ip.s_addr, sap); if (sap == NULL) { err = ENOENT; break; } /* * Notify all VCCs using this entry that they must finish * up now. */ sap->sa_flags |= SAF_LOCKED; for (ivp = sap->sa_ivp; ivp; ivp = inext) { inext = ivp->iv_arpnext; (*ivp->iv_ipnif->inf_arpnotify)(ivp, MAP_FAILED); } /* * Now free up the entry */ UNLINK(sap, struct spansarp, spansarp_retry_head, sa_rnext); SPANSARP_DELETE(sap); atm_free((caddr_t)sap); break; case AIOCS_INF_ARP: /* * Get ARP table information */ aip = (struct atminfreq *)data; spp = (struct spans *)arg1; if (aip->air_arp_addr.sa_family != AF_INET) break; dst = SATOSIN(&aip->air_arp_addr)->sin_addr.s_addr; buf_addr = aip->air_buf_addr; buf_len = aip->air_buf_len; if ((clp = spp->sp_cls) == NULL) break; /* * Run through entire arp table */ for (i = 0; i < SPANSARP_HASHSIZ; i++) { for (sap = spansarp_arptab[i]; sap; sap = sap->sa_next) { /* * We only want entries learned * from the supplied interface. */ if (sap->sa_cls != clp) continue; if ((dst != INADDR_ANY) && (dst != sap->sa_dstip.s_addr)) continue; /* * Make sure there's room in the user's buffer */ if (buf_len < sizeof(aar)) { err = ENOSPC; break; } /* * Fill in info to be returned */ SATOSIN(&aar.aap_arp_addr)->sin_family = AF_INET; SATOSIN(&aar.aap_arp_addr)->sin_addr.s_addr = sap->sa_dstip.s_addr; strlcpy(aar.aap_intf, clp->cls_ipnif->inf_nif->nif_if.if_xname, sizeof(aar.aap_intf)); aar.aap_flags = sap->sa_flags; aar.aap_origin = sap->sa_origin; if (sap->sa_flags & SAF_VALID) aar.aap_age = SPANSARP_MAXAGE - sap->sa_reftime; else aar.aap_age = 0; ATM_ADDR_COPY(&sap->sa_dstatm, &aar.aap_addr); ATM_ADDR_COPY(&sap->sa_dstatmsub, &aar.aap_subaddr); /* * Copy the response into the user's buffer */ if ((err = copyout((caddr_t)&aar, buf_addr, sizeof(aar))) != 0) break; buf_addr += sizeof(aar); buf_len -= sizeof(aar); } if (err) break; } /* * Update the buffer pointer and length */ aip->air_buf_addr = buf_addr; aip->air_buf_len = buf_len; break; case AIOCS_INF_ASV: /* * Get ARP server information */ /* SPANS doesn't have an ARP server */ break; default: err = EOPNOTSUPP; } return (err); }
/* * Process a SPANS ARP input packet * * Arguments: * clp pointer to interface CLS control block * m pointer to input packet buffer chain * * Returns: * none * */ void spansarp_input(struct spanscls *clp, KBuffer *m) { struct spans *spp = clp->cls_spans; struct spanscls_hdr *chp; struct spansarp_hdr *ahp; struct spansarp *sap; struct ip_nif *inp = clp->cls_ipnif; struct in_addr in_me, in_src, in_targ; int err; /* * Make sure IP interface has been activated */ if (inp == NULL) goto free; /* * Get the packet together */ if (KB_LEN(m) < ARP_PACKET_LEN) { KB_PULLUP(m, ARP_PACKET_LEN, m); if (m == NULL) return; } KB_DATASTART(m, chp, struct spanscls_hdr *); ahp = (struct spansarp_hdr *)(chp + 1); KM_COPY(ahp->ah_spa, &in_src, sizeof(struct in_addr)); KM_COPY(ahp->ah_tpa, &in_targ, sizeof(struct in_addr)); KM_COPY(&(IA_SIN(inp->inf_addr)->sin_addr), &in_me, sizeof(struct in_addr)); /* * Initial packet verification */ if ((ahp->ah_hrd != htons(ARP_SPANS)) || (ahp->ah_pro != htons(ETHERTYPE_IP))) goto free; /* * Validate source addresses * can't be from hardware broadcast * can't be from me */ if (!spans_addr_cmp(&ahp->ah_sha, &spans_bcastaddr)) goto free; if (!spans_addr_cmp(&ahp->ah_sha, spp->sp_addr.address)) goto free; if (in_src.s_addr == in_me.s_addr) { log(LOG_ERR, "duplicate IP address sent from spans address %s\n", spans_addr_print(&ahp->ah_sha)); in_targ = in_me; goto chkop; } /* * If source IP address is from unspecified or broadcast addresses, * don't bother updating arp table, but answer possible requests */ if (in_broadcast(in_src, &inp->inf_nif->nif_if)) goto chkop; /* * Update arp table with source address info */ crit_enter(); SPANSARP_LOOKUP(in_src.s_addr, sap); if (sap) { /* * Found an entry for the source, but don't * update permanent entries */ if (sap->sa_origin != SAO_PERM) { /* * Update the entry */ sap->sa_dstatm.address_format = T_ATM_SPANS_ADDR; sap->sa_dstatm.address_length = sizeof(spans_addr); spans_addr_copy(&ahp->ah_sha, sap->sa_dstatm.address); sap->sa_cls = clp; sap->sa_reftime = 0; if ((sap->sa_flags & SAF_VALID) == 0) { /* * Newly valid entry, notify waiting users */ struct ipvcc *ivp, *inext; sap->sa_flags |= SAF_VALID; for (ivp = sap->sa_ivp; ivp; ivp = inext) { inext = ivp->iv_arpnext; ivp->iv_arpent = (struct arpmap *)sap; (*inp->inf_arpnotify)(ivp, MAP_VALID); } /* * Remove ourselves from the retry chain */ UNLINK(sap, struct spansarp, spansarp_retry_head, sa_rnext); } } } else if (in_targ.s_addr == in_me.s_addr) { /* * Source unknown and we're the target - add new entry */ sap = (struct spansarp *)atm_allocate(&spansarp_pool); if (sap) { sap->sa_dstip.s_addr = in_src.s_addr; sap->sa_dstatm.address_format = T_ATM_SPANS_ADDR; sap->sa_dstatm.address_length = sizeof(spans_addr); spans_addr_copy(&ahp->ah_sha, sap->sa_dstatm.address); sap->sa_dstatmsub.address_format = T_ATM_ABSENT; sap->sa_dstatmsub.address_length = 0; sap->sa_cls = clp; sap->sa_flags = SAF_VALID; sap->sa_origin = SAO_LOOKUP; SPANSARP_ADD(sap); } } crit_exit(); chkop: /* * If this is a request for our address, send a reply */ if (ntohs(ahp->ah_op) != ARP_REQUEST) goto free; if (in_targ.s_addr != in_me.s_addr) goto free; spans_addr_copy(&chp->ch_src, &chp->ch_dst); spans_addr_copy(spp->sp_addr.address, &chp->ch_src); ahp->ah_op = htons(ARP_REPLY); spans_addr_copy(&ahp->ah_sha, &ahp->ah_tha); spans_addr_copy(spp->sp_addr.address, &ahp->ah_sha); KM_COPY(ahp->ah_spa, ahp->ah_tpa, sizeof(struct in_addr)); KM_COPY(&in_me, ahp->ah_spa, sizeof(struct in_addr)); err = atm_cm_cpcs_data(clp->cls_conn, m); if (err) goto free; return; free: KB_FREEALL(m); }
/* * Process a SETUP message * * Arguments: * usp pointer to UNISIG protocol instance block * msg pointer to the SETUP message * * Returns: * none * */ static void unisig_rcv_setup(struct unisig *usp, struct unisig_msg *msg) { struct unisig_vccb *uvp = NULL; struct ie_generic *iep; ATM_DEBUG2("unisig_rcv_setup: usp=%p, msg=%p\n", usp, msg); /* * If we already have a VCC with the call reference, * ignore the SETUP message */ uvp = unisig_find_conn(usp, EXTRACT_CREF(msg->msg_call_ref)); if (uvp) return; /* * If the call reference flag is incorrectly set, * ignore the SETUP message */ if (msg->msg_call_ref & UNI_MSG_CALL_REF_RMT) return; /* * If there are missing mandatory IEs, send a * RELEASE COMPLETE message and ignore the SETUP */ for (iep = msg->msg_ie_err; iep; iep = iep->ie_next) { if (iep->ie_err_cause == UNI_IE_CAUS_MISSING) { unisig_send_release_complete(usp, uvp, msg, UNI_IE_CAUS_MISSING); return; } } /* * If there are mandatory IEs with invalid content, send a * RELEASE COMPLETE message and ignore the SETUP */ for (iep = msg->msg_ie_err; iep; iep = iep->ie_next) { if (iep->ie_err_cause == UNI_IE_CAUS_IECONTENT) { unisig_send_release_complete(usp, uvp, msg, UNI_IE_CAUS_IECONTENT); return; } } /* * Get a new VCCB for the connection */ uvp = (struct unisig_vccb *)atm_allocate(&unisig_vcpool); if (uvp == NULL) { return; } /* * Put the VCCB on the UNISIG queue */ ENQUEUE(uvp, struct unisig_vccb, uv_sigelem, usp->us_vccq); /* * Set the state and call reference value */ uvp->uv_sstate = UNI_NULL; uvp->uv_call_ref = EXTRACT_CREF(msg->msg_call_ref); /* * Pass the VCCB and message to the VC state machine */ unisig_vc_state(usp, uvp, UNI_VC_SETUP_MSG, msg); /* * If the VCCB state is NULL, the open failed and the * VCCB should be released */ if (uvp->uv_sstate == UNI_NULL) { DEQUEUE(uvp, struct unisig_vccb, uv_sigelem, usp->us_vccq); atm_free(uvp); }
/* * Process a RESTART message * * Arguments: * usp pointer to UNISIG protocol instance block * msg pointer to the RESTART message * * Returns: * none * */ static void unisig_rcv_restart(struct unisig *usp, struct unisig_msg *msg) { struct unisig_vccb *uvp, *uvnext; struct unisig_msg *rsta_msg; ATM_DEBUG2("unisig_rcv_restart: usp=%p, msg=%p\n", usp, msg); /* * Check what class of VCCs we're supposed to restart */ if (msg->msg_ie_rsti->ie_rsti_class == UNI_IE_RSTI_IND_VC) { /* * Just restart the indicated VCC */ if (msg->msg_ie_cnid) { uvp = unisig_find_vpvc(usp, msg->msg_ie_cnid->ie_cnid_vpci, msg->msg_ie_cnid->ie_cnid_vci, 0); if (uvp && uvp->uv_type & VCC_SVC) { unisig_clear_vcc(usp, uvp, T_ATM_CAUSE_NORMAL_CALL_CLEARING); } } } else { /* * Restart all VCCs */ crit_enter(); for (uvp=Q_HEAD(usp->us_vccq, struct unisig_vccb); uvp; uvp=uvnext) { uvnext = Q_NEXT(uvp, struct unisig_vccb, uv_sigelem); if (uvp->uv_type & VCC_SVC) { unisig_clear_vcc(usp, uvp, T_ATM_CAUSE_NORMAL_CALL_CLEARING); } } crit_exit(); } /* * Get memory for a RESTART ACKNOWLEDGE message */ rsta_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); if (rsta_msg == NULL) { return; } /* * Fill out the message */ rsta_msg->msg_call_ref = EXTRACT_CREF(msg->msg_call_ref); rsta_msg->msg_type = UNI_MSG_RSTA; rsta_msg->msg_type_flag = 0; rsta_msg->msg_type_action = 0; rsta_msg->msg_ie_rsti = msg->msg_ie_rsti; if (msg->msg_ie_cnid) { rsta_msg->msg_ie_cnid = msg->msg_ie_cnid; } /* * Send the message */ unisig_send_msg(usp, rsta_msg); rsta_msg->msg_ie_rsti = NULL; rsta_msg->msg_ie_cnid = NULL; unisig_free_msg(rsta_msg); return; }
/* * Send a STATUS message * * Arguments: * usp pointer to UNISIG protocol instance block * uvp pointer to VCCB for which the STATUS is being sent. * NULL indicates that a VCCB wasn't found for a call * reference value. * msg pointer to the message which triggered the send * cause the cause code to include in the message * * Returns: * none * */ int unisig_send_status(struct unisig *usp, struct unisig_vccb *uvp, struct unisig_msg *msg, int cause) { int err = 0, i; struct unisig_msg *stat_msg; struct ie_generic *cause_ie, *clst_ie, *iep; ATM_DEBUG4("unisig_send_status: usp=%p, uvp=%p, msg=%p, cause=%d\n", usp, uvp, msg, cause); /* * Get memory for a STATUS message */ stat_msg = (struct unisig_msg *) atm_allocate(&unisig_msgpool); if (stat_msg == NULL) { return(ENOMEM); } cause_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); if (cause_ie == NULL) { atm_free(stat_msg); return(ENOMEM); } clst_ie = (struct ie_generic *) atm_allocate(&unisig_iepool); if (clst_ie == NULL) { atm_free(stat_msg); atm_free(cause_ie); return(ENOMEM); } /* * Fill in the STATUS message */ if (uvp) { stat_msg->msg_call_ref = uvp->uv_call_ref; } else if (msg) { stat_msg->msg_call_ref = EXTRACT_CREF(msg->msg_call_ref); } else { stat_msg->msg_call_ref = UNI_MSG_CALL_REF_GLOBAL; } stat_msg->msg_type = UNI_MSG_STAT; stat_msg->msg_type_flag = 0; stat_msg->msg_type_action = 0; stat_msg->msg_ie_clst = clst_ie; stat_msg->msg_ie_caus = cause_ie; /* * Fill out the call state IE */ clst_ie->ie_ident = UNI_IE_CLST; clst_ie->ie_coding = 0; clst_ie->ie_flag = 0; clst_ie->ie_action = 0; if (uvp) { clst_ie->ie_clst_state = uvp->uv_sstate; } else { clst_ie->ie_clst_state = UNI_NULL; } /* * Fill out the cause IE */ cause_ie->ie_ident = UNI_IE_CAUS; cause_ie->ie_coding = 0; cause_ie->ie_flag = 0; cause_ie->ie_action = 0; cause_ie->ie_caus_loc = UNI_IE_CAUS_LOC_USER; cause_ie->ie_caus_cause = cause; switch (cause) { case UNI_IE_CAUS_MTEXIST: case UNI_IE_CAUS_STATE: if (msg) { cause_ie->ie_caus_diagnostic[0] = msg->msg_type; } break; case UNI_IE_CAUS_MISSING: case UNI_IE_CAUS_IECONTENT: case UNI_IE_CAUS_IEEXIST: for (i=0, iep=msg->msg_ie_err; iep && i<UNI_MSG_IE_CNT; i++, iep = iep->ie_next) { if (iep->ie_err_cause == cause) { cause_ie->ie_caus_diagnostic[i] = iep->ie_ident; } } } /* * Send the STATUS message */ err = unisig_send_msg(usp, stat_msg); unisig_free_msg(stat_msg); return(err); }
/* * Send a SETUP request * * Build and send a Q.2931 SETUP message. * * Arguments: * usp pointer to UNISIG protocol instance block * uvp pointer to VCCB for which the request is being sent * * Returns: * none * */ int unisig_send_setup(struct unisig *usp, struct unisig_vccb *uvp) { int err = 0; struct unisig_msg *setup; Atm_attributes *ap = &uvp->uv_connvc->cvc_attr; ATM_DEBUG1("unisig_send_setup: uvp=%p\n", uvp); /* * Make sure required connection attriutes are set */ if (ap->aal.tag != T_ATM_PRESENT || ap->traffic.tag != T_ATM_PRESENT || ap->bearer.tag != T_ATM_PRESENT || ap->called.tag != T_ATM_PRESENT || ap->qos.tag != T_ATM_PRESENT) { err = EINVAL; setup = NULL; goto done; } /* * Get memory for a SETUP message */ setup = (struct unisig_msg *)atm_allocate(&unisig_msgpool); if (setup == NULL) { err = ENOMEM; goto done; } /* * Fill in the SETUP message */ if (!uvp->uv_call_ref) uvp->uv_call_ref = unisig_alloc_call_ref(usp); setup->msg_call_ref = uvp->uv_call_ref; setup->msg_type = UNI_MSG_SETU; /* * Set IEs from connection attributes */ err = unisig_set_attrs(usp, setup, ap); if (err) goto done; /* * Attach a Calling Party Number IE if the user didn't * specify one in the attribute block */ if (ap->calling.tag != T_ATM_PRESENT) { setup->msg_ie_cgad = (struct ie_generic *) atm_allocate(&unisig_iepool); if (setup->msg_ie_cgad == NULL) { err = ENOMEM; goto done; } setup->msg_ie_cgad->ie_ident = UNI_IE_CGAD; ATM_ADDR_COPY(&usp->us_addr, &setup->msg_ie_cgad->ie_cgad_addr); ATM_ADDR_SEL_COPY(&usp->us_addr, uvp->uv_nif ? uvp->uv_nif->nif_sel : 0, &setup->msg_ie_cgad->ie_cgad_addr); } /* * Send the SETUP message */ err = unisig_send_msg(usp, setup); done: if (setup) unisig_free_msg(setup); return(err); }