/* * * =================================== * 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 CLS - ARP support * */ #include #include #include #include "spans_xdr.h" #include #include #ifndef lint __RCSID("@(#) $FreeBSD$"); #endif /* * Global variables */ struct spansarp *spansarp_arptab[SPANSARP_HASHSIZ] = {NULL}; /* * Local functions */ static int spansarp_request __P((struct spansarp *)); static void spansarp_aging __P((struct atm_time *)); static void spansarp_retry __P((struct atm_time *)); /* * Local variables */ static struct atm_time spansarp_timer = {0, 0}; /* Aging timer */ static struct atm_time spansarp_rtimer = {0, 0}; /* Retry timer */ static struct spansarp *spansarp_retry_head = NULL; /* Retry chain */ static struct sp_info spansarp_pool = { "spans arp pool", /* si_name */ sizeof(struct spansarp), /* si_blksiz */ 10, /* si_blkcnt */ 100 /* si_maxallow */ }; /* * 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(ivp, dst) struct ipvcc *ivp; struct in_addr *dst; { struct spanscls *clp; struct spansarp *sap; int s; ivp->iv_arpent = NULL; /* * Lookup destination address */ s = splnet(); 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; (void) splx(s); return (MAP_VALID); } /* * We're already looking for this address */ (void) splx(s); 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) { (void) splx(s); return (MAP_FAILED); } /* * Now get the new arp entry */ sap = (struct spansarp *)atm_allocate(&spansarp_pool); if (sap == NULL) { (void) splx(s); 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 */ (void) spansarp_request(sap); (void) splx(s); return (MAP_PROCEEDING); } /* * Process a new incoming SVC requiring SPANS ARP support * * This function is called by an endpoint wishing to resolve a destination * ATM address to its IP address for an incoming call in order to allow a * bi-directional flow of IP packets on the SVC. * * SPANS ARP does not provide reverse mapping facilities and only supports * uni-directional SVCs. Thus, we lie a little to IP and always return a * MAP_PROCEEDING indication, but we will never later notify IP of a * MAP_VALID condition. * * Arguments: * ivp pointer to SVC's IPVCC control block * dst pointer to destination ATM address * dstsub pointer to destination ATM subaddress * * 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_svcin(ivp, dst, dstsub) struct ipvcc *ivp; Atm_addr *dst; Atm_addr *dstsub; { /* * Clear ARP entry field */ ivp->iv_arpent = NULL; return (MAP_PROCEEDING); } /* * SPANS ARP SVC activation notification * * This function is called when a previously opened SVC has successfully * been connected. * * Arguments: * ivp pointer to SVC's IPVCC control block * * Returns: * 0 activation processing successful * errno activation failed - reason indicated * */ int spansarp_svcactive(ivp) struct ipvcc *ivp; { struct spansarp *sap; int s = splnet(); /* * Find an entry for the destination address */ SPANSARP_LOOKUP(ivp->iv_dst.s_addr, sap); if (sap) { /* * IP is finished with entry, so remove IP VCC from chain */ UNLINK(ivp, struct ipvcc, sap->sa_ivp, iv_arpnext); ivp->iv_arpent = NULL; /* * This seems like a reasonable reason to refresh the entry */ sap->sa_reftime = 0; } (void) splx(s); return (0); } /* * SPANS ARP supported VCC is closing * * This function is called just prior to a user closing a VCC which * supports SPANS ARP. We'll sever our links to the VCC and then * figure out how much more cleanup we need to do for now. * * Arguments: * ivp pointer to VCC's IPVCC control block * * Returns: * none * */ void spansarp_vcclose(ivp) struct ipvcc *ivp; { struct spansarp *sap; int s = splnet(); /* * Get spansarp entry */ SPANSARP_LOOKUP(ivp->iv_dst.s_addr, sap); if (sap == NULL) { (void) splx(s); return; } /* * Remove IP VCC from chain */ UNLINK(ivp, struct ipvcc, sap->sa_ivp, iv_arpnext); ivp->iv_arpent = NULL; /* * If entry is currently valid or in use, not much else for us to do */ if ((sap->sa_flags & (SAF_VALID | SAF_LOCKED)) || (sap->sa_origin >= SAO_PERM)) { (void) splx(s); return; } /* * If there are still other VCCs waiting, exit */ if (sap->sa_ivp) { (void) splx(s); return; } /* * Noone else waiting, so remove entry from the retry chain */ UNLINK(sap, struct spansarp, spansarp_retry_head, sa_rnext); /* * Free entry */ SPANSARP_DELETE(sap); atm_free((caddr_t)sap); (void) splx(s); } /* * Process module unloading notification * * Called whenever the spans module is about to be unloaded. All signalling * instances will have been previously detached. All spansarp resources * must be freed now. * * Arguments: * none * * Returns: * none * */ void spansarp_stop() { int i; /* * Make sure the arp table is empty */ for (i = 0; i < SPANSARP_HASHSIZ; i++) { if (spansarp_arptab[i] != NULL) panic("spansarp_stop: arp table not empty"); } /* * Cancel timers */ (void) atm_untimeout(&spansarp_timer); (void) atm_untimeout(&spansarp_rtimer); /* * Free our storage pools */ atm_release_pool(&spansarp_pool); } /* * Process IP Network Interface Activation * * Called whenever an IP network interface becomes active. * * Called at splnet. * * Arguments: * clp pointer to CLS interface * * Returns: * none * */ void spansarp_ipact(clp) struct spanscls *clp; { /* * Make sure aging timer is running */ if ((spansarp_timer.ti_flag & TIF_QUEUED) == 0) atm_timeout(&spansarp_timer, SPANSARP_AGING, spansarp_aging); } /* * Process IP Network Interface Deactivation * * Called whenever an IP network interface becomes inactive. * * Called at splnet. * * Arguments: * clp pointer to CLS interface * * Returns: * none * */ void spansarp_ipdact(clp) struct spanscls *clp; { struct spanscls *clp2; struct spansarp *sap, *snext; int i; /* * Delete all interface entries */ for (i = 0; i < SPANSARP_HASHSIZ; i++) { for (sap = spansarp_arptab[i]; sap; sap = snext) { snext = sap->sa_next; /* * Clean up entries for this interface */ if (sap->sa_cls != clp) continue; /* * All VCCs better be gone by now */ if (sap->sa_ivp) panic("spansarp_ipdact: entry not empty"); /* * Remove entry from the retry chain */ UNLINK(sap, struct spansarp, spansarp_retry_head, sa_rnext); /* * Delete entry from arp table */ SPANSARP_DELETE(sap); atm_free((caddr_t)sap); } } /* * Stop aging timer if this is the last active interface */ for (clp2 = spanscls_head; clp2; clp2 = clp2->cls_next) { if ((clp != clp2) && (clp2->cls_ipnif)) break; } if (clp2 == NULL) (void) atm_untimeout(&spansarp_timer); } /* * Issue a SPANS ARP request packet * * Arguments: * sap pointer to arp table entry * * Returns: * 0 packet was successfully sent * else unable to send packet * */ static int spansarp_request(sap) struct spansarp *sap; { struct spanscls *clp; struct spans *spp; struct spanscls_hdr *chp; struct spansarp_hdr *ahp; KBuffer *m; struct ip_nif *inp; int err; clp = sap->sa_cls; spp = clp->cls_spans; inp = clp->cls_ipnif; /* * Make sure CLS VCC is open and that we know our addresses */ if (clp->cls_state != CLS_OPEN) return (1); if (spp->sp_addr.address_format != T_ATM_SPANS_ADDR) return (1); if (inp == NULL) return (1); /* * Get a buffer for pdu */ KB_ALLOCPKT(m, ARP_PACKET_LEN, KB_F_NOWAIT, KB_T_DATA); if (m == NULL) return (1); /* * Place pdu at end of buffer */ KB_PLENSET(m, ARP_PACKET_LEN); KB_TAILALIGN(m, ARP_PACKET_LEN); KB_DATASTART(m, chp, struct spanscls_hdr *); ahp = (struct spansarp_hdr *)(chp + 1); /* * Build headers */ spans_addr_copy(&spans_bcastaddr, &chp->ch_dst); spans_addr_copy(spp->sp_addr.address, &chp->ch_src); *(u_int *)&chp->ch_proto = *(u_int *)&spanscls_hdr.ch_proto; *(u_int *)&chp->ch_dsap = *(u_int *)&spanscls_hdr.ch_dsap; *(u_short *)&chp->ch_oui[1] = *(u_short *)&spanscls_hdr.ch_oui[1]; chp->ch_pid = htons(ETHERTYPE_ARP); /* * Build ARP packet */ ahp->ah_hrd = htons(ARP_SPANS); ahp->ah_pro = htons(ETHERTYPE_IP); ahp->ah_hln = sizeof(spans_addr); ahp->ah_pln = sizeof(struct in_addr); ahp->ah_op = htons(ARP_REQUEST); spans_addr_copy(spp->sp_addr.address, &ahp->ah_sha); KM_COPY(&(IA_SIN(inp->inf_addr)->sin_addr), ahp->ah_spa, sizeof(struct in_addr)); KM_COPY(&sap->sa_dstip, ahp->ah_tpa, sizeof(struct in_addr)); /* * Now, send the pdu via the CLS service */ err = atm_cm_cpcs_data(clp->cls_conn, m); if (err) { KB_FREEALL(m); return (1); } return (0); } /* * 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(clp, m) 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 s, 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 == 0) 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 (defined(BSD) && (BSD >= 199306)) if (in_broadcast(in_src, &inp->inf_nif->nif_if)) #else if (in_broadcast(in_src)) #endif goto chkop; /* * Update arp table with source address info */ s = splnet(); 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); } } (void) splx(s); 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 SPANS ARP aging timer tick * * This function is called every SPANSARP_AGING seconds, in order to age * all the arp table entries. * * Called at splnet. * * Arguments: * tip pointer to spansarp aging timer control block * * Returns: * none * */ static void spansarp_aging(tip) struct atm_time *tip; { struct spansarp *sap, *snext; struct ipvcc *ivp, *inext; int i; /* * Schedule next timeout */ atm_timeout(&spansarp_timer, SPANSARP_AGING, spansarp_aging); /* * Run through arp table bumping each entry's aging timer. */ for (i = 0; i < SPANSARP_HASHSIZ; i++) { for (sap = spansarp_arptab[i]; sap; sap = snext) { snext = sap->sa_next; /* * Permanent (manually installed) entries aren't aged */ if (sap->sa_origin == SAO_PERM) continue; /* * See if entry is valid and over-aged */ if ((sap->sa_flags & SAF_VALID) == 0) continue; if (++sap->sa_reftime < SPANSARP_MAXAGE) continue; /* * Entry is now invalid, tell IP/ATM about it */ sap->sa_flags |= SAF_LOCKED; for (ivp = sap->sa_ivp; ivp; ivp = inext) { inext = ivp->iv_arpnext; (*ivp->iv_ipnif->inf_arpnotify) (ivp, MAP_INVALID); } sap->sa_flags &= ~(SAF_LOCKED | SAF_VALID); if (sap->sa_ivp != NULL) { /* * Somebody still cares, so add the arp * entry to the retry list. */ 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 */ (void) spansarp_request(sap); } else { /* * Delete unused entry */ SPANSARP_DELETE(sap); atm_free((caddr_t)sap); } } } } /* * Process a SPANS ARP retry timer tick * * This function is called every SPANSARP_RETRY seconds, in order to retry * awaiting arp resolution requests. We will retry requests indefinitely, * assuming that IP will set a timeout to close the VCC(s) requesting the * failing address resolution. * * Called at splnet. * * Arguments: * tip pointer to spansarp retry timer control block * * Returns: * none * */ static void spansarp_retry(tip) struct atm_time *tip; { struct spansarp *sap; /* * See if there's work to do */ if (spansarp_retry_head == NULL) { return; } /* * Schedule next timeout */ atm_timeout(&spansarp_rtimer, SPANSARP_RETRY, spansarp_retry); /* * Run through retry chain, (re)issuing arp requests. */ for (sap = spansarp_retry_head; sap; sap = sap->sa_next) { /* * Send another arp request */ (void) spansarp_request(sap); } } /* * SPANS ARP IOCTL support * * Function will be called at splnet. * * 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(code, data, arg1) 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; (void) snprintf(aar.aap_intf, sizeof(aar.aap_intf), "%s%d", clp->cls_ipnif->inf_nif->nif_if.if_name, clp->cls_ipnif->inf_nif->nif_if.if_unit ); 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); }