/* * 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; }
static void sctp_rc_timer(sctp_t *sctp, sctp_faddr_t *fp) { #define SCTP_CLR_SENT_FLAG(mp) ((mp)->b_flag &= ~SCTP_CHUNK_FLAG_SENT) sctp_faddr_t *nfp; sctp_faddr_t *ofp; sctp_stack_t *sctps = sctp->sctp_sctps; ASSERT(fp != NULL); fp->rc_timer_running = 0; if (sctp->sctp_state != SCTPS_ESTABLISHED || sctp->sctp_cxmit_list == NULL) { return; } /* * Not a retransmission, this was deferred due to some error * condition */ if (!SCTP_CHUNK_ISSENT(sctp->sctp_cxmit_list)) { sctp_wput_asconf(sctp, fp); return; } /* * The sent flag indicates if the msg has been sent on this fp. */ SCTP_CLR_SENT_FLAG(sctp->sctp_cxmit_list); /* Retransmission */ if (sctp->sctp_strikes >= sctp->sctp_pa_max_rxt) { /* time to give up */ BUMP_MIB(&sctps->sctps_mib, sctpAborted); sctp_assoc_event(sctp, SCTP_COMM_LOST, 0, NULL); sctp_clean_death(sctp, ETIMEDOUT); return; } if (fp->strikes >= fp->max_retr) { if (sctp_faddr_dead(sctp, fp, SCTP_FADDRS_DOWN) == -1) return; } fp->strikes++; sctp->sctp_strikes++; SCTP_CALC_RXT(sctp, fp); nfp = sctp_rotate_faddr(sctp, fp); sctp->sctp_cchunk_pend = 0; ofp = SCTP_CHUNK_DEST(sctp->sctp_cxmit_list); SCTP_SET_CHUNK_DEST(sctp->sctp_cxmit_list, NULL); ASSERT(ofp != NULL && ofp == fp); ASSERT(ofp->suna >= MBLKL(sctp->sctp_cxmit_list)); /* * Enter slow start for this destination. * XXX anything in the data path that needs to be considered? */ ofp->ssthresh = ofp->cwnd / 2; if (ofp->ssthresh < 2 * ofp->sfa_pmss) ofp->ssthresh = 2 * ofp->sfa_pmss; ofp->cwnd = ofp->sfa_pmss; ofp->pba = 0; ofp->suna -= MBLKL(sctp->sctp_cxmit_list); /* * The rexmit flags is used to determine if a serial number needs to * be assigned or not, so once set we leave it there. */ if (!SCTP_CHUNK_WANT_REXMIT(sctp->sctp_cxmit_list)) SCTP_CHUNK_REXMIT(sctp->sctp_cxmit_list); sctp_wput_asconf(sctp, nfp); #undef SCTP_CLR_SENT_FLAG }
void sctp_input_asconf_ack(sctp_t *sctp, sctp_chunk_hdr_t *ch, sctp_faddr_t *fp) { const dispatch_t *dp; uint32_t *idp; uint32_t *snp; ssize_t rlen; ssize_t plen; sctp_parm_hdr_t *ph; sctp_parm_hdr_t *oph; sctp_parm_hdr_t *fph; mblk_t *mp; sctp_chunk_hdr_t *och; int redosrcs = 0; uint16_t param_len; uchar_t *alist; uchar_t *dlist; uint_t acount = 0; uint_t dcount = 0; uchar_t *aptr; uchar_t *dptr; sctp_cl_ainfo_t *ainfo; in6_addr_t addr; ASSERT(ch->sch_id == CHUNK_ASCONF_ACK); snp = (uint32_t *)(ch + 1); rlen = ntohs(ch->sch_len) - sizeof (*ch) - sizeof (*snp); if (rlen < 0) { return; } /* Accept only an ACK for the current serial number */ *snp = ntohl(*snp); if (sctp->sctp_cxmit_list == NULL || *snp != (sctp->sctp_lcsn - 1)) { /* Need to send an abort */ return; } sctp->sctp_cchunk_pend = 0; SCTP_FADDR_RC_TIMER_STOP(fp); mp = sctp->sctp_cxmit_list; /* * We fill in the addresses here to update the clustering's state for * this assoc. */ if (mp != NULL && cl_sctp_assoc_change != NULL) { ASSERT(mp->b_prev != NULL); ainfo = (sctp_cl_ainfo_t *)mp->b_prev; alist = ainfo->sctp_cl_alist; dlist = ainfo->sctp_cl_dlist; aptr = alist; dptr = dlist; } /* * Pass explicit replies to callbacks: * For each reply in the ACK, look up the corresponding * original parameter in the request using the correlation * ID, and pass it to the right callback. */ och = (sctp_chunk_hdr_t *)sctp->sctp_cxmit_list->b_rptr; plen = ntohs(och->sch_len) - sizeof (*och) - sizeof (*idp); idp = (uint32_t *)(och + 1); /* Get to the 1st ASCONF param, need to skip Address TLV parm */ fph = (sctp_parm_hdr_t *)(idp + 1); plen -= ntohs(fph->sph_len); fph = (sctp_parm_hdr_t *)((char *)fph + ntohs(fph->sph_len)); ph = (sctp_parm_hdr_t *)(snp + 1); while (rlen > 0) { /* Sanity checks */ if (rlen < sizeof (*ph)) { break; } param_len = ntohs(ph->sph_len); if (param_len < sizeof (*ph) || param_len > rlen) { break; } idp = (uint32_t *)(ph + 1); oph = sctp_lookup_asconf_param(fph, *idp, plen); if (oph != NULL) { dp = sctp_lookup_asconf_dispatch(ntohs(oph->sph_type)); ASSERT(dp); if (dp->asconf_ack) { dp->asconf_ack(sctp, ph, oph, fp, &addr); /* hack. see below */ if (oph->sph_type == htons(PARM_ADD_IP) || oph->sph_type == htons(PARM_DEL_IP)) { redosrcs = 1; /* * If the address was sucessfully * processed, add it to the add/delete * list to send to the clustering * module. */ if (cl_sctp_assoc_change != NULL && !SCTP_IS_ADDR_UNSPEC( IN6_IS_ADDR_V4MAPPED(&addr), addr)) { if (oph->sph_type == htons(PARM_ADD_IP)) { bcopy(&addr, aptr, sizeof (addr)); aptr += sizeof (addr); acount++; } else { bcopy(&addr, dptr, sizeof (addr)); dptr += sizeof (addr); dcount++; } } } } } ph = sctp_next_parm(ph, &rlen); if (ph == NULL) break; } /* * Pass implicit replies to callbacks: * For each original request, look up its parameter * in the ACK. If there is no corresponding reply, * call the callback with a NULL parameter, indicating * success. */ rlen = plen; plen = ntohs(ch->sch_len) - sizeof (*ch) - sizeof (*idp); oph = fph; fph = (sctp_parm_hdr_t *)((char *)ch + sizeof (sctp_chunk_hdr_t) + sizeof (uint32_t)); while (rlen > 0) { idp = (uint32_t *)(oph + 1); ph = sctp_lookup_asconf_param(fph, *idp, plen); if (ph == NULL) { dp = sctp_lookup_asconf_dispatch(ntohs(oph->sph_type)); ASSERT(dp); if (dp->asconf_ack) { dp->asconf_ack(sctp, NULL, oph, fp, &addr); /* hack. see below */ if (oph->sph_type == htons(PARM_ADD_IP) || oph->sph_type == htons(PARM_DEL_IP)) { redosrcs = 1; /* * If the address was sucessfully * processed, add it to the add/delete * list to send to the clustering * module. */ if (cl_sctp_assoc_change != NULL && !SCTP_IS_ADDR_UNSPEC( IN6_IS_ADDR_V4MAPPED(&addr), addr)) { if (oph->sph_type == htons(PARM_ADD_IP)) { bcopy(&addr, aptr, sizeof (addr)); aptr += sizeof (addr); acount++; } else { bcopy(&addr, dptr, sizeof (addr)); dptr += sizeof (addr); dcount++; } } } } } oph = sctp_next_parm(oph, &rlen); if (oph == NULL) { break; } } /* We can now free up the first chunk in the cxmit list */ sctp->sctp_cxmit_list = mp->b_cont; mp->b_cont = NULL; fp = SCTP_CHUNK_DEST(mp); ASSERT(fp != NULL && fp->suna >= MBLKL(mp)); fp->suna -= MBLKL(mp); /* * Update clustering's state for this assoc. Note acount/dcount * could be zero (i.e. if the add/delete address(es) did not * succeed). Regardless, if the ?size is > 0, it is the clustering * module's responsibility to free the lists. */ if (cl_sctp_assoc_change != NULL) { ASSERT(mp->b_prev != NULL); mp->b_prev = NULL; ainfo->sctp_cl_alist = NULL; ainfo->sctp_cl_dlist = NULL; (*cl_sctp_assoc_change)(sctp->sctp_connp->conn_family, alist, ainfo->sctp_cl_asize, acount, dlist, ainfo->sctp_cl_dsize, dcount, SCTP_CL_LADDR, (cl_sctp_handle_t)sctp); /* alist and dlist will be freed by the clustering module */ ainfo->sctp_cl_asize = 0; ainfo->sctp_cl_dsize = 0; kmem_free(ainfo, sizeof (*ainfo)); } freeb(mp); /* can now send the next control chunk */ if (sctp->sctp_cxmit_list != NULL) sctp_wput_asconf(sctp, NULL); /* * If an add-ip or del-ip has completed (successfully or * unsuccessfully), the pool of available source addresses * may have changed, so we need to redo faddr source * address selections. This is a bit of a hack since * this really belongs in the add/del-ip code. However, * that code consists of callbacks called for *each* * add/del-ip parameter, and sctp_redo_faddr_srcs() is * expensive enough that we really don't want to be * doing it for each one. So we do it once here. */ if (redosrcs) sctp_redo_faddr_srcs(sctp); }