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 }
/* * Returns 0 if there is at leave one other active faddr, -1 if there * are none. If there are none left, faddr_dead() will start killing the * association. * If the downed faddr was the current faddr, a new current faddr * will be chosen. */ int sctp_faddr_dead(sctp_t *sctp, sctp_faddr_t *fp, int newstate) { sctp_faddr_t *ofp; sctp_stack_t *sctps = sctp->sctp_sctps; if (fp->state == SCTP_FADDRS_ALIVE) { sctp_intf_event(sctp, fp->faddr, SCTP_ADDR_UNREACHABLE, 0); } fp->state = newstate; dprint(1, ("sctp_faddr_dead: %x:%x:%x:%x down (state=%d)\n", SCTP_PRINTADDR(fp->faddr), newstate)); if (fp == sctp->sctp_current) { /* Current faddr down; need to switch it */ sctp->sctp_current = NULL; } /* Find next alive faddr */ ofp = fp; for (fp = fp->next; fp != NULL; fp = fp->next) { if (fp->state == SCTP_FADDRS_ALIVE) { break; } } if (fp == NULL) { /* Continue from beginning of list */ for (fp = sctp->sctp_faddrs; fp != ofp; fp = fp->next) { if (fp->state == SCTP_FADDRS_ALIVE) { break; } } } /* * Find a new fp, so if the current faddr is dead, use the new fp * as the current one. */ if (fp != ofp) { if (sctp->sctp_current == NULL) { dprint(1, ("sctp_faddr_dead: failover->%x:%x:%x:%x\n", SCTP_PRINTADDR(fp->faddr))); /* * Note that we don't need to reset the source addr * of the new fp. */ sctp_set_faddr_current(sctp, fp); } return (0); } /* All faddrs are down; kill the association */ dprint(1, ("sctp_faddr_dead: all faddrs down, killing assoc\n")); BUMP_MIB(&sctps->sctps_mib, sctpAborted); sctp_assoc_event(sctp, sctp->sctp_state < SCTPS_ESTABLISHED ? SCTP_CANT_STR_ASSOC : SCTP_COMM_LOST, 0, NULL); sctp_clean_death(sctp, sctp->sctp_client_errno ? sctp->sctp_client_errno : ETIMEDOUT); return (-1); }
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); }