/* * * =================================== * 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_output.c,v 1.3 1999/08/28 01:15:33 peter Exp $ * @(#) $DragonFly: src/usr.sbin/atm/scspd/scsp_output.c,v 1.4 2004/12/18 22:48:02 swildner Exp $ */ /* * Server Cache Synchronization Protocol (SCSP) Support * ---------------------------------------------------- * * Output packet processing * */ #include #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" /* * Put a long integer into the output buffer * * This routine is provided for cases where long ints may not be * word-aligned in the output buffer. * * Arguments: * l long integer * cp pointer to output buffer * * Returns: * None * */ static void put_long(u_long l, u_char *cp) { u_long nl; /* * Convert to network order and copy to output buffer */ nl = htonl(l); UM_COPY(&nl, cp, sizeof(u_long)); } /* * Format a Sender or Receiver ID * * Arguments: * idp ponter to ID structure * buff pointer to ID * * Returns: * 0 input was invalid * else length of ID processed * */ static int scsp_format_id(Scsp_id *idp, char *buff) { /* * Copy the ID */ UM_COPY(idp->id, buff, idp->id_len); /* * Return the ID length */ return(idp->id_len); } /* * Format the Mandatory Common Part of an SCSP input packet * * Arguments: * mcp pointer to MCP * buff pointer to mandatory common part * * Returns: * 0 input was invalid * else length of MCP in message * */ static int scsp_format_mcp(Scsp_mcp *mcp, char *buff) { int len; char *odp; struct scsp_nmcp *smp; /* * Set the protocol ID */ smp = (struct scsp_nmcp *)buff; smp->sm_pid = htons(mcp->pid); /* * Set the server group ID */ smp->sm_sgid = htons(mcp->sgid); /* * Set the flags */ smp->sm_flags = htons(mcp->flags); /* * Set the sender ID and length */ smp->sm_sid_len = mcp->sid.id_len; odp = buff + sizeof(struct scsp_nmcp); len = scsp_format_id(&mcp->sid, odp); if (len == 0) { goto mcp_invalid; } /* * Set the receiver ID and length */ smp->sm_rid_len = mcp->rid.id_len; odp += mcp->sid.id_len; len = scsp_format_id(&mcp->rid, odp); if (len == 0) { goto mcp_invalid; } /* * Set the record count */ smp->sm_rec_cnt = htons(mcp->rec_cnt); /* * Return the length of data we processed */ return(sizeof(struct scsp_nmcp) + mcp->sid.id_len + mcp->rid.id_len); mcp_invalid: return(0); } /* * Format an Extension * * Arguments: * exp pointer to extension in internal format * buff pointer to output buffer * blen space available in buffer * * Returns: * 0 input was invalid * else length of extension processed * */ static int scsp_format_ext(Scsp_ext *exp, char *buff, int blen) { struct scsp_next *sep; /* * Make sure there's room in the buffer */ if (blen < (sizeof(struct scsp_next) + exp->len)) return(0); /* * Set the type */ sep = (struct scsp_next *)buff; sep->se_type = htons(exp->type); /* * Set the length */ sep->se_len = htons(exp->len); /* * Set the value */ if (exp->len > 0) { buff += sizeof(struct scsp_next); UM_COPY((caddr_t)exp + sizeof(Scsp_ext), buff, exp->len); } /* * Return the number of bytes processed */ return(sizeof(struct scsp_next) + exp->len); } /* * Format the ATMARP part of a CSA record * * Arguments: * acsp pointer to ATMARP protocol-specific CSA record * buff pointer to output buffer * * Returns: * 0 input was invalid * else length of record processed * */ static int scsp_format_atmarp(Scsp_atmarp_csa *acsp, char *buff) { char *cp; int len, pkt_len; struct scsp_atmarp_ncsa *sanp; /* * Figure out how long PDU is going to be */ pkt_len = sizeof(struct scsp_atmarp_ncsa); switch (acsp->sa_sha.address_format) { case T_ATM_ENDSYS_ADDR: pkt_len += acsp->sa_sha.address_length; break; case T_ATM_E164_ADDR: pkt_len += acsp->sa_sha.address_length; if (acsp->sa_ssa.address_format == T_ATM_ENDSYS_ADDR) pkt_len += acsp->sa_ssa.address_length; break; } switch (acsp->sa_tha.address_format) { case T_ATM_ENDSYS_ADDR: pkt_len += acsp->sa_tha.address_length; break; case T_ATM_E164_ADDR: pkt_len += acsp->sa_tha.address_length; if (acsp->sa_tha.address_format == T_ATM_ENDSYS_ADDR) pkt_len += acsp->sa_tha.address_length; break; } if (acsp->sa_spa.s_addr != 0) pkt_len += sizeof(struct in_addr); if (acsp->sa_tpa.s_addr != 0) pkt_len += sizeof(struct in_addr); /* * Set up pointers */ sanp = (struct scsp_atmarp_ncsa *)buff; cp = (char *)sanp + sizeof(struct scsp_atmarp_ncsa); /* * Build fields */ sanp->sa_hrd = htons(ARP_ATMFORUM); sanp->sa_pro = htons(ETHERTYPE_IP); /* sa_sha */ len = acsp->sa_sha.address_length; switch (acsp->sa_sha.address_format) { case T_ATM_ENDSYS_ADDR: sanp->sa_shtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); /* sa_sha */ UM_COPY(acsp->sa_sha.address, cp, len); cp += len; sanp->sa_sstl = 0; break; case T_ATM_E164_ADDR: sanp->sa_shtl = ARP_TL_E164 | (len & ARP_TL_LMASK); /* sa_sha */ UM_COPY(acsp->sa_sha.address, cp, len); cp += len; if (acsp->sa_ssa.address_format == T_ATM_ENDSYS_ADDR) { len = acsp->sa_ssa.address_length; sanp->sa_sstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); /* sa_ssa */ UM_COPY(acsp->sa_ssa.address, cp, len); cp += len; } else sanp->sa_sstl = 0; break; default: sanp->sa_shtl = 0; sanp->sa_sstl = 0; } /* sa_state */ sanp->sa_state = acsp->sa_state; sanp->sa_fill1 = 0; /* sa_spa */ if (acsp->sa_spa.s_addr != 0) { sanp->sa_spln = sizeof(struct in_addr); UM_COPY(&acsp->sa_spa, cp, sizeof(struct in_addr)); cp += sizeof(struct in_addr); } /* sa_tha */ len = acsp->sa_tha.address_length; switch (acsp->sa_tha.address_format) { case T_ATM_ENDSYS_ADDR: sanp->sa_thtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); /* sa_tha */ UM_COPY(acsp->sa_tha.address, cp, len); cp += len; sanp->sa_tstl = 0; break; case T_ATM_E164_ADDR: sanp->sa_thtl = ARP_TL_E164 | (len & ARP_TL_LMASK); /* sa_tha */ UM_COPY(acsp->sa_tha.address, cp, len); cp += len; if (acsp->sa_tsa.address_format == T_ATM_ENDSYS_ADDR) { len = acsp->sa_tha.address_length; sanp->sa_tstl = ARP_TL_NSAPA | (len & ARP_TL_LMASK); /* sa_tsa */ UM_COPY(acsp->sa_tsa.address, cp, len); cp += len; } else sanp->sa_tstl = 0; break; default: sanp->sa_thtl = 0; sanp->sa_tstl = 0; } /* sa_tpa */ if (acsp->sa_tpa.s_addr != 0) { sanp->sa_tpln = sizeof(struct in_addr); UM_COPY(&acsp->sa_tpa, cp, sizeof(struct in_addr)); } return(pkt_len); } /* * Format a Cache State Advertisement or Cache State Advertisement * Summary record * * Arguments: * csapp pointer to CSA or CSAS * buff pointer to output buffer * * Returns: * 0 input was invalid * else length of record processed * */ static int scsp_format_csa(Scsp_csa *csap, char *buff) { int len = 0; char *odp; struct scsp_ncsa *scp; /* * Set the hop count */ scp = (struct scsp_ncsa *)buff; scp->scs_hop_cnt = htons(csap->hops); /* * Set the null flag */ if (csap->null) { scp->scs_nfill = htons(SCSP_CSAS_NULL); } /* * Set the sequence number */ put_long(csap->seq, (u_char *)&scp->scs_seq); /* * Set the cache key */ scp->scs_ck_len = csap->key.key_len; odp = buff + sizeof(struct scsp_ncsa); UM_COPY(csap->key.key, odp, scp->scs_ck_len); /* * Set the originator ID */ odp += scp->scs_ck_len; scp->scs_oid_len = scsp_format_id(&csap->oid, odp); /* * Set the protocol-specific data, if present. At the * moment, we only handle data for ATMARP. */ if (csap->atmarp_data) { odp += scp->scs_oid_len; len = scsp_format_atmarp(csap->atmarp_data, odp); } /* * Set the record length */ scp->scs_len = htons(sizeof(struct scsp_ncsa) + scp->scs_ck_len + scp->scs_oid_len + len); /* * Return the length of data we processed */ return(ntohs(scp->scs_len)); } /* * Format a Cache Alignment message * * Arguments: * cap pointer to CA message * buff pointer to output buffer * blen space available in buffer * * Returns: * 0 input was invalid * else length of CA message processed * */ static int scsp_format_ca(Scsp_ca *cap, char *buff, int blen) { int i, len, proc_len; struct scsp_nca *scap; Scsp_csa *csap; /* * Set the sequence number */ scap = (struct scsp_nca *)buff; put_long(cap->ca_seq, (u_char *)&scap->sca_seq); proc_len = sizeof(scap->sca_seq); buff += sizeof(scap->sca_seq); /* * Set the flags */ cap->ca_mcp.flags = 0; if (cap->ca_m) cap->ca_mcp.flags |= SCSP_CA_M; if (cap->ca_i) cap->ca_mcp.flags |= SCSP_CA_I; if (cap->ca_o) cap->ca_mcp.flags |= SCSP_CA_O; /* * Format the mandatory common part of the message */ len = scsp_format_mcp(&cap->ca_mcp, buff); if (len == 0) goto ca_invalid; buff += len; proc_len += len; /* * Put any CSAS records into the message */ for (i = 0, csap = cap->ca_csa_rec; i < cap->ca_mcp.rec_cnt; i++, csap = csap->next) { len = scsp_format_csa(csap, buff); buff += len; proc_len += len; if (proc_len > blen) { scsp_log(LOG_CRIT, "scsp_format_ca: buffer overflow"); abort(); } } /* * Return the length of processed data */ return(proc_len); ca_invalid: return(0); } /* * Format 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: * csup pointer to location to put pointer to CSU Req message * buff pointer to output buffer * blen space available in buffer * * Returns: * 0 input was invalid * else length of CSU Req message processed * */ static int scsp_format_csu(Scsp_csu_msg *csup, char *buff, int blen) { int i, len, proc_len; struct scsp_ncsu_msg *scsup; Scsp_csa *csap; /* * Format the mandatory common part of the message */ scsup = (struct scsp_ncsu_msg *)buff; len = scsp_format_mcp(&csup->csu_mcp, buff); if (len == 0) goto csu_invalid; buff += len; proc_len = len; /* * Put the CSAS records into the message */ for (i = 0, csap = csup->csu_csa_rec; i < csup->csu_mcp.rec_cnt && csap; i++, csap = csap->next) { len = scsp_format_csa(csap, buff); buff += len; proc_len += len; if (proc_len > blen) { scsp_log(LOG_CRIT, "scsp_format_csu: buffer overflow"); abort(); } } /* * Return the length of processed data */ return(proc_len); csu_invalid: return(0); } /* * Format a Hello message * * Arguments: * hpp pointer to Hello message * buff pointer to output buffer * blen space available in buffer * * Returns: * 0 input was invalid * else length of Hello message processed * */ static int scsp_format_hello(Scsp_hello *hp, char *buff, int blen) { int len, proc_len; struct scsp_nhello *shp; Scsp_id *ridp; /* * Set the hello interval */ shp = (struct scsp_nhello *)buff; shp->sch_hi = htons(hp->hello_int); /* * Set the dead factor */ shp->sch_df = htons(hp->dead_factor); /* * Set the family ID */ shp->sch_fid = htons(hp->family_id); /* * Process the mandatory common part of the message */ proc_len = sizeof(struct scsp_nhello) - sizeof(struct scsp_nmcp); buff += proc_len; len = scsp_format_mcp(&hp->hello_mcp, buff); if (len == 0) goto hello_invalid; proc_len += len; buff += len; /* * Add any additional receiver ID records to the message */ for (ridp = hp->hello_mcp.rid.next; ridp; ridp = ridp->next) { len = scsp_format_id(ridp, buff); if (len == 0) { goto hello_invalid; } proc_len += len; buff += len; } /* * Return the length of the Hello message body */ if (proc_len > blen) { scsp_log(LOG_CRIT, "scsp_format_hello: buffer overflow"); abort(); } return(proc_len); hello_invalid: return(0); } /* * Format an SCSP output packet * * Arguments: * dcsp pointer to DCS for which message is being prepared * msg pointer to input packet * bpp pointer to location to put pointer to formatted packet * * Returns: * 0 input packet was invalid * else length of formatted packet * */ int scsp_format_msg(Scsp_dcs *dcsp, Scsp_msg *msg, char **bpp) { char *buff = (char *)0, *e_buff = (char *)0; int buff_len, e_buff_len; int e_len, len, plen; struct scsp_nhdr *shp; Scsp_ext *exp; /* * Allocate a buffer for the message */ buff_len = dcsp->sd_server->ss_mtu; buff = (char *)UM_ALLOC(buff_len); if (!buff) { scsp_mem_err("scsp_format_msg: dcsp->sd_server->ss_mtu"); } UM_ZERO(buff, buff_len); *bpp = buff; /* * Encode the fixed header * * Set the version */ shp = (struct scsp_nhdr *)buff; shp->sh_ver = SCSP_VER_1; /* * Set the message type */ shp->sh_type = msg->sc_msg_type; /* * Point past the fixed header */ len = sizeof(struct scsp_nhdr); buff_len -= len; /* * Encode any extensions into a temporary buffer */ e_len = 0; if (msg->sc_ext) { /* * Get a buffer for the extensions */ e_buff_len = 1024; e_buff = (char *)UM_ALLOC(e_buff_len); if (!buff) { scsp_mem_err("scsp_format_msg: e_buff_len"); } UM_ZERO(e_buff, e_buff_len); /* * Encode the extensions */ for (exp = msg->sc_ext = 0; exp; exp = exp->next) { plen = scsp_format_ext(exp, e_buff + e_len, e_buff_len - e_len); if (plen == 0) { goto ignore; } e_len += plen; } /* * Free the buffer if we didn't use it */ if (!e_len) { UM_FREE(e_buff); e_buff = (char *)0; } } buff_len -= e_len; /* * Encode the body of the message, depending on the type */ switch(msg->sc_msg_type) { case SCSP_CA_MSG: plen = scsp_format_ca(msg->sc_ca, buff + len, buff_len); break; case SCSP_CSU_REQ_MSG: case SCSP_CSU_REPLY_MSG: case SCSP_CSUS_MSG: plen = scsp_format_csu(msg->sc_csu_msg, buff + len, buff_len); break; case SCSP_HELLO_MSG: plen = scsp_format_hello(msg->sc_hello, buff + len, buff_len); break; default: goto ignore; } if (plen == 0) { goto ignore; } len += plen; /* * Copy the extensions to the end of the message */ if (e_len) { shp->sh_ext_off = htons(len); UM_COPY(e_buff, buff + len, e_len); UM_FREE(e_buff); } /* * Set the length */ shp->sh_len = htons(len); /* * Compute the message checksum */ shp->sh_checksum = htons(ip_checksum(buff, len)); /* * Return the length of the buffer */ return(len); ignore: if (buff) UM_FREE(buff); if (e_buff) UM_FREE(e_buff); *bpp = (char *)0; return(0); } /* * Send an SCSP message * * Arguments: * dcsp pointer to DCS control block * msg pointer to message to send * * Returns: * 0 success * errno error encountered * */ int scsp_send_msg(Scsp_dcs *dcsp, Scsp_msg *msg) { int len, rc; char *buff; /* * Make sure we have a socket open */ if (dcsp->sd_sock == -1) { return(EBADF); } /* * Trace the message */ if (((scsp_trace_mode & SCSP_TRACE_HELLO_MSG) && msg->sc_msg_type == SCSP_HELLO_MSG) || ((scsp_trace_mode & SCSP_TRACE_CA_MSG) && msg->sc_msg_type != SCSP_HELLO_MSG)) { scsp_trace_msg(dcsp, msg, 0); scsp_trace("\n"); } /* * Put the message into network format */ len = scsp_format_msg(dcsp, msg, &buff); if (len == 0) { scsp_log(LOG_ERR, "scsp_send_msg: message conversion failed\n"); abort(); } /* * Write the message to the DCS */ rc = write(dcsp->sd_sock, (void *)buff, len); UM_FREE(buff); if (rc == len || (rc == -1 && errno == EINPROGRESS)) { rc = 0; } else { /* * There was an error on the write--close the VCC */ close(dcsp->sd_sock); dcsp->sd_sock = -1; /* * Inform the Hello FSM */ scsp_hfsm(dcsp, SCSP_HFSM_VC_CLOSED, (Scsp_msg *)0); /* * Set the return code */ if (rc == -1) rc = errno; else rc = EINVAL; } return(rc); }