/* * * =================================== * 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$ * */ /* * ATM Forum UNI Support * --------------------- * * UNI ATMARP support (RFC1577) - Input packet processing * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef lint __RCSID("@(#) $FreeBSD$"); #endif /* * Local functions */ static void proc_arp_req(struct ipvcc *, KBuffer *); static void proc_arp_rsp(struct ipvcc *, KBuffer *); static void proc_arp_nak(struct ipvcc *, KBuffer *); static void proc_inarp_req(struct ipvcc *, KBuffer *); static void proc_inarp_rsp(struct ipvcc *, KBuffer *); /* * Local variables */ static Atm_addr satm; static Atm_addr satmsub; static Atm_addr tatm; static Atm_addr tatmsub; static struct in_addr sip; static struct in_addr tip; /* * Process ATMARP Input Data * * Arguments: * tok uniarp connection token (pointer to ipvcc) * m pointer to input packet buffer chain * * Returns: * none * */ void uniarp_cpcs_data(tok, m) void *tok; KBuffer *m; { struct ipvcc *ivp = tok; struct atmarp_hdr *ahp; KBuffer *n; int len, plen = sizeof(struct atmarp_hdr); #ifdef DIAGNOSTIC if (uniarp_print) uniarp_pdu_print(ivp, m, "receive"); #endif /* * Verify IP's VCC state */ if (ivp->iv_state != IPVCC_ACTIVE) { goto bad; } /* * Get the fixed fields together */ if (KB_LEN(m) < sizeof(struct atmarp_hdr)) { KB_PULLUP(m, sizeof(struct atmarp_hdr), m); if (m == NULL) goto bad; } KB_DATASTART(m, ahp, struct atmarp_hdr *); /* * Initial packet verification */ if ((ahp->ah_hrd != htons(ARP_ATMFORUM)) || (ahp->ah_pro != htons(ETHERTYPE_IP))) goto bad; /* * Verify/gather source address fields */ if ((len = (ahp->ah_shtl & ARP_TL_LMASK)) != 0) { if (ahp->ah_shtl & ARP_TL_E164) { if (len > sizeof(struct atm_addr_e164)) goto bad; satm.address_format = T_ATM_E164_ADDR; } else { if (len != sizeof(struct atm_addr_nsap)) goto bad; satm.address_format = T_ATM_ENDSYS_ADDR; } satm.address_length = len; if (KB_COPYDATA(m, plen, len, (caddr_t)satm.address)) goto bad; plen += len; } else { satm.address_format = T_ATM_ABSENT; satm.address_length = 0; } if ((len = (ahp->ah_sstl & ARP_TL_LMASK)) != 0) { if (((ahp->ah_sstl & ARP_TL_TMASK) != ARP_TL_NSAPA) || (len != sizeof(struct atm_addr_nsap))) goto bad; satmsub.address_format = T_ATM_ENDSYS_ADDR; satmsub.address_length = len; if (KB_COPYDATA(m, plen, len, (caddr_t)satmsub.address)) goto bad; plen += len; } else { satmsub.address_format = T_ATM_ABSENT; satmsub.address_length = 0; } if ((len = ahp->ah_spln) != 0) { if (len != sizeof(struct in_addr)) goto bad; if (KB_COPYDATA(m, plen, len, (caddr_t)&sip)) goto bad; plen += len; } else { sip.s_addr = 0; } /* * Verify/gather target address fields */ if ((len = (ahp->ah_thtl & ARP_TL_LMASK)) != 0) { if (ahp->ah_thtl & ARP_TL_E164) { if (len > sizeof(struct atm_addr_e164)) goto bad; tatm.address_format = T_ATM_E164_ADDR; } else { if (len != sizeof(struct atm_addr_nsap)) goto bad; tatm.address_format = T_ATM_ENDSYS_ADDR; } tatm.address_length = len; if (KB_COPYDATA(m, plen, len, (caddr_t)tatm.address)) goto bad; plen += len; } else { tatm.address_format = T_ATM_ABSENT; tatm.address_length = 0; } if ((len = (ahp->ah_tstl & ARP_TL_LMASK)) != 0) { if (((ahp->ah_tstl & ARP_TL_TMASK) != ARP_TL_NSAPA) || (len != sizeof(struct atm_addr_nsap))) goto bad; tatmsub.address_format = T_ATM_ENDSYS_ADDR; tatmsub.address_length = len; if (KB_COPYDATA(m, plen, len, (caddr_t)tatmsub.address)) goto bad; plen += len; } else { tatmsub.address_format = T_ATM_ABSENT; tatmsub.address_length = 0; } if ((len = ahp->ah_tpln) != 0) { if (len != sizeof(struct in_addr)) goto bad; if (KB_COPYDATA(m, plen, len, (caddr_t)&tip)) goto bad; plen += len; } else { tip.s_addr = 0; } /* * Verify packet length */ for (len = 0, n = m; n; n = KB_NEXT(n)) len += KB_LEN(n); if (len != plen) goto bad; /* * Now finish with packet-specific processing */ switch (ntohs(ahp->ah_op)) { case ARP_REQUEST: proc_arp_req(ivp, m); break; case ARP_REPLY: proc_arp_rsp(ivp, m); break; case INARP_REQUEST: proc_inarp_req(ivp, m); break; case INARP_REPLY: proc_inarp_rsp(ivp, m); break; case ARP_NAK: proc_arp_nak(ivp, m); break; default: goto bad; } return; bad: uniarp_stat.uas_rcvdrop++; if (m) KB_FREEALL(m); } /* * Process an ATMARP request packet * * Arguments: * ivp pointer to input VCC's IPVCC control block * m pointer to input packet buffer chain * * Returns: * none * */ static void proc_arp_req(ivp, m) struct ipvcc *ivp; KBuffer *m; { struct ip_nif *inp; struct atm_nif *nip; struct siginst *sgp; struct uniip *uip; struct uniarp *uap; struct in_addr myip; int s = splnet(); /* * Only an arp server should receive these */ inp = ivp->iv_ipnif; nip = inp->inf_nif; uip = (struct uniip *)inp->inf_isintf; if ((uip == NULL) || (uip->uip_arpstate != UIAS_SERVER_ACTIVE)) goto drop; /* * These should be sent only on SVCs */ if ((ivp->iv_flags & IVF_SVC) == 0) goto drop; /* * Locate our addresses */ sgp = nip->nif_pif->pif_siginst; myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; /* * Target IP address must be present */ if (tip.s_addr == 0) goto drop; /* * Drop packet if both Source addresses aren't present */ if ((sip.s_addr == 0) || (satm.address_format == T_ATM_ABSENT)) goto drop; /* * Source addresses can't be ours */ if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) && ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) { struct vccb *vcp = ivp->iv_conn->co_connvc->cvc_vcc; log(LOG_WARNING, "uniarp: vcc=(%d,%d) reports our ATM address\n", vcp->vc_vpi, vcp->vc_vci); goto drop; } if (sip.s_addr == myip.s_addr) { struct vccb *vcp = ivp->iv_conn->co_connvc->cvc_vcc; log(LOG_WARNING, "uniarp: vcc=(%d,%d) reports our IP address\n", vcp->vc_vpi, vcp->vc_vci); goto drop; } /* * Validate Source IP address */ if (uniarp_validate_ip(uip, &sip, UAO_REGISTER) != 0) goto drop; /* * If the source and target IP addresses are the same, then this * must be a client registration request (RFC-2225). Otherwise, * try to accomodate old clients (per RFC-2225 8.4.4). */ if (sip.s_addr == tip.s_addr) (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_REGISTER); else { uap = (struct uniarp *)ivp->iv_arpent; if ((uap == NULL) || (uap->ua_origin < UAO_REGISTER)) (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_REGISTER); } /* * Lookup the target IP address in the cache (and also check if * the query is for our address). */ UNIARP_LOOKUP(tip.s_addr, uap); if (uap && (uap->ua_flags & UAF_VALID)) { /* * We've found a valid mapping */ (void) uniarp_arp_rsp(uip, &uap->ua_arpmap, &sip, &satm, &satmsub, ivp); } else if (tip.s_addr == myip.s_addr) { /* * We're the target, so respond accordingly */ (void) uniarp_arp_rsp(uip, &uip->uip_arpsvrmap, &sip, &satm, &satmsub, ivp); } else { /* * We don't know who the target is, so NAK the query */ (void) uniarp_arp_nak(uip, m, ivp); m = NULL; } drop: (void) splx(s); if (m) KB_FREEALL(m); return; } /* * Process an ATMARP reply packet * * Arguments: * ivp pointer to input VCC's IPVCC control block * m pointer to input packet buffer chain * * Returns: * none * */ static void proc_arp_rsp(ivp, m) struct ipvcc *ivp; KBuffer *m; { struct ip_nif *inp; struct atm_nif *nip; struct siginst *sgp; struct uniip *uip; struct uniarp *uap; struct in_addr myip; int s = splnet(); /* * Only the arp server should send these */ inp = ivp->iv_ipnif; nip = inp->inf_nif; uip = (struct uniip *)inp->inf_isintf; if ((uip == NULL) || (uip->uip_arpsvrvcc != ivp)) goto drop; /* * Locate our addresses */ sgp = nip->nif_pif->pif_siginst; myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; /* * Target addresses must be ours */ if ((tip.s_addr != myip.s_addr) || !ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) || !ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub)) goto drop; /* * Drop packet if both Source addresses aren't present */ if ((sip.s_addr == 0) || (satm.address_format == T_ATM_ABSENT)) goto drop; /* * If the Source addresses are ours, this is an arp server * registration response */ if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) && ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) { if (sip.s_addr == myip.s_addr) { /* * Registration response - update our state and * set a registration refresh timer */ if (uip->uip_arpstate == UIAS_CLIENT_REGISTER) uip->uip_arpstate = UIAS_CLIENT_ACTIVE; if (uip->uip_arpstate == UIAS_CLIENT_ACTIVE) { UNIIP_ARP_CANCEL(uip); UNIIP_ARP_TIMER(uip, UNIARP_REGIS_REFRESH); } /* * If the cache entry for the server VCC isn't valid * yet, then send an Inverse ATMARP request to solicit * the server's IP address */ uap = (struct uniarp *)ivp->iv_arpent; if ((uap->ua_flags & UAF_VALID) == 0) { (void) uniarp_inarp_req(uip, &uap->ua_dstatm, &uap->ua_dstatmsub, ivp); } goto drop; } else { log(LOG_WARNING, "uniarp: arpserver has our IP address wrong\n"); goto drop; } } else if (sip.s_addr == myip.s_addr) { log(LOG_WARNING, "uniarp: arpserver has our ATM address wrong\n"); goto drop; } /* * Validate the Source IP address */ if (uniarp_validate_ip(uip, &sip, UAO_LOOKUP) != 0) goto drop; /* * Now we believe this packet contains an authoritative mapping, * which we probably need to setup an outgoing SVC connection */ (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_LOOKUP); drop: (void) splx(s); KB_FREEALL(m); return; } /* * Process an ATMARP negative ack packet * * Arguments: * ivp pointer to input VCC's IPVCC control block * m pointer to input packet buffer chain * * Returns: * none * */ static void proc_arp_nak(ivp, m) struct ipvcc *ivp; KBuffer *m; { struct ip_nif *inp; struct atm_nif *nip; struct siginst *sgp; struct uniip *uip; struct uniarp *uap; struct in_addr myip; struct ipvcc *inext; int s = splnet(); /* * Only the arp server should send these */ inp = ivp->iv_ipnif; nip = inp->inf_nif; uip = (struct uniip *)inp->inf_isintf; if ((uip == NULL) || (uip->uip_arpsvrvcc != ivp)) goto drop; /* * Locate our addresses */ sgp = nip->nif_pif->pif_siginst; myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; /* * Source addresses must be ours */ if ((sip.s_addr != myip.s_addr) || !ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) || !ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) goto drop; /* * Drop packet if the Target IP address isn't there or if this * is a registration response, indicating an old or flakey server */ if ((tip.s_addr == 0) || (tip.s_addr == myip.s_addr)) goto drop; /* * Otherwise, see who we were looking for */ UNIARP_LOOKUP(tip.s_addr, uap); if (uap == NULL) goto drop; /* * This entry isn't valid any longer, so notify all VCCs using this * entry that they must finish up. The last notify should cause * this entry to be freed by the vcclose() function. */ uap->ua_flags &= ~UAF_VALID; for (ivp = uap->ua_ivp; ivp; ivp = inext) { inext = ivp->iv_arpnext; (*inp->inf_arpnotify)(ivp, MAP_FAILED); } drop: (void) splx(s); KB_FREEALL(m); return; } /* * Process an InATMARP request packet * * Arguments: * ivp pointer to input VCC's IPVCC control block * m pointer to input packet buffer chain * * Returns: * none * */ static void proc_inarp_req(ivp, m) struct ipvcc *ivp; KBuffer *m; { struct ip_nif *inp; struct atm_nif *nip; struct siginst *sgp; struct uniip *uip; struct in_addr myip; int s = splnet(); /* * Get interface pointers */ inp = ivp->iv_ipnif; nip = inp->inf_nif; uip = (struct uniip *)inp->inf_isintf; if (uip == NULL) goto drop; /* * Locate our addresses */ sgp = nip->nif_pif->pif_siginst; myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; /* * Packet must have a Source IP address and, if it was received * over an SVC, a Source ATM address too. */ if ((sip.s_addr == 0) || ((ivp->iv_flags & IVF_SVC) && (satm.address_format == T_ATM_ABSENT))) goto drop; /* * Validate Source ATM address * - can't be me */ if (satm.address_format != T_ATM_ABSENT) { if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) && ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) goto drop; } /* * Validate Source IP address */ if ((sip.s_addr == myip.s_addr) || (uniarp_validate_ip(uip, &sip, UAO_PEER_REQ) != 0)) goto drop; /* * The Target ATM address is required for a packet received over * an SVC, optional for a PVC. If one is present, it must be our * address. */ if ((ivp->iv_flags & IVF_SVC) && (tatm.address_format == T_ATM_ABSENT)) goto drop; if ((tatm.address_format != T_ATM_ABSENT) && (!ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) || !ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub))) goto drop; /* * See where this packet is from */ if (ivp->iv_flags & IVF_PVC) { /* * Process the PVC arp data, although we don't really * update the arp cache with this information */ uniarp_cache_pvc(ivp, &sip, &satm, &satmsub); } else if (uip->uip_arpsvrvcc == ivp) { /* * Packet is from the arp server, so we've received a * registration/refresh request (1577 version). * * Therefore, update cache with authoritative data. */ (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_LOOKUP); /* * Make sure the cache update didn't kill the server VCC */ if (uip->uip_arpsvrvcc != ivp) goto drop; /* * Update the server state and set the * registration refresh timer */ uip->uip_arpstate = UIAS_CLIENT_ACTIVE; UNIIP_ARP_CANCEL(uip); UNIIP_ARP_TIMER(uip, UNIARP_REGIS_REFRESH); } else { /* * Otherwise, we consider this source mapping data as * non-authoritative and update the cache appropriately */ if (uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_PEER_REQ)) goto drop; } /* * Send an InATMARP response back to originator */ (void) uniarp_inarp_rsp(uip, &sip, &satm, &satmsub, ivp); drop: (void) splx(s); KB_FREEALL(m); return; } /* * Process an InATMARP response packet * * Arguments: * ivp pointer to input VCC's IPVCC control block * m pointer to input packet buffer chain * * Returns: * none * */ static void proc_inarp_rsp(ivp, m) struct ipvcc *ivp; KBuffer *m; { struct ip_nif *inp; struct atm_nif *nip; struct siginst *sgp; struct uniip *uip; struct in_addr myip; int s = splnet(); /* * Get interface pointers */ inp = ivp->iv_ipnif; nip = inp->inf_nif; uip = (struct uniip *)inp->inf_isintf; if (uip == NULL) goto drop; /* * Locate our addresses */ sgp = nip->nif_pif->pif_siginst; myip.s_addr = IA_SIN(inp->inf_addr)->sin_addr.s_addr; /* * Packet must have a Source IP address and, if it was received * over an SVC, a Source ATM address too. */ if ((sip.s_addr == 0) || ((ivp->iv_flags & IVF_SVC) && (satm.address_format == T_ATM_ABSENT))) goto drop; /* * Validate Source ATM address * - can't be me */ if (satm.address_format != T_ATM_ABSENT) { if (ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &satm) && ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &satmsub)) goto drop; } /* * Validate Source IP address * - must be in our LIS * - can't be me * - can't be broadcast * - can't be multicast */ if ((sip.s_addr == myip.s_addr) || (uniarp_validate_ip(uip, &sip, UAO_PEER_RSP) != 0)) goto drop; /* * The Target ATM address is required for a packet received over * an SVC, optional for a PVC. If one is present, it must be our * address. */ if ((ivp->iv_flags & IVF_SVC) && (tatm.address_format == T_ATM_ABSENT)) goto drop; if ((tatm.address_format != T_ATM_ABSENT) && (!ATM_ADDR_SEL_EQUAL(&sgp->si_addr, nip->nif_sel, &tatm) || !ATM_ADDR_SEL_EQUAL(&sgp->si_subaddr, nip->nif_sel, &tatmsub))) goto drop; /* * See where this packet is from */ if (ivp->iv_flags & IVF_PVC) { /* * Process the PVC arp data, although we don't really * update the arp cache with this information */ uniarp_cache_pvc(ivp, &sip, &satm, &satmsub); } else { /* * Can't tell the difference between an RFC-1577 registration * and a data connection from a client of another arpserver * on our LIS (using SCSP) - so we'll update the cache now * with what we've got. Our clients will get "registered" * when (if) they query us with an arp request. */ (void) uniarp_cache_svc(uip, &sip, &satm, &satmsub, UAO_PEER_RSP); } drop: (void) splx(s); KB_FREEALL(m); return; } /* * Print an ATMARP PDU * * Arguments: * ivp pointer to input VCC control block * m pointer to pdu buffer chain * msg pointer to message string * * Returns: * none * */ void uniarp_pdu_print(ivp, m, msg) struct ipvcc *ivp; KBuffer *m; char *msg; { char buf[128]; struct vccb *vcp; vcp = ivp->iv_conn->co_connvc->cvc_vcc; snprintf(buf, sizeof(buf), "uniarp %s: vcc=(%d,%d)\n", msg, vcp->vc_vpi, vcp->vc_vci); atm_pdu_print(m, buf); }