/* * Called from sctp_input_data() to add one error chunk to the error * chunks list. The error chunks list will be processed at the end * of sctp_input_data() by calling sctp_process_err(). */ void sctp_add_err(sctp_t *sctp, uint16_t serror, void *details, size_t len, sctp_faddr_t *dest) { sctp_stack_t *sctps = sctp->sctp_sctps; mblk_t *emp; uint32_t emp_len; uint32_t mss; mblk_t *sendmp; sctp_faddr_t *fp; emp = sctp_make_err(sctp, serror, details, len); if (emp == NULL) return; emp_len = MBLKL(emp); if (sctp->sctp_err_chunks != NULL) { fp = SCTP_CHUNK_DEST(sctp->sctp_err_chunks); } else { fp = dest; SCTP_SET_CHUNK_DEST(emp, dest); } mss = fp->sf_pmss; /* * If the current output packet cannot include the new error chunk, * send out the current packet and then add the new error chunk * to the new output packet. */ if (sctp->sctp_err_len + emp_len > mss) { if ((sendmp = sctp_make_mp(sctp, fp, 0)) == NULL) { SCTP_KSTAT(sctps, sctp_send_err_failed); /* Just free the latest error chunk. */ freeb(emp); return; } sendmp->b_cont = sctp->sctp_err_chunks; sctp_set_iplen(sctp, sendmp, fp->sf_ixa); (void) conn_ip_output(sendmp, fp->sf_ixa); BUMP_LOCAL(sctp->sctp_opkts); sctp->sctp_err_chunks = emp; sctp->sctp_err_len = emp_len; SCTP_SET_CHUNK_DEST(emp, dest); } else { if (sctp->sctp_err_chunks != NULL) linkb(sctp->sctp_err_chunks, emp); else sctp->sctp_err_chunks = emp; sctp->sctp_err_len += emp_len; } /* Assume that we will send it out... */ BUMP_LOCAL(sctp->sctp_obchunks); }
/* * Called from sctp_input_data() to send out error chunks created during * the processing of all the chunks in an incoming packet. */ void sctp_process_err(sctp_t *sctp) { sctp_stack_t *sctps = sctp->sctp_sctps; mblk_t *errmp; mblk_t *sendmp; sctp_faddr_t *fp; ASSERT(sctp->sctp_err_chunks != NULL); errmp = sctp->sctp_err_chunks; fp = SCTP_CHUNK_DEST(errmp); if ((sendmp = sctp_make_mp(sctp, fp, 0)) == NULL) { SCTP_KSTAT(sctps, sctp_send_err_failed); freemsg(errmp); goto done; } sendmp->b_cont = errmp; sctp_set_iplen(sctp, sendmp, fp->sf_ixa); (void) conn_ip_output(sendmp, fp->sf_ixa); BUMP_LOCAL(sctp->sctp_opkts); done: sctp->sctp_err_chunks = NULL; sctp->sctp_err_len = 0; }
void sctp_wput_asconf(sctp_t *sctp, sctp_faddr_t *fp) { #define SCTP_SET_SENT_FLAG(mp) ((mp)->b_flag = SCTP_CHUNK_FLAG_SENT) mblk_t *mp; mblk_t *ipmp; uint32_t *snp; sctp_parm_hdr_t *ph; boolean_t isv4; sctp_stack_t *sctps = sctp->sctp_sctps; boolean_t saddr_set; if (sctp->sctp_cchunk_pend || sctp->sctp_cxmit_list == NULL || /* Queue it for later transmission if not yet established */ sctp->sctp_state < SCTPS_ESTABLISHED) { ip2dbg(("sctp_wput_asconf: cchunk pending? (%d) or null "\ "sctp_cxmit_list? (%s) or incorrect state? (%x)\n", sctp->sctp_cchunk_pend, sctp->sctp_cxmit_list == NULL ? "yes" : "no", sctp->sctp_state)); return; } if (fp == NULL) fp = sctp->sctp_current; /* OK to send */ ipmp = sctp_make_mp(sctp, fp, 0); if (ipmp == NULL) { SCTP_FADDR_RC_TIMER_RESTART(sctp, fp, fp->rto); SCTP_KSTAT(sctps, sctp_send_asconf_failed); return; } mp = sctp->sctp_cxmit_list; /* Fill in the mandatory Address Parameter TLV */ isv4 = (fp != NULL) ? fp->isv4 : sctp->sctp_current->isv4; ph = (sctp_parm_hdr_t *)(mp->b_rptr + sizeof (sctp_chunk_hdr_t) + sizeof (uint32_t)); if (isv4) { ipha_t *ipha = (ipha_t *)ipmp->b_rptr; in6_addr_t ipaddr; ipaddr_t addr4; ph->sph_type = htons(PARM_ADDR4); ph->sph_len = htons(PARM_ADDR4_LEN); if (ipha->ipha_src != INADDR_ANY) { bcopy(&ipha->ipha_src, ph + 1, IP_ADDR_LEN); } else { ipaddr = sctp_get_valid_addr(sctp, B_FALSE, &saddr_set); /* * All the addresses are down. * Maybe we might have better luck next time. */ if (!saddr_set) { SCTP_FADDR_RC_TIMER_RESTART(sctp, fp, fp->rto); freeb(ipmp); return; } IN6_V4MAPPED_TO_IPADDR(&ipaddr, addr4); bcopy(&addr4, ph + 1, IP_ADDR_LEN); } } else { ip6_t *ip6 = (ip6_t *)ipmp->b_rptr; in6_addr_t ipaddr; ph->sph_type = htons(PARM_ADDR6); ph->sph_len = htons(PARM_ADDR6_LEN); if (!IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { bcopy(&ip6->ip6_src, ph + 1, IPV6_ADDR_LEN); } else { ipaddr = sctp_get_valid_addr(sctp, B_TRUE, &saddr_set); /* * All the addresses are down. * Maybe we might have better luck next time. */ if (!saddr_set) { SCTP_FADDR_RC_TIMER_RESTART(sctp, fp, fp->rto); freeb(ipmp); return; } bcopy(&ipaddr, ph + 1, IPV6_ADDR_LEN); } } /* Don't exceed CWND */ if ((MBLKL(mp) > (fp->cwnd - fp->suna)) || ((mp = dupb(sctp->sctp_cxmit_list)) == NULL)) { SCTP_FADDR_RC_TIMER_RESTART(sctp, fp, fp->rto); freeb(ipmp); return; } /* Set the serial number now, if sending for the first time */ if (!SCTP_CHUNK_WANT_REXMIT(mp)) { snp = (uint32_t *)(mp->b_rptr + sizeof (sctp_chunk_hdr_t)); *snp = htonl(sctp->sctp_lcsn++); } SCTP_CHUNK_CLEAR_FLAGS(mp); fp->suna += MBLKL(mp); /* Attach the header and send the chunk */ ipmp->b_cont = mp; sctp->sctp_cchunk_pend = 1; SCTP_SET_SENT_FLAG(sctp->sctp_cxmit_list); SCTP_SET_CHUNK_DEST(sctp->sctp_cxmit_list, fp); sctp_set_iplen(sctp, ipmp, fp->ixa); (void) conn_ip_output(ipmp, fp->ixa); BUMP_LOCAL(sctp->sctp_opkts); SCTP_FADDR_RC_TIMER_RESTART(sctp, fp, fp->rto); #undef SCTP_SET_SENT_FLAG }
void sctp_input_asconf(sctp_t *sctp, sctp_chunk_hdr_t *ch, sctp_faddr_t *fp) { const dispatch_t *dp; mblk_t *hmp; mblk_t *mp; uint32_t *idp; uint32_t *hidp; ssize_t rlen; sctp_parm_hdr_t *ph; sctp_chunk_hdr_t *ach; int cont; int act; uint16_t plen; uchar_t *alist = NULL; size_t asize = 0; uchar_t *dlist = NULL; size_t dsize = 0; uchar_t *aptr = NULL; uchar_t *dptr = NULL; int acount = 0; int dcount = 0; sctp_stack_t *sctps = sctp->sctp_sctps; ASSERT(ch->sch_id == CHUNK_ASCONF); idp = (uint32_t *)(ch + 1); rlen = ntohs(ch->sch_len) - sizeof (*ch) - sizeof (*idp); if (rlen < 0 || rlen < sizeof (*idp)) { /* nothing there; bail out */ return; } /* Check for duplicates */ *idp = ntohl(*idp); if (*idp == (sctp->sctp_fcsn + 1)) { act = 1; } else if (*idp == sctp->sctp_fcsn) { act = 0; } else { /* stale or malicious packet; drop */ return; } /* Create the ASCONF_ACK header */ hmp = sctp_make_mp(sctp, fp, sizeof (*ach) + sizeof (*idp)); if (hmp == NULL) { /* Let the peer retransmit */ SCTP_KSTAT(sctps, sctp_send_asconf_ack_failed); return; } ach = (sctp_chunk_hdr_t *)hmp->b_wptr; ach->sch_id = CHUNK_ASCONF_ACK; ach->sch_flags = 0; /* Set the length later */ hidp = (uint32_t *)(ach + 1); *hidp = htonl(*idp); hmp->b_wptr = (uchar_t *)(hidp + 1); /* Move to the Address Parameter */ ph = (sctp_parm_hdr_t *)(idp + 1); if (rlen <= ntohs(ph->sph_len)) { freeb(hmp); return; } /* * We already have the association here, so this address parameter * doesn't seem to be very useful, should we make sure this is part * of the association and send an error, if not? * Ignore it for now. */ rlen -= ntohs(ph->sph_len); ph = (sctp_parm_hdr_t *)((char *)ph + ntohs(ph->sph_len)); /* * We need to pre-allocate buffer before processing the ASCONF * chunk. We don't want to fail allocating buffers after processing * the ASCONF chunk. So, we walk the list and get the number of * addresses added and/or deleted. */ if (cl_sctp_assoc_change != NULL) { sctp_parm_hdr_t *oph = ph; ssize_t orlen = rlen; /* * This not very efficient, but there is no better way of * doing it. It should be fine since normally the param list * will not be very long. */ while (orlen > 0) { /* Sanity checks */ if (orlen < sizeof (*oph)) break; plen = ntohs(oph->sph_len); if (plen < sizeof (*oph) || plen > orlen) break; if (oph->sph_type == htons(PARM_ADD_IP)) acount++; if (oph->sph_type == htons(PARM_DEL_IP)) dcount++; oph = sctp_next_parm(oph, &orlen); if (oph == NULL) break; } if (acount > 0 || dcount > 0) { if (acount > 0) { asize = sizeof (in6_addr_t) * acount; alist = kmem_alloc(asize, KM_NOSLEEP); if (alist == NULL) { freeb(hmp); SCTP_KSTAT(sctps, sctp_cl_assoc_change); return; } } if (dcount > 0) { dsize = sizeof (in6_addr_t) * dcount; dlist = kmem_alloc(dsize, KM_NOSLEEP); if (dlist == NULL) { if (acount > 0) kmem_free(alist, asize); freeb(hmp); SCTP_KSTAT(sctps, sctp_cl_assoc_change); return; } } aptr = alist; dptr = dlist; /* * We will get the actual count when we process * the chunk. */ acount = 0; dcount = 0; } } cont = 1; while (rlen > 0 && cont) { in6_addr_t addr; /* Sanity checks */ if (rlen < sizeof (*ph)) break; plen = ntohs(ph->sph_len); if (plen < sizeof (*ph) || plen > rlen) { break; } idp = (uint32_t *)(ph + 1); dp = sctp_lookup_asconf_dispatch(ntohs(ph->sph_type)); ASSERT(dp); if (dp->asconf) { mp = dp->asconf(sctp, ph, *idp, fp, &cont, act, &addr); if (cont == -1) { /* * Not even enough memory to create * an out-of-resources error. Free * everything and return; the peer * should retransmit. */ freemsg(hmp); if (alist != NULL) kmem_free(alist, asize); if (dlist != NULL) kmem_free(dlist, dsize); return; } if (mp != NULL) { linkb(hmp, mp); } else if (act != 0) { /* update the add/delete list */ if (cl_sctp_assoc_change != NULL) { if (ph->sph_type == htons(PARM_ADD_IP)) { ASSERT(alist != NULL); bcopy(&addr, aptr, sizeof (addr)); aptr += sizeof (addr); acount++; } else if (ph->sph_type == htons(PARM_DEL_IP)) { ASSERT(dlist != NULL); bcopy(&addr, dptr, sizeof (addr)); dptr += sizeof (addr); dcount++; } } } } ph = sctp_next_parm(ph, &rlen); if (ph == NULL) break; } /* * Update clustering's state for this assoc. Note acount/dcount * could be zero (i.e. if the add/delete address(es) were not * processed successfully). Regardless, if the ?size is > 0, * it is the clustering module's responsibility to free the lists. */ if (cl_sctp_assoc_change != NULL) { (*cl_sctp_assoc_change)(sctp->sctp_connp->conn_family, alist, asize, acount, dlist, dsize, dcount, SCTP_CL_PADDR, (cl_sctp_handle_t)sctp); /* alist and dlist will be freed by the clustering module */ } /* Now that the params have been processed, increment the fcsn */ if (act) { sctp->sctp_fcsn++; } BUMP_LOCAL(sctp->sctp_obchunks); if (fp->isv4) ach->sch_len = htons(msgdsize(hmp) - sctp->sctp_hdr_len); else ach->sch_len = htons(msgdsize(hmp) - sctp->sctp_hdr6_len); sctp_set_iplen(sctp, hmp, fp->ixa); (void) conn_ip_output(hmp, fp->ixa); BUMP_LOCAL(sctp->sctp_opkts); sctp_validate_peer(sctp); }
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); }
void sctp_user_abort(sctp_t *sctp, mblk_t *data) { mblk_t *mp; int len, hdrlen; char *cause; sctp_faddr_t *fp = sctp->sctp_current; ip_xmit_attr_t *ixa = fp->sf_ixa; sctp_stack_t *sctps = sctp->sctp_sctps; /* * Don't need notification if connection is not yet setup, * call sctp_clean_death() to reclaim resources. * Any pending connect call(s) will error out. */ if (sctp->sctp_state < SCTPS_COOKIE_WAIT) { sctp_clean_death(sctp, ECONNABORTED); return; } mp = sctp_make_mp(sctp, fp, 0); if (mp == NULL) { SCTP_KSTAT(sctps, sctp_send_user_abort_failed); return; } /* * Create abort chunk. */ if (data) { if (fp->sf_isv4) { hdrlen = sctp->sctp_hdr_len; } else { hdrlen = sctp->sctp_hdr6_len; } hdrlen += sizeof (sctp_chunk_hdr_t) + sizeof (sctp_parm_hdr_t); cause = (char *)data->b_rptr; len = data->b_wptr - data->b_rptr; if (len + hdrlen > fp->sf_pmss) { len = fp->sf_pmss - hdrlen; } } else { cause = NULL; len = 0; } /* * Since it is a user abort, we should have the sctp_t and hence * the correct verification tag. So we should not set the T-bit * in the ABORT. */ if ((len = sctp_link_abort(mp, SCTP_ERR_USER_ABORT, cause, len, 0, B_FALSE)) < 0) { freemsg(mp); return; } SCTPS_BUMP_MIB(sctps, sctpAborted); BUMP_LOCAL(sctp->sctp_opkts); BUMP_LOCAL(sctp->sctp_obchunks); sctp_set_iplen(sctp, mp, ixa); ASSERT(ixa->ixa_ire != NULL); ASSERT(ixa->ixa_cred != NULL); (void) conn_ip_output(mp, ixa); sctp_assoc_event(sctp, SCTP_COMM_LOST, 0, NULL); sctp_clean_death(sctp, ECONNABORTED); }