/* * * =================================== * 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: src/usr.sbin/atm/scspd/scsp_input.c,v 1.3 1999/08/28 01:15:33 peter Exp $ * */ /* * Server Cache Synchronization Protocol (SCSP) Support * ---------------------------------------------------- * * Input packet processing * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scsp_msg.h" #include "scsp_if.h" #include "scsp_var.h" #ifndef lint __RCSID("@(#) $FreeBSD: src/usr.sbin/atm/scspd/scsp_input.c,v 1.3 1999/08/28 01:15:33 peter Exp $"); #endif static int scsp_parse_atmarp __P((char *, int, Scsp_atmarp_csa **)); /* * Get a long ingeter * * This routine is provided to handle long integers that may not * be word-aligned in the input buffer. * * Arguments: * cp pointer to long int in message * * Returns: * int long int in host order * */ static u_long get_long(cp) u_char *cp; { int i; u_long l; /* * Read the long out of the input buffer */ l = 0; for (i = 0; i < sizeof(u_long); i++) l = (l << 8) + *cp++; /* * Return the value in host order */ return(l); } /* * Free an SCSP Cache Alignment message in internal format * * Arguments: * cap pointer to CA message * * Returns: * None * */ static void scsp_free_ca(cap) Scsp_ca *cap; { Scsp_csa *csap, *ncsap; /* * Return if there's nothing to free */ if (cap == (Scsp_ca *)0) return; /* * Free the CSAS records */ for (csap = cap->ca_csa_rec; csap; csap = ncsap) { ncsap = csap->next; SCSP_FREE_CSA(csap); } /* * Free the CA message structure */ UM_FREE(cap); } /* * Free an SCSP Cache State Update Request, Cache State Update Reply, * or Cache State Update Solicit message in internal format * * Arguments: * csup pointer to CSU message * * Returns: * None * */ static void scsp_free_csu(csup) Scsp_csu_msg *csup; { Scsp_csa *csap, *ncsap; /* * Return if there's nothing to free */ if (csup == (Scsp_csu_msg *)0) return; /* * Free the CSA records */ for (csap = csup->csu_csa_rec; csap; csap = ncsap) { ncsap = csap->next; SCSP_FREE_CSA(csap); } /* * Free the CSU message structure */ UM_FREE(csup); } /* * Free an SCSP Hello message in internal format * * Arguments: * hp pointer to Hello message * * Returns: * None * */ static void scsp_free_hello(hp) Scsp_hello *hp; { /* * Return if there's nothing to free */ if (hp == (Scsp_hello *)0) return; /* * Free the Hello message structure */ UM_FREE(hp); } /* * Free an SCSP message in internal format * * Arguments: * msg pointer to input packet * * Returns: * None * */ void scsp_free_msg(msg) Scsp_msg *msg; { Scsp_ext *exp, *nexp; /* * Return if there's nothing to free */ if (msg == (Scsp_msg *)0) return; /* * Free the message body */ switch(msg->sc_msg_type) { case SCSP_CA_MSG: scsp_free_ca(msg->sc_ca); break; case SCSP_CSU_REQ_MSG: case SCSP_CSU_REPLY_MSG: case SCSP_CSUS_MSG: scsp_free_csu(msg->sc_csu_msg); break; case SCSP_HELLO_MSG: scsp_free_hello(msg->sc_hello); break; } /* * Free any extensions */ for (exp = msg->sc_ext; exp; exp = nexp) { nexp = exp->next; UM_FREE(exp); } /* * Free the message structure */ UM_FREE(msg); } /* * Parse a Sender or Receiver ID * * Arguments: * buff pointer to ID * id_len length of ID * idp pointer to structure to receive the ID * * Returns: * 0 input was invalid * else length of ID processed * */ static int scsp_parse_id(buff, id_len, idp) char *buff; int id_len; Scsp_id *idp; { /* * Sanity check */ if (!buff || id_len == 0 || id_len > SCSP_MAX_ID_LEN || !idp) { return(0); } /* * Save the ID length */ idp->id_len = id_len; /* * Get the ID */ UM_COPY(buff, idp->id, id_len); /* * Return the ID length */ return(id_len); } /* * Parse the Mandatory Common Part of an SCSP input packet * * Arguments: * buff pointer to mandatory common part * pdu_len length of input packet * mcp pointer to location of MCP in decoded record * * Returns: * 0 input was invalid * else length of MCP in message * */ static int scsp_parse_mcp(buff, pdu_len, mcp) char *buff; int pdu_len; Scsp_mcp *mcp; { int len; u_char *idp; struct scsp_nmcp *smp; /* * Get the protocol ID */ smp = (struct scsp_nmcp *)buff; mcp->pid = ntohs(smp->sm_pid); if (mcp->pid < SCSP_PROTO_ATMARP || mcp->pid > SCSP_PROTO_LNNI) { /* Protocol ID is invalid */ goto mcp_invalid; } /* * Get the server group ID */ mcp->sgid = ntohs(smp->sm_sgid); /* * Get the flags */ mcp->flags = ntohs(smp->sm_flags); /* * Get the sender ID and length */ idp = (u_char *) ((caddr_t)smp + sizeof(struct scsp_nmcp)); len = scsp_parse_id(idp, smp->sm_sid_len, &mcp->sid); if (len == 0) { goto mcp_invalid; } /* * Get the receiver ID and length */ idp += len; len = scsp_parse_id(idp, smp->sm_rid_len, &mcp->rid); if (len == 0) { goto mcp_invalid; } /* * Get the record count */ mcp->rec_cnt = ntohs(smp->sm_rec_cnt); /* * Return the length of data we processed */ return(sizeof(struct scsp_nmcp) + smp->sm_sid_len + smp->sm_rid_len); mcp_invalid: return(0); } /* * Parse an Extension * * Arguments: * buff pointer to Extension * pdu_len length of buffer * expp pointer to location to receive pointer to the Extension * * Returns: * 0 input was invalid * else length of Extension processed * */ static int scsp_parse_ext(buff, pdu_len, expp) char *buff; int pdu_len; Scsp_ext **expp; { int len; struct scsp_next *sep; Scsp_ext *exp; /* * Get memory for the extension */ sep = (struct scsp_next *)buff; len = sizeof(Scsp_ext) + ntohs(sep->se_len); exp = (Scsp_ext *)UM_ALLOC(len); if (!exp) { goto ext_invalid; } UM_ZERO(exp, len); /* * Get the type */ exp->type = ntohs(sep->se_type); /* * Get the length */ exp->len = ntohs(sep->se_len); /* * Get the value */ if (exp->len > 0) { UM_COPY((caddr_t)sep + sizeof(struct scsp_next), (caddr_t)exp + sizeof(Scsp_ext), exp->len); } /* * Save a pointer to the extension and return the * number of bytes processed */ *expp = exp; return(sizeof(struct scsp_next) + exp->len); ext_invalid: if (exp) { UM_FREE(exp); } return(0); } /* * Parse a Cache State Advertisement or Cache State Advertisement * Summary record * * Arguments: * buff pointer to CSA or CSAS record * pdu_len length of input packet * csapp pointer to location to put pointer to CSA or CSAS * * Returns: * 0 input was invalid * else length of record processed * */ static int scsp_parse_csa(buff, pdu_len, csapp) char *buff; int pdu_len; Scsp_csa **csapp; { int len; char *idp; struct scsp_ncsa *scp; Scsp_csa *csap = NULL; /* * Check the record length */ scp = (struct scsp_ncsa *)buff; if (ntohs(scp->scs_len) < (sizeof(struct scsp_ncsa) + scp->scs_ck_len + scp->scs_oid_len)) { goto csa_invalid; } /* * Get memory for the returned structure */ len = sizeof(Scsp_csa) + ntohs(scp->scs_len) - sizeof(struct scsp_ncsa) - scp->scs_ck_len - scp->scs_oid_len; csap = (Scsp_csa *)UM_ALLOC(len); if (!csap) { goto csa_invalid; } UM_ZERO(csap, len); /* * Get the hop count */ csap->hops = ntohs(scp->scs_hop_cnt); /* * Set the null flag */ csap->null = (ntohs(scp->scs_nfill) & SCSP_CSAS_NULL) != 0; /* * Get the sequence number */ csap->seq = get_long((u_char *)&scp->scs_seq); /* * Get the cache key */ if (scp->scs_ck_len == 0 || scp->scs_ck_len > SCSP_MAX_KEY_LEN) { goto csa_invalid; } csap->key.key_len = scp->scs_ck_len; idp = (char *) ((caddr_t)scp + sizeof(struct scsp_ncsa)); UM_COPY(idp, csap->key.key, scp->scs_ck_len); /* * Get the originator ID */ idp += scp->scs_ck_len; len = scsp_parse_id(idp, scp->scs_oid_len, &csap->oid); if (len == 0) { goto csa_invalid; } /* * Get the protocol-specific data, if present */ len = ntohs(scp->scs_len) - (sizeof(struct scsp_ncsa) + scp->scs_ck_len + scp->scs_oid_len); if (len > 0) { idp += scp->scs_oid_len; len = scsp_parse_atmarp(idp, len, &csap->atmarp_data); if (len == 0) goto csa_invalid; } /* * Set a pointer to the MCP and return the length * of data we processed */ *csapp = csap; return(ntohs(scp->scs_len)); csa_invalid: if (csap) SCSP_FREE_CSA(csap); return(0); } /* * Parse a Cache Alignment message * * Arguments: * buff pointer to start of CA in message * pdu_len length of input packet * capp pointer to location to put pointer to CA message * * Returns: * 0 input was invalid * else length of CA message processed * */ static int scsp_parse_ca(buff, pdu_len, capp) char *buff; int pdu_len; Scsp_ca **capp; { int i, len, proc_len; struct scsp_nca *scap; Scsp_ca *cap; Scsp_csa **csapp; /* * Get memory for the returned structure */ scap = (struct scsp_nca *)buff; cap = (Scsp_ca *)UM_ALLOC(sizeof(Scsp_ca)); if (!cap) { goto ca_invalid; } UM_ZERO(cap, sizeof(Scsp_ca)); /* * Get the sequence number */ cap->ca_seq = get_long((u_char *)&scap->sca_seq); proc_len = sizeof(scap->sca_seq); buff += sizeof(scap->sca_seq); /* * Process the mandatory common part of the message */ len = scsp_parse_mcp(buff, pdu_len - proc_len, &cap->ca_mcp); if (len == 0) goto ca_invalid; buff += len; proc_len += len; /* * Set the flags */ cap->ca_m = (cap->ca_mcp.flags & SCSP_CA_M) != 0; cap->ca_i = (cap->ca_mcp.flags & SCSP_CA_I) != 0; cap->ca_o = (cap->ca_mcp.flags & SCSP_CA_O) != 0; /* * Get the CSAS records from the message */ for (i = 0, csapp = &cap->ca_csa_rec; i < cap->ca_mcp.rec_cnt; i++, csapp = &(*csapp)->next) { len = scsp_parse_csa(buff, pdu_len - proc_len, csapp); buff += len; proc_len += len; } /* * Set the address of the CA message and * return the length of processed data */ *capp = cap; return(proc_len); ca_invalid: if (cap) scsp_free_ca(cap); return(0); } /* * Parse the ATMARP-specific part of a CSA record * * Arguments: * buff pointer to ATMARP part of CSU message * pdu_len length of data to process * acspp pointer to location to put pointer to CSU message * * Returns: * 0 input was invalid * else length of CSU Req message processed * */ static int scsp_parse_atmarp(buff, pdu_len, acspp) char *buff; int pdu_len; Scsp_atmarp_csa **acspp; { int len, proc_len; struct scsp_atmarp_ncsa *sacp; Scsp_atmarp_csa *acsp = NULL; /* * Initial packet verification */ sacp = (struct scsp_atmarp_ncsa *)buff; if ((sacp->sa_hrd != ntohs(ARP_ATMFORUM)) || (sacp->sa_pro != ntohs(ETHERTYPE_IP))) goto acs_invalid; /* * Get memory for the returned structure */ acsp = (Scsp_atmarp_csa *)UM_ALLOC(sizeof(Scsp_atmarp_csa)); if (!acsp) { goto acs_invalid; } UM_ZERO(acsp, sizeof(Scsp_atmarp_csa)); /* * Get state code */ acsp->sa_state = sacp->sa_state; proc_len = sizeof(struct scsp_atmarp_ncsa); /* * Verify/gather source ATM address */ acsp->sa_sha.address_format = T_ATM_ABSENT; acsp->sa_sha.address_length = 0; if ((len = (sacp->sa_shtl & ARP_TL_LMASK)) != 0) { if (sacp->sa_shtl & ARP_TL_E164) { if (len > sizeof(Atm_addr_e164)) goto acs_invalid; acsp->sa_sha.address_format = T_ATM_E164_ADDR; } else { if (len != sizeof(Atm_addr_nsap)) goto acs_invalid; acsp->sa_sha.address_format = T_ATM_ENDSYS_ADDR; } acsp->sa_sha.address_length = len; if (pdu_len < proc_len + len) goto acs_invalid; UM_COPY(&buff[proc_len], (char *)acsp->sa_sha.address, len); proc_len += len; } /* * Verify/gather source ATM subaddress */ acsp->sa_ssa.address_format = T_ATM_ABSENT; acsp->sa_ssa.address_length = 0; if ((len = (sacp->sa_sstl & ARP_TL_LMASK)) != 0) { if (((sacp->sa_sstl & ARP_TL_TMASK) != ARP_TL_NSAPA) || (len != sizeof(Atm_addr_nsap))) goto acs_invalid; acsp->sa_ssa.address_format = T_ATM_ENDSYS_ADDR; acsp->sa_ssa.address_length = len; if (pdu_len < proc_len + len) goto acs_invalid; UM_COPY(&buff[proc_len], (char *)acsp->sa_ssa.address, len); proc_len += len; } /* * Verify/gather source IP address */ if ((len = sacp->sa_spln) != 0) { if (len != sizeof(struct in_addr)) goto acs_invalid; if (pdu_len < proc_len + len) goto acs_invalid; UM_COPY(&buff[proc_len], (char *)&acsp->sa_spa, len); proc_len += len; } else { acsp->sa_spa.s_addr = 0; } /* * Verify/gather target ATM address */ acsp->sa_tha.address_format = T_ATM_ABSENT; acsp->sa_tha.address_length = 0; if ((len = (sacp->sa_thtl & ARP_TL_LMASK)) != 0) { if (sacp->sa_thtl & ARP_TL_E164) { if (len > sizeof(Atm_addr_e164)) goto acs_invalid; acsp->sa_tha.address_format = T_ATM_E164_ADDR; } else { if (len != sizeof(Atm_addr_nsap)) goto acs_invalid; acsp->sa_tha.address_format = T_ATM_ENDSYS_ADDR; } acsp->sa_tha.address_length = len; if (pdu_len < proc_len + len) goto acs_invalid; UM_COPY(&buff[proc_len], (char *)acsp->sa_tha.address, len); proc_len += len; } /* * Verify/gather target ATM subaddress */ acsp->sa_tsa.address_format = T_ATM_ABSENT; acsp->sa_tsa.address_length = 0; if ((len = (sacp->sa_tstl & ARP_TL_LMASK)) != 0) { if (((sacp->sa_tstl & ARP_TL_TMASK) != ARP_TL_NSAPA) || (len != sizeof(Atm_addr_nsap))) goto acs_invalid; acsp->sa_tsa.address_format = T_ATM_ENDSYS_ADDR; acsp->sa_tsa.address_length = len; if (pdu_len < proc_len + len) goto acs_invalid; UM_COPY(&buff[proc_len], (char *)acsp->sa_tsa.address, len); proc_len += len; } /* * Verify/gather target IP address */ if ((len = sacp->sa_tpln) != 0) { if (len != sizeof(struct in_addr)) goto acs_invalid; if (pdu_len < proc_len + len) goto acs_invalid; UM_COPY(&buff[proc_len], (char *)&acsp->sa_tpa, len); proc_len += len; } else { acsp->sa_tpa.s_addr = 0; } /* * Verify packet length */ if (proc_len != pdu_len) goto acs_invalid; *acspp = acsp; return(proc_len); acs_invalid: if (acsp) UM_FREE(acsp); return(0); } /* * Parse a Cache State Update Request, Cache State Update Reply, or * Cache State Update Solicit message. These all have the same format, * a Mandatory Common Part followed by a number of CSA or CSAS records. * * Arguments: * buff pointer to start of CSU message * pdu_len length of input packet * csupp pointer to location to put pointer to CSU message * * Returns: * 0 input was invalid * else length of CSU Req message processed * */ static int scsp_parse_csu(buff, pdu_len, csupp) char *buff; int pdu_len; Scsp_csu_msg **csupp; { int i, len, proc_len; Scsp_csu_msg *csup; Scsp_csa **csapp; /* * Get memory for the returned structure */ csup = (Scsp_csu_msg *)UM_ALLOC(sizeof(Scsp_csu_msg)); if (!csup) { goto csu_invalid; } UM_ZERO(csup, sizeof(Scsp_csu_msg)); /* * Process the mandatory common part of the message */ len = scsp_parse_mcp(buff, pdu_len, &csup->csu_mcp); if (len == 0) goto csu_invalid; buff += len; proc_len = len; /* * Get the CSAS records from the message */ for (i = 0, csapp = &csup->csu_csa_rec; i < csup->csu_mcp.rec_cnt; i++, csapp = &(*csapp)->next) { len = scsp_parse_csa(buff, pdu_len - proc_len, csapp); buff += len; proc_len += len; } /* * Set the address of the CSU Req message and * return the length of processed data */ *csupp = csup; return(proc_len); csu_invalid: if (csup) scsp_free_csu(csup); return(0); } /* * Parse a Hello message * * Arguments: * buff pointer to start of Hello in message * pdu_len length of input packet * hpp pointer to location to put pointer to Hello message * * Returns: * 0 input was invalid * else length of Hello message processed * */ static int scsp_parse_hello(buff, pdu_len, hpp) char *buff; int pdu_len; Scsp_hello **hpp; { int i, len, proc_len; struct scsp_nhello *shp = (struct scsp_nhello *)buff; Scsp_hello *hp; Scsp_id *idp; Scsp_id **ridpp; /* * Get memory for the returned structure */ hp = (Scsp_hello *)UM_ALLOC(sizeof(Scsp_hello)); if (!hp) { goto hello_invalid; } UM_ZERO(hp, sizeof(Scsp_hello)); /* * Get the hello interval */ hp->hello_int = ntohs(shp->sch_hi); /* * Get the dead factor */ hp->dead_factor = ntohs(shp->sch_df); /* * Get the family ID */ hp->family_id = ntohs(shp->sch_fid); /* * Process the mandatory common part of the message */ proc_len = sizeof(struct scsp_nhello) - sizeof(struct scsp_nmcp); buff += proc_len; len = scsp_parse_mcp(buff, pdu_len - proc_len, &hp->hello_mcp); if (len == 0) goto hello_invalid; buff += len; proc_len += len; /* * Get additional receiver ID records from the message */ for (i = 0, ridpp = &hp->hello_mcp.rid.next; i < hp->hello_mcp.rec_cnt; i++, ridpp = &idp->next) { idp = (Scsp_id *)UM_ALLOC(sizeof(Scsp_id)); if (!idp) { goto hello_invalid; } UM_ZERO(idp, sizeof(Scsp_id)); len = scsp_parse_id(buff, hp->hello_mcp.rid.id_len, idp); if (len == 0) { UM_FREE(idp); goto hello_invalid; } buff += len; proc_len += len; *ridpp = idp; } /* * Set the address of the CA message and * return the length of processed data */ *hpp = hp; return(proc_len); hello_invalid: if (hp) scsp_free_hello(hp); return(0); } /* * Parse an SCSP input packet * * Arguments: * buff pointer to input packet * pdu_len length of input packet * * Returns: * NULL input packet was invalid * else pointer to packet in internal format * */ Scsp_msg * scsp_parse_msg(buff, pdu_len) char *buff; int pdu_len; { int ext_off, len, plen; struct scsp_nhdr *shp; Scsp_msg *msg = (Scsp_msg *)0; Scsp_ext **expp; /* * Check the message checksum */ if (ip_checksum(buff, pdu_len) != 0) { /* * Checksum was bad--discard the message */ goto ignore; } /* * Allocate storage for the message */ msg = (Scsp_msg *)UM_ALLOC(sizeof(Scsp_msg)); if (!msg) { goto ignore; } UM_ZERO(msg, sizeof(Scsp_msg)); /* * Decode the fixed header * * Check the version */ shp = (struct scsp_nhdr *)buff; if (shp->sh_ver != SCSP_VER_1) goto ignore; /* * Get the message type */ msg->sc_msg_type = shp->sh_type; /* * Get and check the length */ len = ntohs(shp->sh_len); if (len != pdu_len) goto ignore; /* * Get the extension offset */ ext_off = ntohs(shp->sh_ext_off); /* * Decode the body of the message, depending on the type */ buff += sizeof(struct scsp_nhdr); len -= sizeof(struct scsp_nhdr); switch(msg->sc_msg_type) { case SCSP_CA_MSG: plen = scsp_parse_ca(buff, len, &msg->sc_ca); break; case SCSP_CSU_REQ_MSG: case SCSP_CSU_REPLY_MSG: case SCSP_CSUS_MSG: plen = scsp_parse_csu(buff, len, &msg->sc_csu_msg); break; case SCSP_HELLO_MSG: plen = scsp_parse_hello(buff, len, &msg->sc_hello); break; default: goto ignore; } if (plen == 0) { goto ignore; } buff += plen; len -= plen; /* * Decode any extensions */ if (ext_off != 0) { for (expp = &msg->sc_ext; len > 0; expp = &(*expp)->next) { plen = scsp_parse_ext(buff, len, expp); if (plen == 0) { goto ignore; } buff += plen; len -= plen; } } /* * Make sure we handled the whole message */ if (len != 0) { goto ignore; } /* * Return the address of the SCSP message in internal format */ return(msg); ignore: if (msg) scsp_free_msg(msg); return(Scsp_msg *)0; }