* Fix a bug where PR-SCTP settings are ignore when using implicit

association setup.
* Fix a bug where message with illegal stream ids are not deleted.
* Fix a crash when reporting back unsent messages from the send_queue.
* Fix a bug related to INIT retransmission when the socket is already
  closed.
* Fix a bug where associations were stalled when partial delivery API
  was enabled.
* Fix a bug where the receive buffer size was smaller than the
  partial_delivery_point.

Approved by: re, rrs (mentor)
MFC after: One day.
This commit is contained in:
Michael Tuexen 2009-08-15 21:10:52 +00:00
parent 68a5590836
commit 810ec53688
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=196260
7 changed files with 176 additions and 99 deletions

View File

@ -900,7 +900,7 @@ sctp_deliver_reasm_check(struct sctp_tcb *stcb, struct sctp_association *asoc)
{
struct sctp_tmit_chunk *chk;
uint16_t nxt_todel;
uint32_t tsize;
uint32_t tsize, pd_point;
doit_again:
chk = TAILQ_FIRST(&asoc->reasmqueue);
@ -920,8 +920,13 @@ sctp_deliver_reasm_check(struct sctp_tcb *stcb, struct sctp_association *asoc)
* Yep the first one is here and its ok to deliver
* but should we?
*/
if ((sctp_is_all_msg_on_reasm(asoc, &tsize) ||
(tsize >= stcb->sctp_ep->partial_delivery_point))) {
if (stcb->sctp_socket) {
pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket) >> SCTP_PARTIAL_DELIVERY_SHIFT,
stcb->sctp_ep->partial_delivery_point);
} else {
pd_point = stcb->sctp_ep->partial_delivery_point;
}
if (sctp_is_all_msg_on_reasm(asoc, &tsize) || (tsize >= pd_point)) {
/*
* Yes, we setup to start reception, by
@ -2824,7 +2829,7 @@ void
sctp_service_queues(struct sctp_tcb *stcb, struct sctp_association *asoc)
{
struct sctp_tmit_chunk *chk;
uint32_t tsize;
uint32_t tsize, pd_point;
uint16_t nxt_todel;
if (asoc->fragmented_delivery_inprogress) {
@ -2860,8 +2865,13 @@ sctp_service_queues(struct sctp_tcb *stcb, struct sctp_association *asoc)
* be here or 1/4 the socket buffer max or nothing on the
* delivery queue and something can be delivered.
*/
if ((sctp_is_all_msg_on_reasm(asoc, &tsize) ||
(tsize >= stcb->sctp_ep->partial_delivery_point))) {
if (stcb->sctp_socket) {
pd_point = min(SCTP_SB_LIMIT_RCV(stcb->sctp_socket) >> SCTP_PARTIAL_DELIVERY_SHIFT,
stcb->sctp_ep->partial_delivery_point);
} else {
pd_point = stcb->sctp_ep->partial_delivery_point;
}
if (sctp_is_all_msg_on_reasm(asoc, &tsize) || (tsize >= pd_point)) {
asoc->fragmented_delivery_inprogress = 1;
asoc->tsn_last_delivered = chk->rec.data.TSN_seq - 1;
asoc->str_of_pdapi = chk->rec.data.stream_number;
@ -5192,7 +5202,7 @@ sctp_handle_sack(struct mbuf *m, int offset,
/* sa_ignore NO_NULL_CHK */
sctp_free_bufspace(stcb, asoc, tp1, 1);
sctp_m_freem(tp1->data);
if (PR_SCTP_BUF_ENABLED(tp1->flags)) {
if (asoc->peer_supports_prsctp && PR_SCTP_BUF_ENABLED(tp1->flags)) {
asoc->sent_queue_cnt_removeable--;
}
}
@ -6289,10 +6299,11 @@ sctp_handle_forward_tsn(struct sctp_tcb *stcb,
ctl->pdapi_aborted = 1;
sv = stcb->asoc.control_pdapi;
stcb->asoc.control_pdapi = ctl;
sctp_notify_partial_delivery_indication(stcb,
sctp_ulp_notify(SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION,
stcb,
SCTP_PARTIAL_DELIVERY_ABORTED,
SCTP_HOLDS_LOCK,
str_seq);
(void *)&str_seq,
SCTP_SO_NOT_LOCKED);
stcb->asoc.control_pdapi = sv;
break;
} else if ((ctl->sinfo_stream == stseq->stream) &&
@ -7786,7 +7797,7 @@ sctp_handle_nr_sack(struct mbuf *m, int offset,
/* sa_ignore NO_NULL_CHK */
sctp_free_bufspace(stcb, asoc, tp1, 1);
sctp_m_freem(tp1->data);
if (PR_SCTP_BUF_ENABLED(tp1->flags)) {
if (asoc->peer_supports_prsctp && PR_SCTP_BUF_ENABLED(tp1->flags)) {
asoc->sent_queue_cnt_removeable--;
}
}

View File

@ -278,18 +278,38 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
unsigned int newcnt;
struct sctp_stream_out *outs;
struct sctp_stream_queue_pending *sp;
struct sctp_tmit_chunk *chk, *chk_next;
/* cut back on number of streams */
/* abandon the upper streams */
newcnt = ntohs(init->num_inbound_streams);
/* This if is probably not needed but I am cautious */
if (!TAILQ_EMPTY(&asoc->send_queue)) {
chk = TAILQ_FIRST(&asoc->send_queue);
while (chk) {
chk_next = TAILQ_NEXT(chk, sctp_next);
if (chk->rec.data.stream_number >= newcnt) {
TAILQ_REMOVE(&asoc->send_queue, chk, sctp_next);
asoc->send_queue_cnt--;
if (chk->data != NULL) {
sctp_free_bufspace(stcb, asoc, chk, 1);
sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
SCTP_NOTIFY_DATAGRAM_UNSENT, chk, SCTP_SO_NOT_LOCKED);
if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
}
}
sctp_free_a_chunk(stcb, chk);
/* sa_ignore FREED_MEMORY */
}
chk = chk_next;
}
}
if (asoc->strmout) {
/* First make sure no data chunks are trapped */
for (i = newcnt; i < asoc->pre_open_streams; i++) {
outs = &asoc->strmout[i];
sp = TAILQ_FIRST(&outs->outqueue);
while (sp) {
TAILQ_REMOVE(&outs->outqueue, sp,
next);
TAILQ_REMOVE(&outs->outqueue, sp, next);
asoc->stream_queue_cnt--;
sctp_ulp_notify(SCTP_NOTIFY_SPECIAL_SP_FAIL,
stcb, SCTP_NOTIFY_DATAGRAM_UNSENT,
@ -301,16 +321,13 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb,
sctp_free_remote_addr(sp->net);
sp->net = NULL;
/* Free the chunk */
SCTP_PRINTF("sp:%p tcb:%p weird free case\n",
sp, stcb);
sctp_free_a_strmoq(stcb, sp);
/* sa_ignore FREED_MEMORY */
sp = TAILQ_FIRST(&outs->outqueue);
}
}
}
/* cut back the count and abandon the upper streams */
/* cut back the count */
asoc->pre_open_streams = newcnt;
}
SCTP_TCB_SEND_UNLOCK(stcb);

View File

@ -4200,7 +4200,7 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked
/* place in my tag */
init->init.initiate_tag = htonl(stcb->asoc.my_vtag);
/* set up some of the credits. */
init->init.a_rwnd = htonl(max(SCTP_SB_LIMIT_RCV(inp->sctp_socket),
init->init.a_rwnd = htonl(max(inp->sctp_socket ? SCTP_SB_LIMIT_RCV(inp->sctp_socket) : 0,
SCTP_MINIMAL_RWND));
init->init.num_outbound_streams = htons(stcb->asoc.pre_open_streams);
@ -4411,7 +4411,6 @@ sctp_send_initiate(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int so_locked
net->port, so_locked, NULL);
SCTPDBG(SCTP_DEBUG_OUTPUT4, "lowlevel_output - %d\n", ret);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
(void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time);
}
@ -5786,61 +5785,58 @@ sctp_get_frag_point(struct sctp_tcb *stcb,
}
static void
sctp_set_prsctp_policy(struct sctp_tcb *stcb,
struct sctp_stream_queue_pending *sp)
sctp_set_prsctp_policy(struct sctp_stream_queue_pending *sp)
{
sp->pr_sctp_on = 0;
if (stcb->asoc.peer_supports_prsctp) {
/*
* We assume that the user wants PR_SCTP_TTL if the user provides a
* positive lifetime but does not specify any PR_SCTP policy. This
* is a BAD assumption and causes problems at least with the
* U-Vancovers MPI folks. I will change this to be no policy means
* NO PR-SCTP.
*/
if (PR_SCTP_ENABLED(sp->sinfo_flags)) {
sp->act_flags |= PR_SCTP_POLICY(sp->sinfo_flags);
sp->pr_sctp_on = 1;
} else {
return;
}
switch (PR_SCTP_POLICY(sp->sinfo_flags)) {
case CHUNK_FLAGS_PR_SCTP_BUF:
/*
* We assume that the user wants PR_SCTP_TTL if the user
* provides a positive lifetime but does not specify any
* PR_SCTP policy. This is a BAD assumption and causes
* problems at least with the U-Vancovers MPI folks. I will
* change this to be no policy means NO PR-SCTP.
* Time to live is a priority stored in tv_sec when doing
* the buffer drop thing.
*/
if (PR_SCTP_ENABLED(sp->sinfo_flags)) {
sp->act_flags |= PR_SCTP_POLICY(sp->sinfo_flags);
sp->pr_sctp_on = 1;
} else {
return;
}
switch (PR_SCTP_POLICY(sp->sinfo_flags)) {
case CHUNK_FLAGS_PR_SCTP_BUF:
/*
* Time to live is a priority stored in tv_sec when
* doing the buffer drop thing.
*/
sp->ts.tv_sec = sp->timetolive;
sp->ts.tv_usec = 0;
break;
case CHUNK_FLAGS_PR_SCTP_TTL:
{
struct timeval tv;
sp->ts.tv_sec = sp->timetolive;
sp->ts.tv_usec = 0;
break;
case CHUNK_FLAGS_PR_SCTP_TTL:
{
struct timeval tv;
(void)SCTP_GETTIME_TIMEVAL(&sp->ts);
tv.tv_sec = sp->timetolive / 1000;
tv.tv_usec = (sp->timetolive * 1000) % 1000000;
/*
* TODO sctp_constants.h needs alternative
* time macros when _KERNEL is undefined.
*/
timevaladd(&sp->ts, &tv);
}
break;
case CHUNK_FLAGS_PR_SCTP_RTX:
(void)SCTP_GETTIME_TIMEVAL(&sp->ts);
tv.tv_sec = sp->timetolive / 1000;
tv.tv_usec = (sp->timetolive * 1000) % 1000000;
/*
* Time to live is a the number or retransmissions
* stored in tv_sec.
* TODO sctp_constants.h needs alternative time
* macros when _KERNEL is undefined.
*/
sp->ts.tv_sec = sp->timetolive;
sp->ts.tv_usec = 0;
break;
default:
SCTPDBG(SCTP_DEBUG_USRREQ1,
"Unknown PR_SCTP policy %u.\n",
PR_SCTP_POLICY(sp->sinfo_flags));
break;
timevaladd(&sp->ts, &tv);
}
break;
case CHUNK_FLAGS_PR_SCTP_RTX:
/*
* Time to live is a the number or retransmissions stored in
* tv_sec.
*/
sp->ts.tv_sec = sp->timetolive;
sp->ts.tv_usec = 0;
break;
default:
SCTPDBG(SCTP_DEBUG_USRREQ1,
"Unknown PR_SCTP policy %u.\n",
PR_SCTP_POLICY(sp->sinfo_flags));
break;
}
}
@ -5911,7 +5907,7 @@ sctp_msg_append(struct sctp_tcb *stcb,
sp->tail_mbuf = NULL;
sp->length = 0;
at = m;
sctp_set_prsctp_policy(stcb, sp);
sctp_set_prsctp_policy(sp);
/*
* We could in theory (for sendall) sifa the length in, but we would
* still have to hunt through the chain since we need to setup the
@ -7138,7 +7134,7 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net,
}
/* We only re-set the policy if it is on */
if (sp->pr_sctp_on) {
sctp_set_prsctp_policy(stcb, sp);
sctp_set_prsctp_policy(sp);
asoc->pr_sctp_cnt++;
chk->pr_sctp_on = 1;
} else {
@ -12285,7 +12281,7 @@ sctp_copy_it_in(struct sctp_tcb *stcb,
sp->addr_over = 0;
}
atomic_add_int(&sp->net->ref_count, 1);
sctp_set_prsctp_policy(stcb, sp);
sctp_set_prsctp_policy(sp);
}
out_now:
return (sp);

View File

@ -4547,8 +4547,11 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
stcb->asoc.control_pdapi = sq;
strseq = (sq->sinfo_stream << 16) | sq->sinfo_ssn;
sctp_notify_partial_delivery_indication(stcb,
SCTP_PARTIAL_DELIVERY_ABORTED, 1, strseq);
sctp_ulp_notify(SCTP_NOTIFY_PARTIAL_DELVIERY_INDICATION,
stcb,
SCTP_PARTIAL_DELIVERY_ABORTED,
(void *)&strseq,
SCTP_SO_LOCKED);
stcb->asoc.control_pdapi = NULL;
}
}

View File

@ -588,7 +588,7 @@ sctp_recover_sent_list(struct sctp_tcb *stcb)
/* sa_ignore NO_NULL_CHK */
sctp_free_bufspace(stcb, asoc, chk, 1);
sctp_m_freem(chk->data);
if (PR_SCTP_BUF_ENABLED(chk->flags)) {
if (asoc->peer_supports_prsctp && PR_SCTP_BUF_ENABLED(chk->flags)) {
asoc->sent_queue_cnt_removeable--;
}
}
@ -757,7 +757,7 @@ sctp_mark_all_for_resend(struct sctp_tcb *stcb,
continue;
}
}
if (PR_SCTP_TTL_ENABLED(chk->flags)) {
if (stcb->asoc.peer_supports_prsctp && PR_SCTP_TTL_ENABLED(chk->flags)) {
/* Is it expired? */
if ((now.tv_sec > chk->rec.data.timetodrop.tv_sec) ||
((chk->rec.data.timetodrop.tv_sec == now.tv_sec) &&
@ -772,7 +772,7 @@ sctp_mark_all_for_resend(struct sctp_tcb *stcb,
continue;
}
}
if (PR_SCTP_RTX_ENABLED(chk->flags)) {
if (stcb->asoc.peer_supports_prsctp && PR_SCTP_RTX_ENABLED(chk->flags)) {
/* Has it been retransmitted tv_sec times? */
if (chk->snd_count > chk->rec.data.timetodrop.tv_sec) {
if (chk->data) {

View File

@ -1484,6 +1484,7 @@ sctp_timeout_handler(void *t)
SCTP_INP_INCR_REF(inp);
if ((inp->sctp_socket == 0) &&
((tmr->type != SCTP_TIMER_TYPE_INPKILL) &&
(tmr->type != SCTP_TIMER_TYPE_INIT) &&
(tmr->type != SCTP_TIMER_TYPE_SEND) &&
(tmr->type != SCTP_TIMER_TYPE_RECV) &&
(tmr->type != SCTP_TIMER_TYPE_HEARTBEAT) &&
@ -2984,8 +2985,6 @@ sctp_notify_send_failed(struct sctp_tcb *stcb, uint32_t error,
ssf->ssf_info.sinfo_assoc_id = sctp_get_associd(stcb);
ssf->ssf_assoc_id = sctp_get_associd(stcb);
SCTP_BUF_NEXT(m_notify) = chk->data;
SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_send_failed);
if (chk->data) {
/*
* trim off the sctp chunk header(it should be there)
@ -2996,6 +2995,8 @@ sctp_notify_send_failed(struct sctp_tcb *stcb, uint32_t error,
chk->send_size -= sizeof(struct sctp_data_chunk);
}
}
SCTP_BUF_NEXT(m_notify) = chk->data;
SCTP_BUF_LEN(m_notify) = sizeof(struct sctp_send_failed);
/* Steal off the mbuf */
chk->data = NULL;
/*
@ -3146,9 +3147,13 @@ sctp_notify_adaptation_layer(struct sctp_tcb *stcb,
}
/* This always must be called with the read-queue LOCKED in the INP */
void
static void
sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb, uint32_t error,
int nolock, uint32_t val)
uint32_t val, int so_locked
#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
SCTP_UNUSED
#endif
)
{
struct mbuf *m_notify;
struct sctp_pdapi_event *pdapi;
@ -3189,9 +3194,6 @@ sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb, uint32_t error,
control->tail_mbuf = m_notify;
control->held_length = 0;
control->length = 0;
if (nolock == 0) {
SCTP_INP_READ_LOCK(stcb->sctp_ep);
}
sb = &stcb->sctp_socket->so_rcv;
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_SB_LOGGING_ENABLE) {
sctp_sblog(sb, control->do_not_ref_stcb ? NULL : stcb, SCTP_LOG_SBALLOC, SCTP_BUF_LEN(m_notify));
@ -3208,12 +3210,30 @@ sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb, uint32_t error,
/* we really should not see this case */
TAILQ_INSERT_TAIL(&stcb->sctp_ep->read_queue, control, next);
}
if (nolock == 0) {
SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
}
if (stcb->sctp_ep && stcb->sctp_socket) {
/* This should always be the case */
#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
struct socket *so;
so = SCTP_INP_SO(stcb->sctp_ep);
if (!so_locked) {
atomic_add_int(&stcb->asoc.refcnt, 1);
SCTP_TCB_UNLOCK(stcb);
SCTP_SOCKET_LOCK(so, 1);
SCTP_TCB_LOCK(stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1);
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) {
SCTP_SOCKET_UNLOCK(so, 1);
return;
}
}
#endif
sctp_sorwakeup(stcb->sctp_ep, stcb->sctp_socket);
#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
if (!so_locked) {
SCTP_SOCKET_UNLOCK(so, 1);
}
#endif
}
}
@ -3540,9 +3560,9 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb,
val = *((uint32_t *) data);
sctp_notify_partial_delivery_indication(stcb, error, 0, val);
sctp_notify_partial_delivery_indication(stcb, error, val, so_locked);
break;
}
break;
case SCTP_NOTIFY_STRDATA_ERR:
break;
case SCTP_NOTIFY_ASSOC_ABORTED:
@ -3585,11 +3605,9 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb,
case SCTP_NOTIFY_STR_RESET_FAILED_OUT:
sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), (SCTP_STRRESET_OUTBOUND_STR | SCTP_STRRESET_FAILED));
break;
case SCTP_NOTIFY_STR_RESET_FAILED_IN:
sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), (SCTP_STRRESET_INBOUND_STR | SCTP_STRRESET_FAILED));
break;
case SCTP_NOTIFY_ASCONF_ADD_IP:
sctp_notify_peer_addr_change(stcb, SCTP_ADDR_ADDED, data,
error);
@ -3671,8 +3689,10 @@ sctp_report_all_outbound(struct sctp_tcb *stcb, int holds_lock, int so_locked
sctp_free_bufspace(stcb, asoc, chk, 1);
sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
SCTP_NOTIFY_DATAGRAM_SENT, chk, so_locked);
sctp_m_freem(chk->data);
chk->data = NULL;
if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
}
}
sctp_free_a_chunk(stcb, chk);
/* sa_ignore FREED_MEMORY */
@ -3689,8 +3709,10 @@ sctp_report_all_outbound(struct sctp_tcb *stcb, int holds_lock, int so_locked
sctp_free_bufspace(stcb, asoc, chk, 1);
sctp_ulp_notify(SCTP_NOTIFY_DG_FAIL, stcb,
SCTP_NOTIFY_DATAGRAM_UNSENT, chk, so_locked);
sctp_m_freem(chk->data);
chk->data = NULL;
if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
}
}
sctp_free_a_chunk(stcb, chk);
/* sa_ignore FREED_MEMORY */
@ -5346,6 +5368,38 @@ sctp_sorecvmsg(struct socket *so,
}
goto restart;
}
if ((control->length == 0) &&
(control->end_added == 1)) {
/*
* Do we also need to check for (control->pdapi_aborted ==
* 1)?
*/
if (hold_rlock == 0) {
hold_rlock = 1;
SCTP_INP_READ_LOCK(inp);
}
TAILQ_REMOVE(&inp->read_queue, control, next);
if (control->data) {
#ifdef INVARIANTS
panic("control->data not null but control->length == 0");
#else
SCTP_PRINTF("Strange, data left in the control buffer. Cleaning up.\n");
sctp_m_freem(control->data);
control->data = NULL;
#endif
}
if (control->aux_data) {
sctp_m_free(control->aux_data);
control->aux_data = NULL;
}
sctp_free_remote_addr(control->whoFrom);
sctp_free_a_readq(stcb, control);
if (hold_rlock) {
hold_rlock = 0;
SCTP_INP_READ_UNLOCK(inp);
}
goto restart;
}
if (control->length == 0) {
if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_FRAG_INTERLEAVE)) &&
(filling_sinfo)) {

View File

@ -234,10 +234,6 @@ int sctp_cmpaddr(struct sockaddr *, struct sockaddr *);
void sctp_print_address(struct sockaddr *);
void sctp_print_address_pkt(struct ip *, struct sctphdr *);
void
sctp_notify_partial_delivery_indication(struct sctp_tcb *stcb,
uint32_t error, int no_lock, uint32_t strseq);
int
sctp_release_pr_sctp_chunk(struct sctp_tcb *, struct sctp_tmit_chunk *,
int, int