/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sctp_impl.h" #include "sctp_addr.h" /*ARGSUSED*/ size_t sctp_supaddr_param_len(sctp_t *sctp) { return (sizeof (sctp_parm_hdr_t) + sizeof (int32_t)); } size_t sctp_supaddr_param(sctp_t *sctp, uchar_t *p) { sctp_parm_hdr_t *sph; uint16_t *addrtype; conn_t *connp = sctp->sctp_connp; sph = (sctp_parm_hdr_t *)p; sph->sph_type = htons(PARM_SUPP_ADDRS); addrtype = (uint16_t *)(sph + 1); switch (connp->conn_family) { case AF_INET: *addrtype++ = htons(PARM_ADDR4); *addrtype = 0; sph->sph_len = htons(sizeof (*sph) + sizeof (*addrtype)); break; case AF_INET6: *addrtype++ = htons(PARM_ADDR6); if (!sctp->sctp_connp->conn_ipv6_v6only) { *addrtype = htons(PARM_ADDR4); sph->sph_len = htons(sizeof (*sph) + sizeof (*addrtype) * 2); } else { *addrtype = 0; sph->sph_len = htons(sizeof (*sph) + sizeof (*addrtype)); } break; default: break; } return (sizeof (*sph) + (sizeof (*addrtype) * 2)); } /* * Currently, we support on PRSCTP option, there is more to come. */ /*ARGSUSED*/ size_t sctp_options_param_len(const sctp_t *sctp, int option) { size_t optlen; switch (option) { case SCTP_PRSCTP_OPTION: optlen = sizeof (sctp_parm_hdr_t); break; default: ASSERT(0); } return (optlen); } /*ARGSUSED*/ size_t sctp_options_param(const sctp_t *sctp, void *p, int option) { sctp_parm_hdr_t *sph = (sctp_parm_hdr_t *)p; switch (option) { case SCTP_PRSCTP_OPTION: sph->sph_type = htons(PARM_FORWARD_TSN); sph->sph_len = htons(sizeof (*sph)); break; default: ASSERT(0); } return (sizeof (*sph)); } size_t sctp_adaptation_code_param(sctp_t *sctp, uchar_t *p) { sctp_parm_hdr_t *sph; if (!sctp->sctp_send_adaptation) { return (0); } sph = (sctp_parm_hdr_t *)p; sph->sph_type = htons(PARM_ADAPT_LAYER_IND); sph->sph_len = htons(sizeof (*sph) + sizeof (uint32_t)); *(uint32_t *)(sph + 1) = htonl(sctp->sctp_tx_adaptation_code); return (sizeof (*sph) + sizeof (uint32_t)); } mblk_t * sctp_init_mp(sctp_t *sctp, sctp_faddr_t *fp) { mblk_t *mp; uchar_t *p; size_t initlen; sctp_init_chunk_t *icp; sctp_chunk_hdr_t *chp; uint16_t schlen; int supp_af; sctp_stack_t *sctps = sctp->sctp_sctps; conn_t *connp = sctp->sctp_connp; if (connp->conn_family == AF_INET) { supp_af = PARM_SUPP_V4; } else { if (sctp->sctp_connp->conn_ipv6_v6only) supp_af = PARM_SUPP_V6; else supp_af = PARM_SUPP_V6 | PARM_SUPP_V4; } initlen = sizeof (*chp) + sizeof (*icp); if (sctp->sctp_send_adaptation) { initlen += (sizeof (sctp_parm_hdr_t) + sizeof (uint32_t)); } initlen += sctp_supaddr_param_len(sctp); initlen += sctp_addr_params(sctp, supp_af, NULL, B_TRUE); if (sctp->sctp_prsctp_aware && sctps->sctps_prsctp_enabled) initlen += sctp_options_param_len(sctp, SCTP_PRSCTP_OPTION); /* * This could be a INIT retransmission in which case sh_verf may * be non-zero, zero it out just to be sure. */ sctp->sctp_sctph->sh_verf = 0; sctp->sctp_sctph6->sh_verf = 0; mp = sctp_make_mp(sctp, fp, initlen); if (mp == NULL) { SCTP_KSTAT(sctps, sctp_send_init_failed); return (NULL); } /* sctp_make_mp could have discovered we have no usable sources */ if (sctp->sctp_nsaddrs == 0) { freemsg(mp); SCTP_KSTAT(sctps, sctp_send_init_failed); return (NULL); } /* Lay in a new INIT chunk, starting with the chunk header */ chp = (sctp_chunk_hdr_t *)mp->b_wptr; chp->sch_id = CHUNK_INIT; chp->sch_flags = 0; schlen = (uint16_t)initlen; U16_TO_ABE16(schlen, &(chp->sch_len)); mp->b_wptr += initlen; icp = (sctp_init_chunk_t *)(chp + 1); icp->sic_inittag = sctp->sctp_lvtag; U32_TO_ABE32(sctp->sctp_rwnd, &(icp->sic_a_rwnd)); U16_TO_ABE16(sctp->sctp_num_ostr, &(icp->sic_outstr)); U16_TO_ABE16(sctp->sctp_num_istr, &(icp->sic_instr)); U32_TO_ABE32(sctp->sctp_ltsn, &(icp->sic_inittsn)); p = (uchar_t *)(icp + 1); /* Adaptation layer param */ p += sctp_adaptation_code_param(sctp, p); /* Add supported address types parameter */ p += sctp_supaddr_param(sctp, p); /* Add address parameters */ p += sctp_addr_params(sctp, supp_af, p, B_FALSE); /* Add Forward-TSN-Supported param */ if (sctp->sctp_prsctp_aware && sctps->sctps_prsctp_enabled) p += sctp_options_param(sctp, p, SCTP_PRSCTP_OPTION); BUMP_LOCAL(sctp->sctp_obchunks); sctp_set_iplen(sctp, mp, fp->sf_ixa); return (mp); } /* * Extracts the verification tag from an INIT chunk. If the INIT * chunk is truncated or malformed, returns 0. */ uint32_t sctp_init2vtag(sctp_chunk_hdr_t *initch) { sctp_init_chunk_t *init; init = (sctp_init_chunk_t *)(initch + 1); return (init->sic_inittag); } size_t sctp_addr_params(sctp_t *sctp, int af, uchar_t *p, boolean_t modify) { size_t param_len; ASSERT(sctp->sctp_nsaddrs > 0); /* * If we have only one local address or it is a loopback or linklocal * association, we let the peer pull the address from the IP header. */ if ((!modify && sctp->sctp_nsaddrs == 1) || sctp->sctp_loopback || sctp->sctp_linklocal) { return (0); } param_len = sctp_saddr_info(sctp, af, p, modify); return ((sctp->sctp_nsaddrs == 1) ? 0 : param_len); }