Author: rrs
Date: Wed Jul 22 11:30:37 2015
New Revision: 285792
URL: https://svnweb.freebsd.org/changeset/base/285792

Log:
  Fix several problems with Stream Reset.
   1) We were not handling (or sending) the IN_PROGRESS case if
      the other side (or our side) was not able to reset (awaiting more data).
   2) We would improperly send a stream-reset when we should not. Not
      waiting until the TSN had been assigned when data was inqueue.
  
  Reviewed by:  tuexen

Modified:
  head/sys/netinet/sctp_indata.c
  head/sys/netinet/sctp_input.c
  head/sys/netinet/sctp_output.c
  head/sys/netinet/sctp_output.h
  head/sys/netinet/sctp_structs.h
  head/sys/netinet/sctp_usrreq.c
  head/sys/netinet/sctputil.c

Modified: head/sys/netinet/sctp_indata.c
==============================================================================
--- head/sys/netinet/sctp_indata.c      Wed Jul 22 10:05:46 2015        
(r285791)
+++ head/sys/netinet/sctp_indata.c      Wed Jul 22 11:30:37 2015        
(r285792)
@@ -1886,6 +1886,7 @@ finish_express_del:
 
                sctp_reset_in_stream(stcb, liste->number_entries, 
liste->list_of_streams);
                TAILQ_REMOVE(&asoc->resetHead, liste, next_resp);
+               sctp_send_deferred_reset_response(stcb, liste, 
SCTP_STREAM_RESET_RESULT_PERFORMED);
                SCTP_FREE(liste, SCTP_M_STRESET);
                /* sa_ignore FREED_MEMORY */
                liste = TAILQ_FIRST(&asoc->resetHead);

Modified: head/sys/netinet/sctp_input.c
==============================================================================
--- head/sys/netinet/sctp_input.c       Wed Jul 22 10:05:46 2015        
(r285791)
+++ head/sys/netinet/sctp_input.c       Wed Jul 22 11:30:37 2015        
(r285792)
@@ -357,14 +357,17 @@ sctp_process_init(struct sctp_init_chunk
                                        sctp_free_a_strmoq(stcb, sp, 
SCTP_SO_NOT_LOCKED);
                                        /* sa_ignore FREED_MEMORY */
                                }
+                               outs->state = SCTP_STREAM_CLOSED;
                        }
                }
                /* cut back the count */
                asoc->pre_open_streams = newcnt;
        }
        SCTP_TCB_SEND_UNLOCK(stcb);
-       asoc->strm_realoutsize = asoc->streamoutcnt = asoc->pre_open_streams;
-
+       asoc->streamoutcnt = asoc->pre_open_streams;
+       for (i = 0; i < asoc->streamoutcnt; i++) {
+               asoc->strmout[i].state = SCTP_STREAM_OPEN;
+       }
        /* EY - nr_sack: initialize highest tsn in nr_mapping_array */
        asoc->highest_tsn_inside_nr_map = asoc->highest_tsn_inside_map;
        if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) {
@@ -3506,6 +3509,28 @@ sctp_reset_out_streams(struct sctp_tcb *
        sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_SEND, stcb, number_entries, (void 
*)list, SCTP_SO_NOT_LOCKED);
 }
 
+static void
+sctp_reset_clear_pending(struct sctp_tcb *stcb, uint32_t number_entries, 
uint16_t * list)
+{
+       uint32_t i;
+       uint16_t temp;
+
+       if (number_entries > 0) {
+               for (i = 0; i < number_entries; i++) {
+                       temp = ntohs(list[i]);
+                       if (temp >= stcb->asoc.streamoutcnt) {
+                               /* no such stream */
+                               continue;
+                       }
+                       stcb->asoc.strmout[temp].state = SCTP_STREAM_OPEN;
+               }
+       } else {
+               for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+                       stcb->asoc.strmout[i].state = SCTP_STREAM_OPEN;
+               }
+       }
+}
+
 
 struct sctp_stream_reset_request *
 sctp_find_stream_reset(struct sctp_tcb *stcb, uint32_t seq, struct 
sctp_tmit_chunk **bchk)
@@ -3604,6 +3629,8 @@ sctp_handle_stream_reset_response(struct
                        type = ntohs(req_param->ph.param_type);
                        lparm_len = ntohs(req_param->ph.param_length);
                        if (type == SCTP_STR_RESET_OUT_REQUEST) {
+                               int no_clear = 0;
+
                                req_out_param = (struct 
sctp_stream_reset_out_request *)req_param;
                                number_entries = (lparm_len - sizeof(struct 
sctp_stream_reset_out_request)) / sizeof(uint16_t);
                                asoc->stream_reset_out_is_outstanding = 0;
@@ -3614,9 +3641,20 @@ sctp_handle_stream_reset_response(struct
                                        sctp_reset_out_streams(stcb, 
number_entries, req_out_param->list_of_streams);
                                } else if (action == 
SCTP_STREAM_RESET_RESULT_DENIED) {
                                        
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_DENIED_OUT, stcb, number_entries, 
req_out_param->list_of_streams, SCTP_SO_NOT_LOCKED);
+                               } else if (action == 
SCTP_STREAM_RESET_RESULT_IN_PROGRESS) {
+                                       /*
+                                        * Set it up so we don't stop
+                                        * retransmitting
+                                        */
+                                       stcb->asoc.str_reset_seq_out--;
+                                       asoc->stream_reset_out_is_outstanding = 
1;
+                                       no_clear = 1;
                                } else {
                                        
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_FAILED_OUT, stcb, number_entries, 
req_out_param->list_of_streams, SCTP_SO_NOT_LOCKED);
                                }
+                               if (no_clear == 0) {
+                                       sctp_reset_clear_pending(stcb, 
number_entries, req_out_param->list_of_streams);
+                               }
                        } else if (type == SCTP_STR_RESET_IN_REQUEST) {
                                req_in_param = (struct 
sctp_stream_reset_in_request *)req_param;
                                number_entries = (lparm_len - sizeof(struct 
sctp_stream_reset_in_request)) / sizeof(uint16_t);
@@ -3643,7 +3681,12 @@ sctp_handle_stream_reset_response(struct
                                        asoc->stream_reset_outstanding--;
                                if (action == 
SCTP_STREAM_RESET_RESULT_PERFORMED) {
                                        /* Put the new streams into effect */
-                                       stcb->asoc.streamoutcnt += num_stream;
+                                       int i;
+
+                                       for (i = asoc->streamoutcnt; i < 
(asoc->streamoutcnt + num_stream); i++) {
+                                               asoc->strmout[i].state = 
SCTP_STREAM_OPEN;
+                                       }
+                                       asoc->streamoutcnt += num_stream;
                                        sctp_notify_stream_reset_add(stcb, 
stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, 0);
                                } else if (action == 
SCTP_STREAM_RESET_RESULT_DENIED) {
                                        sctp_notify_stream_reset_add(stcb, 
stcb->asoc.streamincnt, stcb->asoc.streamoutcnt,
@@ -3720,6 +3763,9 @@ sctp_handle_stream_reset_response(struct
                        }
                }
        }
+       if (asoc->stream_reset_outstanding == 0) {
+               sctp_send_stream_reset_out_if_possible(stcb);
+       }
        return (0);
 }
 
@@ -3750,22 +3796,33 @@ sctp_handle_str_reset_request_in(struct 
                } else if (stcb->asoc.stream_reset_out_is_outstanding == 0) {
                        len = ntohs(req->ph.param_length);
                        number_entries = ((len - sizeof(struct 
sctp_stream_reset_in_request)) / sizeof(uint16_t));
-                       for (i = 0; i < number_entries; i++) {
-                               temp = ntohs(req->list_of_streams[i]);
-                               req->list_of_streams[i] = temp;
+                       if (number_entries) {
+                               for (i = 0; i < number_entries; i++) {
+                                       temp = ntohs(req->list_of_streams[i]);
+                                       if (temp >= stcb->asoc.streamoutcnt) {
+                                               asoc->last_reset_action[0] = 
SCTP_STREAM_RESET_RESULT_DENIED;
+                                               goto bad_boy;
+                                       }
+                                       req->list_of_streams[i] = temp;
+                               }
+                               for (i = 0; i < number_entries; i++) {
+                                       if 
(stcb->asoc.strmout[req->list_of_streams[i]].state == SCTP_STREAM_OPEN) {
+                                               
stcb->asoc.strmout[req->list_of_streams[i]].state = SCTP_STREAM_RESET_PENDING;
+                                       }
+                               }
+                       } else {
+                               /* Its all */
+                               for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+                                       if (stcb->asoc.strmout[i].state == 
SCTP_STREAM_OPEN)
+                                               stcb->asoc.strmout[i].state = 
SCTP_STREAM_RESET_PENDING;
+                               }
                        }
                        asoc->last_reset_action[0] = 
SCTP_STREAM_RESET_RESULT_PERFORMED;
-                       sctp_add_stream_reset_out(chk, number_entries, 
req->list_of_streams,
-                           asoc->str_reset_seq_out,
-                           seq, (asoc->sending_seq - 1));
-                       asoc->stream_reset_out_is_outstanding = 1;
-                       asoc->str_reset = chk;
-                       sctp_timer_start(SCTP_TIMER_TYPE_STRRESET, 
stcb->sctp_ep, stcb, chk->whoTo);
-                       stcb->asoc.stream_reset_outstanding++;
                } else {
                        /* Can't do it, since we have sent one out */
                        asoc->last_reset_action[0] = 
SCTP_STREAM_RESET_RESULT_ERR_IN_PROGRESS;
                }
+bad_boy:
                sctp_add_stream_reset_result(chk, seq, 
asoc->last_reset_action[0]);
                asoc->str_reset_seq_in++;
        } else if (asoc->str_reset_seq_in - 1 == seq) {
@@ -3775,6 +3832,7 @@ sctp_handle_str_reset_request_in(struct 
        } else {
                sctp_add_stream_reset_result(chk, seq, 
SCTP_STREAM_RESET_RESULT_ERR_BAD_SEQNO);
        }
+       sctp_send_stream_reset_out_if_possible(stcb);
 }
 
 static int
@@ -3893,11 +3951,12 @@ sctp_handle_str_reset_request_out(struct
                                sctp_add_stream_reset_result(chk, seq, 
asoc->last_reset_action[0]);
                                return;
                        }
+                       liste->seq = seq;
                        liste->tsn = tsn;
                        liste->number_entries = number_entries;
                        memcpy(&liste->list_of_streams, req->list_of_streams, 
number_entries * sizeof(uint16_t));
                        TAILQ_INSERT_TAIL(&asoc->resetHead, liste, next_resp);
-                       asoc->last_reset_action[0] = 
SCTP_STREAM_RESET_RESULT_PERFORMED;
+                       asoc->last_reset_action[0] = 
SCTP_STREAM_RESET_RESULT_IN_PROGRESS;
                }
                sctp_add_stream_reset_result(chk, seq, 
asoc->last_reset_action[0]);
                asoc->str_reset_seq_in++;
@@ -4034,7 +4093,7 @@ sctp_handle_str_reset_add_out_strm(struc
                        mychk += num_stream;
                        if (mychk < 0x10000) {
                                stcb->asoc.last_reset_action[0] = 
SCTP_STREAM_RESET_RESULT_PERFORMED;
-                               if (sctp_send_str_reset_req(stcb, 0, NULL, 0, 
0, 0, 1, num_stream, 0, 1)) {
+                               if (sctp_send_str_reset_req(stcb, 0, NULL, 0, 
0, 1, num_stream, 0, 1)) {
                                        stcb->asoc.last_reset_action[0] = 
SCTP_STREAM_RESET_RESULT_DENIED;
                                }
                        } else {

Modified: head/sys/netinet/sctp_output.c
==============================================================================
--- head/sys/netinet/sctp_output.c      Wed Jul 22 10:05:46 2015        
(r285791)
+++ head/sys/netinet/sctp_output.c      Wed Jul 22 11:30:37 2015        
(r285792)
@@ -7162,6 +7162,10 @@ one_more_time:
                        }
                        atomic_subtract_int(&asoc->stream_queue_cnt, 1);
                        TAILQ_REMOVE(&strq->outqueue, sp, next);
+                       if (strq->state == SCTP_STREAM_RESET_PENDING &&
+                           TAILQ_EMPTY(&strq->outqueue)) {
+                               stcb->asoc.trigger_reset = 1;
+                       }
                        
stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, 
send_lock_up);
                        if (sp->net) {
                                sctp_free_remote_addr(sp->net);
@@ -7560,6 +7564,10 @@ dont_do_it:
                        send_lock_up = 1;
                }
                TAILQ_REMOVE(&strq->outqueue, sp, next);
+               if (strq->state == SCTP_STREAM_RESET_PENDING &&
+                   TAILQ_EMPTY(&strq->outqueue)) {
+                       stcb->asoc.trigger_reset = 1;
+               }
                stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, 
strq, sp, send_lock_up);
                if (sp->net) {
                        sctp_free_remote_addr(sp->net);
@@ -7787,7 +7795,7 @@ sctp_med_chunk_output(struct sctp_inpcb 
 #endif
        SCTP_TCB_LOCK_ASSERT(stcb);
        hbflag = 0;
-       if ((control_only) || (asoc->stream_reset_outstanding))
+       if (control_only)
                no_data_chunks = 1;
        else
                no_data_chunks = 0;
@@ -9856,7 +9864,9 @@ sctp_chunk_output(struct sctp_inpcb *inp
        unsigned int tot_frs = 0;
 
        asoc = &stcb->asoc;
+do_it_again:
        /* The Nagle algorithm is only applied when handling a send call. */
+       stcb->asoc.trigger_reset = 0;
        if (from_where == SCTP_OUTPUT_FROM_USR_SEND) {
                if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NODELAY)) {
                        nagle_on = 0;
@@ -10092,6 +10102,12 @@ sctp_chunk_output(struct sctp_inpcb *inp
         */
        if (stcb->asoc.ecn_echo_cnt_onq)
                sctp_fix_ecn_echo(asoc);
+
+       if (stcb->asoc.trigger_reset) {
+               if (sctp_send_stream_reset_out_if_possible(stcb) == 0) {
+                       goto do_it_again;
+               }
+       }
        return;
 }
 
@@ -11494,30 +11510,58 @@ sctp_send_cwr(struct sctp_tcb *stcb, str
        asoc->ctrl_queue_cnt++;
 }
 
-void
-sctp_add_stream_reset_out(struct sctp_tmit_chunk *chk,
-    int number_entries, uint16_t * list,
+static int
+sctp_add_stream_reset_out(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk,
     uint32_t seq, uint32_t resp_seq, uint32_t last_sent)
 {
        uint16_t len, old_len, i;
        struct sctp_stream_reset_out_request *req_out;
        struct sctp_chunkhdr *ch;
+       int at;
+       int number_entries = 0;
 
        ch = mtod(chk->data, struct sctp_chunkhdr *);
        old_len = len = SCTP_SIZE32(ntohs(ch->chunk_length));
-
        /* get to new offset for the param. */
        req_out = (struct sctp_stream_reset_out_request *)((caddr_t)ch + len);
        /* now how long will this param be? */
+       for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+               if ((stcb->asoc.strmout[i].state == SCTP_STREAM_RESET_PENDING) 
&&
+                   (TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue))) {
+                       number_entries++;
+               }
+       }
+       if (number_entries == 0) {
+               return (0);
+       }
+       if (number_entries == stcb->asoc.streamoutcnt) {
+               number_entries = 0;
+       }
+       if (number_entries > SCTP_MAX_STREAMS_AT_ONCE_RESET) {
+               number_entries = SCTP_MAX_STREAMS_AT_ONCE_RESET;
+       }
        len = (sizeof(struct sctp_stream_reset_out_request) + (sizeof(uint16_t) 
* number_entries));
        req_out->ph.param_type = htons(SCTP_STR_RESET_OUT_REQUEST);
        req_out->ph.param_length = htons(len);
        req_out->request_seq = htonl(seq);
        req_out->response_seq = htonl(resp_seq);
        req_out->send_reset_at_tsn = htonl(last_sent);
+       at = 0;
        if (number_entries) {
-               for (i = 0; i < number_entries; i++) {
-                       req_out->list_of_streams[i] = htons(list[i]);
+               for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+                       if ((stcb->asoc.strmout[i].state == 
SCTP_STREAM_RESET_PENDING) &&
+                           (TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue))) {
+                               req_out->list_of_streams[at] = htons(i);
+                               at++;
+                               stcb->asoc.strmout[i].state = 
SCTP_STREAM_RESET_IN_FLIGHT;
+                               if (at >= number_entries) {
+                                       break;
+                               }
+                       }
+               }
+       } else {
+               for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+                       stcb->asoc.strmout[i].state = 
SCTP_STREAM_RESET_IN_FLIGHT;
                }
        }
        if (SCTP_SIZE32(len) > len) {
@@ -11534,7 +11578,7 @@ sctp_add_stream_reset_out(struct sctp_tm
        chk->book_size_scale = 0;
        chk->send_size = SCTP_SIZE32(chk->book_size);
        SCTP_BUF_LEN(chk->data) = chk->send_size;
-       return;
+       return (1);
 }
 
 static void
@@ -11636,6 +11680,68 @@ sctp_add_stream_reset_result(struct sctp
 }
 
 void
+sctp_send_deferred_reset_response(struct sctp_tcb *stcb,
+    struct sctp_stream_reset_list *ent,
+    int response)
+{
+       struct sctp_association *asoc;
+       struct sctp_tmit_chunk *chk;
+       struct sctp_chunkhdr *ch;
+
+       asoc = &stcb->asoc;
+
+       /*
+        * Reset our last reset action to the new one IP -> response
+        * (PERFORMED probably). This assures that if we fail to send, a
+        * retran from the peer will get the new response.
+        */
+       asoc->last_reset_action[0] = response;
+       if (asoc->stream_reset_outstanding) {
+               return;
+       }
+       sctp_alloc_a_chunk(stcb, chk);
+       if (chk == NULL) {
+               SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, 
ENOMEM);
+               return;
+       }
+       chk->copy_by_ref = 0;
+       chk->rec.chunk_id.id = SCTP_STREAM_RESET;
+       chk->rec.chunk_id.can_take_data = 0;
+       chk->flags = 0;
+       chk->asoc = &stcb->asoc;
+       chk->book_size = sizeof(struct sctp_chunkhdr);
+       chk->send_size = SCTP_SIZE32(chk->book_size);
+       chk->book_size_scale = 0;
+       chk->data = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_NOWAIT, 1, MT_DATA);
+       if (chk->data == NULL) {
+               sctp_free_a_chunk(stcb, chk, SCTP_SO_LOCKED);
+               SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, 
ENOMEM);
+               return;
+       }
+       SCTP_BUF_RESV_UF(chk->data, SCTP_MIN_OVERHEAD);
+       sctp_add_stream_reset_result(chk, ent->seq, response);
+       /* setup chunk parameters */
+       chk->sent = SCTP_DATAGRAM_UNSENT;
+       chk->snd_count = 0;
+       if (stcb->asoc.alternate) {
+               chk->whoTo = stcb->asoc.alternate;
+       } else {
+               chk->whoTo = stcb->asoc.primary_destination;
+       }
+       ch = mtod(chk->data, struct sctp_chunkhdr *);
+       ch->chunk_type = SCTP_STREAM_RESET;
+       ch->chunk_flags = 0;
+       ch->chunk_length = htons(chk->book_size);
+       atomic_add_int(&chk->whoTo->ref_count, 1);
+       SCTP_BUF_LEN(chk->data) = chk->send_size;
+       /* insert the chunk for sending */
+       TAILQ_INSERT_TAIL(&asoc->control_send_queue,
+           chk,
+           sctp_next);
+       asoc->ctrl_queue_cnt++;
+}
+
+void
 sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *chk,
     uint32_t resp_seq, uint32_t result,
     uint32_t send_una, uint32_t recv_next)
@@ -11733,19 +11839,85 @@ sctp_add_an_in_stream(struct sctp_tmit_c
 }
 
 int
+sctp_send_stream_reset_out_if_possible(struct sctp_tcb *stcb)
+{
+       struct sctp_association *asoc;
+       struct sctp_tmit_chunk *chk;
+       struct sctp_chunkhdr *ch;
+       uint32_t seq;
+
+       asoc = &stcb->asoc;
+       if (asoc->stream_reset_outstanding) {
+               return (EALREADY);
+       }
+       sctp_alloc_a_chunk(stcb, chk);
+       if (chk == NULL) {
+               SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, 
ENOMEM);
+               return (ENOMEM);
+       }
+       chk->copy_by_ref = 0;
+       chk->rec.chunk_id.id = SCTP_STREAM_RESET;
+       chk->rec.chunk_id.can_take_data = 0;
+       chk->flags = 0;
+       chk->asoc = &stcb->asoc;
+       chk->book_size = sizeof(struct sctp_chunkhdr);
+       chk->send_size = SCTP_SIZE32(chk->book_size);
+       chk->book_size_scale = 0;
+       chk->data = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_NOWAIT, 1, MT_DATA);
+       if (chk->data == NULL) {
+               sctp_free_a_chunk(stcb, chk, SCTP_SO_LOCKED);
+               SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, 
ENOMEM);
+               return (ENOMEM);
+       }
+       SCTP_BUF_RESV_UF(chk->data, SCTP_MIN_OVERHEAD);
+
+       /* setup chunk parameters */
+       chk->sent = SCTP_DATAGRAM_UNSENT;
+       chk->snd_count = 0;
+       if (stcb->asoc.alternate) {
+               chk->whoTo = stcb->asoc.alternate;
+       } else {
+               chk->whoTo = stcb->asoc.primary_destination;
+       }
+       ch = mtod(chk->data, struct sctp_chunkhdr *);
+       ch->chunk_type = SCTP_STREAM_RESET;
+       ch->chunk_flags = 0;
+       ch->chunk_length = htons(chk->book_size);
+       atomic_add_int(&chk->whoTo->ref_count, 1);
+       SCTP_BUF_LEN(chk->data) = chk->send_size;
+       seq = stcb->asoc.str_reset_seq_out;
+       if (sctp_add_stream_reset_out(stcb, chk, seq, 
(stcb->asoc.str_reset_seq_in - 1), (stcb->asoc.sending_seq - 1))) {
+               seq++;
+               asoc->stream_reset_outstanding++;
+       } else {
+               m_freem(chk->data);
+               chk->data = NULL;
+               sctp_free_a_chunk(stcb, chk, SCTP_SO_LOCKED);
+               return (ENOENT);
+       }
+       asoc->str_reset = chk;
+       /* insert the chunk for sending */
+       TAILQ_INSERT_TAIL(&asoc->control_send_queue,
+           chk,
+           sctp_next);
+       asoc->ctrl_queue_cnt++;
+       sctp_timer_start(SCTP_TIMER_TYPE_STRRESET, stcb->sctp_ep, stcb, 
chk->whoTo);
+       return (0);
+}
+
+int
 sctp_send_str_reset_req(struct sctp_tcb *stcb,
     uint16_t number_entries, uint16_t * list,
-    uint8_t send_out_req,
     uint8_t send_in_req,
     uint8_t send_tsn_req,
     uint8_t add_stream,
     uint16_t adding_o,
     uint16_t adding_i, uint8_t peer_asked)
 {
-
        struct sctp_association *asoc;
        struct sctp_tmit_chunk *chk;
        struct sctp_chunkhdr *ch;
+       int can_send_out_req = 0;
        uint32_t seq;
 
        asoc = &stcb->asoc;
@@ -11756,16 +11928,18 @@ sctp_send_str_reset_req(struct sctp_tcb 
                SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, 
EBUSY);
                return (EBUSY);
        }
-       if ((send_out_req == 0) && (send_in_req == 0) && (send_tsn_req == 0) &&
+       if ((send_in_req == 0) && (send_tsn_req == 0) &&
            (add_stream == 0)) {
                /* nothing to do */
                SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, 
EINVAL);
                return (EINVAL);
        }
-       if (send_tsn_req && (send_out_req || send_in_req)) {
+       if (send_tsn_req && send_in_req) {
                /* error, can't do that */
                SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, 
EINVAL);
                return (EINVAL);
+       } else if (send_in_req) {
+               can_send_out_req = 1;
        }
        if (number_entries > (MCLBYTES -
            SCTP_MIN_OVERHEAD -
@@ -11813,12 +11987,14 @@ sctp_send_str_reset_req(struct sctp_tcb 
        SCTP_BUF_LEN(chk->data) = chk->send_size;
 
        seq = stcb->asoc.str_reset_seq_out;
-       if (send_out_req) {
-               sctp_add_stream_reset_out(chk, number_entries, list,
-                   seq, (stcb->asoc.str_reset_seq_in - 1), 
(stcb->asoc.sending_seq - 1));
-               asoc->stream_reset_out_is_outstanding = 1;
-               seq++;
-               asoc->stream_reset_outstanding++;
+       if (can_send_out_req) {
+               int ret;
+
+               ret = sctp_add_stream_reset_out(stcb, chk, seq, 
(stcb->asoc.str_reset_seq_in - 1), (stcb->asoc.sending_seq - 1));
+               if (ret) {
+                       seq++;
+                       asoc->stream_reset_outstanding++;
+               }
        }
        if ((add_stream & 1) &&
            ((stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt) < 
adding_o)) {
@@ -11858,6 +12034,7 @@ sctp_send_str_reset_req(struct sctp_tcb 
                        stcb->asoc.strmout[i].next_sequence_send = 
oldstream[i].next_sequence_send;
                        stcb->asoc.strmout[i].last_msg_incomplete = 
oldstream[i].last_msg_incomplete;
                        stcb->asoc.strmout[i].stream_no = i;
+                       stcb->asoc.strmout[i].state = oldstream[i].state;
                        
stcb->asoc.ss_functions.sctp_ss_init_stream(&stcb->asoc.strmout[i], 
&oldstream[i]);
                        /* now anything on those queues? */
                        TAILQ_FOREACH_SAFE(sp, &oldstream[i].outqueue, next, 
nsp) {
@@ -11890,6 +12067,7 @@ sctp_send_str_reset_req(struct sctp_tcb 
                        stcb->asoc.strmout[i].stream_no = i;
                        stcb->asoc.strmout[i].last_msg_incomplete = 0;
                        
stcb->asoc.ss_functions.sctp_ss_init_stream(&stcb->asoc.strmout[i], NULL);
+                       stcb->asoc.strmout[i].state = SCTP_STREAM_CLOSED;
                }
                stcb->asoc.strm_realoutsize = stcb->asoc.streamoutcnt + 
adding_o;
                SCTP_FREE(oldstream, SCTP_M_STRMO);
@@ -12499,12 +12677,24 @@ sctp_lower_sosend(struct socket *so,
                SCTP_ASOC_CREATE_UNLOCK(inp);
                create_lock_applied = 0;
        }
-       if (asoc->stream_reset_outstanding) {
+       /* Is the stream no. valid? */
+       if (srcv->sinfo_stream >= asoc->streamoutcnt) {
+               /* Invalid stream number */
+               SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, 
EINVAL);
+               error = EINVAL;
+               goto out_unlocked;
+       }
+       if ((asoc->strmout[srcv->sinfo_stream].state != SCTP_STREAM_OPEN) &&
+           (asoc->strmout[srcv->sinfo_stream].state != SCTP_STREAM_OPENING)) {
                /*
                 * Can't queue any data while stream reset is underway.
                 */
-               SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, 
EAGAIN);
-               error = EAGAIN;
+               if (asoc->strmout[srcv->sinfo_stream].state > SCTP_STREAM_OPEN) 
{
+                       error = EAGAIN;
+               } else {
+                       error = EINVAL;
+               }
+               SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, 
error);
                goto out_unlocked;
        }
        if ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT) ||
@@ -12643,13 +12833,6 @@ sctp_lower_sosend(struct socket *so,
                SCTP_TCB_UNLOCK(stcb);
                hold_tcblock = 0;
        }
-       /* Is the stream no. valid? */
-       if (srcv->sinfo_stream >= asoc->streamoutcnt) {
-               /* Invalid stream number */
-               SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, 
EINVAL);
-               error = EINVAL;
-               goto out_unlocked;
-       }
        if (asoc->strmout == NULL) {
                /* huh? software error */
                SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, 
EFAULT);
@@ -12945,7 +13128,7 @@ skip_preblock:
                                /*-
                                 * Ok, Nagle is set on and we have data 
outstanding.
                                 * Don't send anything and let SACKs drive out 
the
-                                * data unless wen have a "full" segment to 
send.
+                                * data unless we have a "full" segment to send.
                                 */
                                if (SCTP_BASE_SYSCTL(sctp_logging_level) & 
SCTP_NAGLE_LOGGING_ENABLE) {
                                        sctp_log_nagle_event(stcb, 
SCTP_NAGLE_APPLIED);

Modified: head/sys/netinet/sctp_output.h
==============================================================================
--- head/sys/netinet/sctp_output.h      Wed Jul 22 10:05:46 2015        
(r285791)
+++ head/sys/netinet/sctp_output.h      Wed Jul 22 11:30:37 2015        
(r285792)
@@ -170,18 +170,21 @@ void sctp_send_cwr(struct sctp_tcb *, st
 
 
 void
-sctp_add_stream_reset_out(struct sctp_tmit_chunk *,
-    int, uint16_t *, uint32_t, uint32_t, uint32_t);
+     sctp_add_stream_reset_result(struct sctp_tmit_chunk *, uint32_t, 
uint32_t);
 
 void
-     sctp_add_stream_reset_result(struct sctp_tmit_chunk *, uint32_t, 
uint32_t);
+sctp_send_deferred_reset_response(struct sctp_tcb *,
+    struct sctp_stream_reset_list *,
+    int);
 
 void
 sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *,
     uint32_t, uint32_t, uint32_t, uint32_t);
+int
+    sctp_send_stream_reset_out_if_possible(struct sctp_tcb *);
 
 int
-sctp_send_str_reset_req(struct sctp_tcb *, uint16_t, uint16_t *, uint8_t,
+sctp_send_str_reset_req(struct sctp_tcb *, uint16_t, uint16_t *,
     uint8_t, uint8_t, uint8_t, uint16_t, uint16_t, uint8_t);
 
 void

Modified: head/sys/netinet/sctp_structs.h
==============================================================================
--- head/sys/netinet/sctp_structs.h     Wed Jul 22 10:05:46 2015        
(r285791)
+++ head/sys/netinet/sctp_structs.h     Wed Jul 22 11:30:37 2015        
(r285792)
@@ -76,6 +76,7 @@ TAILQ_HEAD(sctpnetlisthead, sctp_nets);
 
 struct sctp_stream_reset_list {
        TAILQ_ENTRY(sctp_stream_reset_list) next_resp;
+       uint32_t seq;
        uint32_t tsn;
        uint32_t number_entries;
        uint16_t list_of_streams[];
@@ -580,11 +581,20 @@ union scheduling_parameters {
        struct ss_fb fb;
 };
 
+/* States for outgoing streams */
+#define SCTP_STREAM_CLOSED           0x00
+#define SCTP_STREAM_OPENING          0x01
+#define SCTP_STREAM_OPEN             0x02
+#define SCTP_STREAM_RESET_PENDING    0x03
+#define SCTP_STREAM_RESET_IN_FLIGHT  0x04
+
+#define SCTP_MAX_STREAMS_AT_ONCE_RESET 200
+
 /* This struct is used to track the traffic on outbound streams */
 struct sctp_stream_out {
        struct sctp_streamhead outqueue;
        union scheduling_parameters ss_params;
-       uint32_t chunks_on_queues;
+       uint32_t chunks_on_queues;      /* send queue and sent queue */
 #if defined(SCTP_DETAILED_STR_STATS)
        uint32_t abandoned_unsent[SCTP_PR_SCTP_MAX + 1];
        uint32_t abandoned_sent[SCTP_PR_SCTP_MAX + 1];
@@ -596,6 +606,7 @@ struct sctp_stream_out {
        uint16_t stream_no;
        uint16_t next_sequence_send;    /* next one I expect to send out */
        uint8_t last_msg_incomplete;
+       uint8_t state;
 };
 
 /* used to keep track of the addresses yet to try to add/delete */
@@ -1148,7 +1159,7 @@ struct sctp_association {
        uint8_t hb_random_idx;
        uint8_t default_dscp;
        uint8_t asconf_del_pending;     /* asconf delete last addr pending */
-
+       uint8_t trigger_reset;
        /*
         * This value, plus all other ack'd but above cum-ack is added
         * together to cross check against the bit that we have yet to

Modified: head/sys/netinet/sctp_usrreq.c
==============================================================================
--- head/sys/netinet/sctp_usrreq.c      Wed Jul 22 10:05:46 2015        
(r285791)
+++ head/sys/netinet/sctp_usrreq.c      Wed Jul 22 11:30:37 2015        
(r285792)
@@ -4620,18 +4620,24 @@ sctp_setopt(struct socket *so, int optna
                                SCTP_TCB_UNLOCK(stcb);
                                break;
                        }
-                       if (stcb->asoc.stream_reset_outstanding) {
-                               SCTP_LTRACE_ERR_RET(inp, NULL, NULL, 
SCTP_FROM_SCTP_USRREQ, EALREADY);
-                               error = EALREADY;
-                               SCTP_TCB_UNLOCK(stcb);
-                               break;
-                       }
                        if (strrst->srs_flags & SCTP_STREAM_RESET_INCOMING) {
                                send_in = 1;
+                               if (stcb->asoc.stream_reset_outstanding) {
+                                       SCTP_LTRACE_ERR_RET(inp, NULL, NULL, 
SCTP_FROM_SCTP_USRREQ, EALREADY);
+                                       error = EALREADY;
+                                       SCTP_TCB_UNLOCK(stcb);
+                                       break;
+                               }
                        }
                        if (strrst->srs_flags & SCTP_STREAM_RESET_OUTGOING) {
                                send_out = 1;
                        }
+                       if ((strrst->srs_number_streams > 
SCTP_MAX_STREAMS_AT_ONCE_RESET) && send_in) {
+                               SCTP_LTRACE_ERR_RET(inp, NULL, NULL, 
SCTP_FROM_SCTP_USRREQ, ENOMEM);
+                               error = ENOMEM;
+                               SCTP_TCB_UNLOCK(stcb);
+                               break;
+                       }
                        if ((send_in == 0) && (send_out == 0)) {
                                SCTP_LTRACE_ERR_RET(inp, NULL, NULL, 
SCTP_FROM_SCTP_USRREQ, EINVAL);
                                error = EINVAL;
@@ -4656,11 +4662,38 @@ sctp_setopt(struct socket *so, int optna
                                SCTP_TCB_UNLOCK(stcb);
                                break;
                        }
-                       error = sctp_send_str_reset_req(stcb, 
strrst->srs_number_streams,
-                           strrst->srs_stream_list,
-                           send_out, send_in, 0, 0, 0, 0, 0);
+                       if (send_out) {
+                               int cnt;
+                               uint16_t strm;
+
+                               if (strrst->srs_number_streams) {
+                                       for (i = 0, cnt = 0; i < 
strrst->srs_number_streams; i++) {
+                                               strm = 
strrst->srs_stream_list[i];
+                                               if 
(stcb->asoc.strmout[strm].state == SCTP_STREAM_OPEN) {
+                                                       
stcb->asoc.strmout[strm].state = SCTP_STREAM_RESET_PENDING;
+                                                       cnt++;
+                                               }
+                                       }
+                               } else {
+                                       /* Its all */
+                                       for (i = 0, cnt = 0; i < 
stcb->asoc.streamoutcnt; i++) {
+                                               if (stcb->asoc.strmout[i].state 
== SCTP_STREAM_OPEN) {
+                                                       
stcb->asoc.strmout[i].state = SCTP_STREAM_RESET_PENDING;
+                                                       cnt++;
+                                               }
+                                       }
+                               }
+                       }
+                       if (send_in) {
+                               error = sctp_send_str_reset_req(stcb, 
strrst->srs_number_streams,
+                                   strrst->srs_stream_list,
+                                   send_in, 0, 0, 0, 0, 0);
+                       } else
+                               error = 
sctp_send_stream_reset_out_if_possible(stcb);
+
+                       if (!error)
+                               sctp_chunk_output(inp, stcb, 
SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED);
 
-                       sctp_chunk_output(inp, stcb, 
SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED);
                        SCTP_TCB_UNLOCK(stcb);
                        break;
                }
@@ -4730,7 +4763,7 @@ sctp_setopt(struct socket *so, int optna
                                        goto skip_stuff;
                                }
                        }
-                       error = sctp_send_str_reset_req(stcb, 0, NULL, 0, 0, 0, 
addstream, add_o_strmcnt, add_i_strmcnt, 0);
+                       error = sctp_send_str_reset_req(stcb, 0, NULL, 0, 0, 
addstream, add_o_strmcnt, add_i_strmcnt, 0);
                        sctp_chunk_output(inp, stcb, 
SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED);
        skip_stuff:
                        SCTP_TCB_UNLOCK(stcb);
@@ -4738,6 +4771,7 @@ sctp_setopt(struct socket *so, int optna
                }
        case SCTP_RESET_ASSOC:
                {
+                       int i;
                        uint32_t *value;
 
                        SCTP_CHECK_AND_CAST(value, optval, uint32_t, optsize);
@@ -4762,7 +4796,25 @@ sctp_setopt(struct socket *so, int optna
                                SCTP_TCB_UNLOCK(stcb);
                                break;
                        }
-                       error = sctp_send_str_reset_req(stcb, 0, NULL, 0, 0, 1, 
0, 0, 0, 0);
+                       /*
+                        * Is there any data pending in the send or sent
+                        * queues?
+                        */
+                       if (!TAILQ_EMPTY(&stcb->asoc.send_queue) ||
+                           !TAILQ_EMPTY(&stcb->asoc.sent_queue)) {
+               busy_out:
+                               error = EBUSY;
+                               SCTP_LTRACE_ERR_RET(inp, NULL, NULL, 
SCTP_FROM_SCTP_USRREQ, error);
+                               SCTP_TCB_UNLOCK(stcb);
+                               break;
+                       }
+                       /* Do any streams have data queued? */
+                       for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+                               if 
(!TAILQ_EMPTY(&stcb->asoc.strmout[i].outqueue)) {
+                                       goto busy_out;
+                               }
+                       }
+                       error = sctp_send_str_reset_req(stcb, 0, NULL, 0, 1, 0, 
0, 0, 0);
                        sctp_chunk_output(inp, stcb, 
SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED);
                        SCTP_TCB_UNLOCK(stcb);
                        break;

Modified: head/sys/netinet/sctputil.c
==============================================================================
--- head/sys/netinet/sctputil.c Wed Jul 22 10:05:46 2015        (r285791)
+++ head/sys/netinet/sctputil.c Wed Jul 22 11:30:37 2015        (r285792)
@@ -1089,6 +1089,7 @@ sctp_init_asoc(struct sctp_inpcb *inp, s
 #endif
                asoc->strmout[i].stream_no = i;
                asoc->strmout[i].last_msg_incomplete = 0;
+               asoc->strmout[i].state = SCTP_STREAM_OPENING;
                asoc->ss_functions.sctp_ss_init_stream(&asoc->strmout[i], NULL);
        }
        asoc->ss_functions.sctp_ss_init(stcb, asoc, 0);
@@ -6855,7 +6856,7 @@ sctp_log_trace(uint32_t subsys, const ch
 
 #endif
 static void
-sctp_recv_udp_tunneled_packet(struct mbuf *m, int off, struct inpcb *ignored,
+sctp_recv_udp_tunneled_packet(struct mbuf *m, int off, struct inpcb *inp,
     const struct sockaddr *sa SCTP_UNUSED, void *ctx SCTP_UNUSED)
 {
        struct ip *iph;
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to