diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h index 192a79b362ef..d81589197448 100644 --- a/sys/netinet/sctp.h +++ b/sys/netinet/sctp.h @@ -155,8 +155,22 @@ struct sctp_paramhdr { * field. */ -/* these should probably go into sockets API */ -#define SCTP_RESET_STREAMS 0x00001004 /* wo */ +#define SCTP_ENABLE_STREAM_RESET 0x00000900 /* struct + * sctp_assoc_value */ +#define SCTP_RESET_STREAMS 0x00000901 /* struct + * sctp_reset_streams */ +#define SCTP_RESET_ASSOC 0x00000902 /* sctp_assoc_t */ +#define SCTP_ADD_STREAMS 0x00000903 /* struct + * sctp_add_streams */ + +/* For enable stream reset */ +#define SCTP_ENABLE_RESET_STREAM_REQ 0x00000001 +#define SCTP_ENABLE_RESET_ASSOC_REQ 0x00000002 +#define SCTP_ENABLE_CHANGE_ASSOC_REQ 0x00000004 +#define SCTP_ENABLE_VALUE_MASK 0x00000007 +/* For reset streams */ +#define SCTP_STREAM_RESET_INCOMING 0x00000001 +#define SCTP_STREAM_RESET_OUTGOING 0x00000002 /* here on down are more implementation specific */ diff --git a/sys/netinet/sctp_constants.h b/sys/netinet/sctp_constants.h index 55e3be9010c3..e53d6994470d 100644 --- a/sys/netinet/sctp_constants.h +++ b/sys/netinet/sctp_constants.h @@ -418,7 +418,8 @@ __FBSDID("$FreeBSD$"); #define SCTP_STR_RESET_IN_REQUEST 0x000e #define SCTP_STR_RESET_TSN_REQUEST 0x000f #define SCTP_STR_RESET_RESPONSE 0x0010 -#define SCTP_STR_RESET_ADD_STREAMS 0x0011 +#define SCTP_STR_RESET_ADD_OUT_STREAMS 0x0011 +#define SCTP_STR_RESET_ADD_IN_STREAMS 0x0012 #define SCTP_MAX_RESET_PARAMS 2 #define SCTP_STREAM_RESET_TSN_DELTA 0x1000 diff --git a/sys/netinet/sctp_header.h b/sys/netinet/sctp_header.h index 9c285a72c04d..1d92b656a294 100644 --- a/sys/netinet/sctp_header.h +++ b/sys/netinet/sctp_header.h @@ -501,7 +501,7 @@ struct sctp_stream_reset_add_strm { #define SCTP_STREAM_RESET_NOTHING 0x00000000 /* Nothing for me to do */ #define SCTP_STREAM_RESET_PERFORMED 0x00000001 /* Did it */ -#define SCTP_STREAM_RESET_DENIED 0x00000002 /* refused to do it */ +#define SCTP_STREAM_RESET_REJECT 0x00000002 /* refused to do it */ #define SCTP_STREAM_RESET_ERROR_STR 0x00000003 /* bad Stream no */ #define SCTP_STREAM_RESET_TRY_LATER 0x00000004 /* collision, try again */ #define SCTP_STREAM_RESET_BAD_SEQNO 0x00000005 /* bad str-reset seq no */ diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c index bd3f26d12a1d..1f9ee89a3a1e 100644 --- a/sys/netinet/sctp_input.c +++ b/sys/netinet/sctp_input.c @@ -2790,6 +2790,7 @@ sctp_handle_cookie_echo(struct mbuf *m, int iphlen, int offset, inp->sctp_ecn_enable = (*inp_p)->sctp_ecn_enable; inp->partial_delivery_point = (*inp_p)->partial_delivery_point; inp->sctp_context = (*inp_p)->sctp_context; + inp->local_strreset_support = (*inp_p)->local_strreset_support; inp->inp_starting_point_for_iterator = NULL; /* * copy in the authentication parameters from the @@ -3612,20 +3613,35 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb, if (asoc->stream_reset_outstanding) asoc->stream_reset_outstanding--; if (action != SCTP_STREAM_RESET_PERFORMED) { - sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_FAILED_IN, stcb, number_entries, srparam->list_of_streams, SCTP_SO_NOT_LOCKED); + sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_FAILED_IN, stcb, + number_entries, srparam->list_of_streams, SCTP_SO_NOT_LOCKED); } - } else if (type == SCTP_STR_RESET_ADD_STREAMS) { + } else if (type == SCTP_STR_RESET_ADD_OUT_STREAMS) { /* Ok we now may have more streams */ + int num_stream; + + num_stream = stcb->asoc.strm_pending_add_size; + if (num_stream > (stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt)) { + /* TSNH */ + num_stream = stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt; + } + stcb->asoc.strm_pending_add_size = 0; if (asoc->stream_reset_outstanding) asoc->stream_reset_outstanding--; if (action == SCTP_STREAM_RESET_PERFORMED) { /* Put the new streams into effect */ - stcb->asoc.streamoutcnt = stcb->asoc.strm_realoutsize; - sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_ADD_OK, stcb, - (uint32_t) stcb->asoc.streamoutcnt, NULL, SCTP_SO_NOT_LOCKED); + stcb->asoc.streamoutcnt += num_stream; + sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, 0); } else { - sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_ADD_FAIL, stcb, - (uint32_t) stcb->asoc.streamoutcnt, NULL, SCTP_SO_NOT_LOCKED); + sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, + SCTP_STREAM_CHANGED_DENIED); + } + } else if (type == SCTP_STR_RESET_ADD_IN_STREAMS) { + if (asoc->stream_reset_outstanding) + asoc->stream_reset_outstanding--; + if (action != SCTP_STREAM_RESET_PERFORMED) { + sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, + SCTP_STREAM_CHANGED_DENIED); } } else if (type == SCTP_STR_RESET_TSN_REQUEST) { /** @@ -3667,7 +3683,10 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb, sctp_reset_out_streams(stcb, 0, (uint16_t *) NULL); sctp_reset_in_stream(stcb, 0, (uint16_t *) NULL); - + sctp_notify_stream_reset_tsn(stcb, stcb->asoc.sending_seq, (stcb->asoc.mapping_array_base_tsn + 1), 0); + } else { + sctp_notify_stream_reset_tsn(stcb, stcb->asoc.sending_seq, (stcb->asoc.mapping_array_base_tsn + 1), + SCTP_STREAM_RESET_FAILED); } } /* get rid of the request and get the request flags */ @@ -3700,8 +3719,7 @@ sctp_handle_str_reset_request_in(struct sctp_tcb *stcb, if (trunc) { /* Can't do it, since they exceeded our buffer size */ asoc->last_reset_action[1] = asoc->last_reset_action[0]; - asoc->last_reset_action[0] = SCTP_STREAM_RESET_DENIED; - sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); + asoc->last_reset_action[0] = SCTP_STREAM_RESET_REJECT; } 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)); @@ -3723,8 +3741,8 @@ sctp_handle_str_reset_request_in(struct sctp_tcb *stcb, /* Can't do it, since we have sent one out */ asoc->last_reset_action[1] = asoc->last_reset_action[0]; asoc->last_reset_action[0] = SCTP_STREAM_RESET_TRY_LATER; - sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); } + 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) { sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); @@ -3786,7 +3804,7 @@ sctp_handle_str_reset_request_tsn(struct sctp_tcb *stcb, sctp_reset_in_stream(stcb, 0, (uint16_t *) NULL); stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0]; stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED; - + sctp_notify_stream_reset_tsn(stcb, stcb->asoc.sending_seq, (stcb->asoc.mapping_array_base_tsn + 1), 0); asoc->str_reset_seq_in++; } else if (asoc->str_reset_seq_in - 1 == seq) { sctp_add_stream_reset_result_tsn(chk, seq, asoc->last_reset_action[0], @@ -3831,12 +3849,10 @@ sctp_handle_str_reset_request_out(struct sctp_tcb *stcb, /* move the reset action back one */ asoc->last_reset_action[1] = asoc->last_reset_action[0]; if (trunc) { - sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED); - asoc->last_reset_action[0] = SCTP_STREAM_RESET_DENIED; + asoc->last_reset_action[0] = SCTP_STREAM_RESET_REJECT; } else if (SCTP_TSN_GE(asoc->cumulative_tsn, tsn)) { /* we can do it now */ sctp_reset_in_stream(stcb, number_entries, req->list_of_streams); - sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED); asoc->last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED; } else { /* @@ -3851,8 +3867,8 @@ sctp_handle_str_reset_request_out(struct sctp_tcb *stcb, siz, SCTP_M_STRESET); if (liste == NULL) { /* gak out of memory */ - sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED); - asoc->last_reset_action[0] = SCTP_STREAM_RESET_DENIED; + sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_REJECT); + asoc->last_reset_action[0] = SCTP_STREAM_RESET_REJECT; return; } liste->tsn = tsn; @@ -3860,9 +3876,9 @@ sctp_handle_str_reset_request_out(struct sctp_tcb *stcb, memcpy(&liste->req, req, (sizeof(struct sctp_stream_reset_out_request) + (number_entries * sizeof(uint16_t)))); TAILQ_INSERT_TAIL(&asoc->resetHead, liste, next_resp); - sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED); asoc->last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED; } + 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) { /* @@ -3889,7 +3905,7 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch * Peer is requesting to add more streams. If its within our * max-streams we will allow it. */ - uint16_t num_stream, i; + uint32_t num_stream, i; uint32_t seq; struct sctp_association *asoc = &stcb->asoc; struct sctp_queued_to_read *ctl, *nctl; @@ -3900,12 +3916,12 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch /* Now what would be the new total? */ if (asoc->str_reset_seq_in == seq) { num_stream += stcb->asoc.streamincnt; - if (num_stream > stcb->asoc.max_inbound_streams) { + if ((num_stream > stcb->asoc.max_inbound_streams) || + (num_stream > 0xffff)) { /* We must reject it they ask for to many */ denied: - sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED); stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0]; - stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_DENIED; + stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_REJECT; } else { /* Ok, we can do that :-) */ struct sctp_stream_in *oldstrm; @@ -3941,13 +3957,12 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch SCTP_FREE(oldstrm, SCTP_M_STRMI); /* update the size */ stcb->asoc.streamincnt = num_stream; - /* Send the ack */ - sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED); stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0]; stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED; - sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK, stcb, - (uint32_t) stcb->asoc.streamincnt, NULL, SCTP_SO_NOT_LOCKED); + sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, 0); } + 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) { /* * one seq back, just echo back last action since my @@ -3966,6 +3981,63 @@ sctp_handle_str_reset_add_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *ch } } +static void +sctp_handle_str_reset_add_out_strm(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk, + struct sctp_stream_reset_add_strm *str_add) +{ + /* + * Peer is requesting to add more streams. If its within our + * max-streams we will allow it. + */ + uint16_t num_stream; + uint32_t seq; + struct sctp_association *asoc = &stcb->asoc; + + /* Get the number. */ + seq = ntohl(str_add->request_seq); + num_stream = ntohs(str_add->number_of_streams); + /* Now what would be the new total? */ + if (asoc->str_reset_seq_in == seq) { + if (stcb->asoc.stream_reset_outstanding) { + /* We must reject it we have something pending */ + stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0]; + stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_REJECT; + } else { + /* Ok, we can do that :-) */ + int mychk; + + mychk = stcb->asoc.streamoutcnt; + mychk += num_stream; + if (mychk < 0x10000) { + stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0]; + stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED; + if (sctp_send_str_reset_req(stcb, 0, NULL, 0, 0, 0, 1, num_stream, 0, 1)) { + stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_REJECT; + } + } else { + stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0]; + stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_REJECT; + } + } + sctp_add_stream_reset_result(chk, seq, stcb->asoc.last_reset_action[0]); + asoc->str_reset_seq_in++; + } else if ((asoc->str_reset_seq_in - 1) == seq) { + /* + * one seq back, just echo back last action since my + * response was lost. + */ + sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]); + } else if ((asoc->str_reset_seq_in - 2) == seq) { + /* + * two seq back, just echo back last action since my + * response was lost. + */ + sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[1]); + } else { + sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_BAD_SEQNO); + } +} + #ifdef __GNUC__ __attribute__((noinline)) #endif @@ -3977,7 +4049,7 @@ __attribute__((noinline)) struct sctp_paramhdr pstore; uint8_t cstore[SCTP_CHUNK_BUFFER_SIZE]; - uint32_t seq; + uint32_t seq = 0; int num_req = 0; int trunc = 0; struct sctp_tmit_chunk *chk; @@ -4041,7 +4113,6 @@ strres_nochunk: } else { trunc = 0; } - if (num_param > SCTP_MAX_RESET_PARAMS) { /* hit the max of parameters already sorry.. */ break; @@ -4059,26 +4130,29 @@ strres_nochunk: } } sctp_handle_str_reset_request_out(stcb, chk, req_out, trunc); - } else if (ptype == SCTP_STR_RESET_ADD_STREAMS) { + } else if (ptype == SCTP_STR_RESET_ADD_OUT_STREAMS) { struct sctp_stream_reset_add_strm *str_add; str_add = (struct sctp_stream_reset_add_strm *)ph; num_req++; sctp_handle_str_reset_add_strm(stcb, chk, str_add); + } else if (ptype == SCTP_STR_RESET_ADD_IN_STREAMS) { + struct sctp_stream_reset_add_strm *str_add; + + str_add = (struct sctp_stream_reset_add_strm *)ph; + num_req++; + sctp_handle_str_reset_add_out_strm(stcb, chk, str_add); } else if (ptype == SCTP_STR_RESET_IN_REQUEST) { struct sctp_stream_reset_in_request *req_in; num_req++; - req_in = (struct sctp_stream_reset_in_request *)ph; - sctp_handle_str_reset_request_in(stcb, chk, req_in, trunc); } else if (ptype == SCTP_STR_RESET_TSN_REQUEST) { struct sctp_stream_reset_tsn_request *req_tsn; num_req++; req_tsn = (struct sctp_stream_reset_tsn_request *)ph; - if (sctp_handle_str_reset_request_tsn(stcb, chk, req_tsn)) { ret_code = 1; goto strres_nochunk; diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c index daee9372cd0e..603bd7ac562f 100644 --- a/sys/netinet/sctp_output.c +++ b/sys/netinet/sctp_output.c @@ -11716,7 +11716,7 @@ sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *chk, } static void -sctp_add_a_stream(struct sctp_tmit_chunk *chk, +sctp_add_an_out_stream(struct sctp_tmit_chunk *chk, uint32_t seq, uint16_t adding) { @@ -11733,7 +11733,7 @@ sctp_add_a_stream(struct sctp_tmit_chunk *chk, len = sizeof(struct sctp_stream_reset_add_strm); /* Fill it out. */ - addstr->ph.param_type = htons(SCTP_STR_RESET_ADD_STREAMS); + addstr->ph.param_type = htons(SCTP_STR_RESET_ADD_OUT_STREAMS); addstr->ph.param_length = htons(len); addstr->request_seq = htonl(seq); addstr->number_of_streams = htons(adding); @@ -11748,15 +11748,49 @@ sctp_add_a_stream(struct sctp_tmit_chunk *chk, return; } +static void +sctp_add_an_in_stream(struct sctp_tmit_chunk *chk, + uint32_t seq, + uint16_t adding) +{ + int len, old_len; + struct sctp_chunkhdr *ch; + struct sctp_stream_reset_add_strm *addstr; + + ch = mtod(chk->data, struct sctp_chunkhdr *); + old_len = len = SCTP_SIZE32(ntohs(ch->chunk_length)); + + /* get to new offset for the param. */ + addstr = (struct sctp_stream_reset_add_strm *)((caddr_t)ch + len); + /* now how long will this param be? */ + len = sizeof(struct sctp_stream_reset_add_strm); + /* Fill it out. */ + addstr->ph.param_type = htons(SCTP_STR_RESET_ADD_IN_STREAMS); + addstr->ph.param_length = htons(len); + addstr->request_seq = htonl(seq); + addstr->number_of_streams = htons(adding); + addstr->reserved = 0; + + /* now fix the chunk length */ + ch->chunk_length = htons(len + old_len); + chk->send_size = len + old_len; + chk->book_size = SCTP_SIZE32(chk->send_size); + chk->book_size_scale = 0; + SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size); + return; +} + + + int sctp_send_str_reset_req(struct sctp_tcb *stcb, int number_entries, uint16_t * list, uint8_t send_out_req, - uint32_t resp_seq, uint8_t send_in_req, uint8_t send_tsn_req, uint8_t add_stream, - uint16_t adding + uint16_t adding_o, + uint16_t adding_i, uint8_t peer_asked ) { @@ -11823,18 +11857,86 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, seq = stcb->asoc.str_reset_seq_out; if (send_out_req) { sctp_add_stream_reset_out(chk, number_entries, list, - seq, resp_seq, (stcb->asoc.sending_seq - 1)); + 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 (add_stream) { - sctp_add_a_stream(chk, seq, adding); + if ((add_stream & 1) && + ((stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt) < adding_o)) { + /* Need to allocate more */ + struct sctp_stream_out *oldstream; + struct sctp_stream_queue_pending *sp, *nsp; + int i; + + oldstream = stcb->asoc.strmout; + /* get some more */ + SCTP_MALLOC(stcb->asoc.strmout, struct sctp_stream_out *, + ((stcb->asoc.streamoutcnt + adding_o) * sizeof(struct sctp_stream_out)), + SCTP_M_STRMO); + if (stcb->asoc.strmout == NULL) { + uint8_t x; + + stcb->asoc.strmout = oldstream; + /* Turn off the bit */ + x = add_stream & 0xfe; + add_stream = x; + goto skip_stuff; + } + /* + * Ok now we proceed with copying the old out stuff and + * initializing the new stuff. + */ + SCTP_TCB_SEND_LOCK(stcb); + stcb->asoc.ss_functions.sctp_ss_clear(stcb, &stcb->asoc, 0, 1); + for (i = 0; i < stcb->asoc.streamoutcnt; i++) { + TAILQ_INIT(&stcb->asoc.strmout[i].outqueue); + stcb->asoc.strmout[i].next_sequence_sent = oldstream[i].next_sequence_sent; + stcb->asoc.strmout[i].last_msg_incomplete = oldstream[i].last_msg_incomplete; + stcb->asoc.strmout[i].stream_no = i; + 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) { + TAILQ_REMOVE(&oldstream[i].outqueue, sp, next); + TAILQ_INSERT_TAIL(&stcb->asoc.strmout[i].outqueue, sp, next); + } + /* Now move assoc pointers too */ + if (stcb->asoc.last_out_stream == &oldstream[i]) { + stcb->asoc.last_out_stream = &stcb->asoc.strmout[i]; + } + if (stcb->asoc.locked_on_sending == &oldstream[i]) { + stcb->asoc.locked_on_sending = &stcb->asoc.strmout[i]; + } + } + /* now the new streams */ + stcb->asoc.ss_functions.sctp_ss_init(stcb, &stcb->asoc, 1); + for (i = stcb->asoc.streamoutcnt; i < (stcb->asoc.streamoutcnt + adding_o); i++) { + stcb->asoc.strmout[i].next_sequence_sent = 0x0; + TAILQ_INIT(&stcb->asoc.strmout[i].outqueue); + 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.strm_realoutsize = stcb->asoc.streamoutcnt + adding_o; + SCTP_FREE(oldstream, SCTP_M_STRMO); + SCTP_TCB_SEND_UNLOCK(stcb); + } +skip_stuff: + if ((add_stream & 1) && (adding_o > 0)) { + asoc->strm_pending_add_size = adding_o; + asoc->peer_req_out = peer_asked; + sctp_add_an_out_stream(chk, seq, adding_o); + seq++; + asoc->stream_reset_outstanding++; + } + if ((add_stream & 2) && (adding_i > 0)) { + sctp_add_an_in_stream(chk, seq, adding_i); seq++; asoc->stream_reset_outstanding++; } if (send_in_req) { sctp_add_stream_reset_in(chk, number_entries, list, seq); + seq++; asoc->stream_reset_outstanding++; } if (send_tsn_req) { @@ -11842,7 +11944,6 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb, asoc->stream_reset_outstanding++; } asoc->str_reset = chk; - /* insert the chunk for sending */ TAILQ_INSERT_TAIL(&asoc->control_send_queue, chk, diff --git a/sys/netinet/sctp_output.h b/sys/netinet/sctp_output.h index 01b282129d72..ba30947f475e 100644 --- a/sys/netinet/sctp_output.h +++ b/sys/netinet/sctp_output.h @@ -194,15 +194,13 @@ sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *chk, int sctp_send_str_reset_req(struct sctp_tcb *stcb, - int number_entries, - uint16_t * list, + int number_entries, uint16_t * list, uint8_t send_out_req, - uint32_t resp_seq, uint8_t send_in_req, uint8_t send_tsn_req, - uint8_t add_str, - uint16_t adding); - + uint8_t add_stream, + uint16_t adding_o, + uint16_t adding_i, uint8_t from_peer); void sctp_send_abort(struct mbuf *, int, struct sctphdr *, uint32_t, diff --git a/sys/netinet/sctp_pcb.h b/sys/netinet/sctp_pcb.h index d8e748449150..325f76869fe1 100644 --- a/sys/netinet/sctp_pcb.h +++ b/sys/netinet/sctp_pcb.h @@ -399,6 +399,7 @@ struct sctp_inpcb { uint32_t sctp_frag_point; uint32_t partial_delivery_point; uint32_t sctp_context; + uint8_t local_strreset_support; uint32_t sctp_cmt_on_off; uint32_t sctp_ecn_enable; struct sctp_nonpad_sndrcvinfo def_send; diff --git a/sys/netinet/sctp_peeloff.c b/sys/netinet/sctp_peeloff.c index 87f586bc1ff3..fb9391a73125 100644 --- a/sys/netinet/sctp_peeloff.c +++ b/sys/netinet/sctp_peeloff.c @@ -127,6 +127,7 @@ sctp_do_peeloff(struct socket *head, struct socket *so, sctp_assoc_t assoc_id) n_inp->sctp_ecn_enable = inp->sctp_ecn_enable; n_inp->partial_delivery_point = inp->partial_delivery_point; n_inp->sctp_context = inp->sctp_context; + n_inp->local_strreset_support = inp->local_strreset_support; n_inp->inp_starting_point_for_iterator = NULL; /* copy in the authentication parameters from the original endpoint */ if (n_inp->sctp_ep.local_hmacs) @@ -202,6 +203,7 @@ sctp_get_peeloff(struct socket *head, sctp_assoc_t assoc_id, int *error) n_inp->sctp_ecn_enable = inp->sctp_ecn_enable; n_inp->partial_delivery_point = inp->partial_delivery_point; n_inp->sctp_context = inp->sctp_context; + n_inp->local_strreset_support = inp->local_strreset_support; n_inp->inp_starting_point_for_iterator = NULL; /* copy in the authentication parameters from the original endpoint */ diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h index 8685dfe0a02b..371169110122 100644 --- a/sys/netinet/sctp_structs.h +++ b/sys/netinet/sctp_structs.h @@ -1095,6 +1095,7 @@ struct sctp_association { uint16_t streamincnt; uint16_t streamoutcnt; uint16_t strm_realoutsize; + uint16_t strm_pending_add_size; /* my maximum number of retrans of INIT and SEND */ /* copied from SCTP but should be individually setable */ uint16_t max_init_times; @@ -1156,6 +1157,9 @@ struct sctp_association { /* Flag to tell if ECN is allowed */ uint8_t ecn_allowed; + /* Did the peer make the stream config (add out) request */ + uint8_t peer_req_out; + /* flag to indicate if peer can do asconf */ uint8_t peer_supports_asconf; /* EY - flag to indicate if peer can do nr_sack */ @@ -1166,6 +1170,7 @@ struct sctp_association { uint8_t peer_supports_auth; /* stream resets are supported by the peer */ uint8_t peer_supports_strreset; + uint8_t local_strreset_support; uint8_t peer_supports_nat; /* diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h index ec24984f9cb4..87af9bf30465 100644 --- a/sys/netinet/sctp_uio.h +++ b/sys/netinet/sctp_uio.h @@ -438,23 +438,51 @@ struct sctp_sender_dry_event { /* - * stream reset event + * Stream reset event - subscribe to SCTP_STREAM_RESET_EVENT */ struct sctp_stream_reset_event { uint16_t strreset_type; uint16_t strreset_flags; uint32_t strreset_length; sctp_assoc_t strreset_assoc_id; - uint16_t strreset_list[]; + uint16_t strreset_stream_list[]; }; -/* flags in strreset_flags field */ -#define SCTP_STRRESET_INBOUND_STR 0x0001 -#define SCTP_STRRESET_OUTBOUND_STR 0x0002 -#define SCTP_STRRESET_ALL_STREAMS 0x0004 -#define SCTP_STRRESET_STREAM_LIST 0x0008 -#define SCTP_STRRESET_FAILED 0x0010 -#define SCTP_STRRESET_ADD_STREAM 0x0020 +/* flags in stream_reset_event (strreset_flags) */ +#define SCTP_STREAM_RESET_DENIED 0x0004 /* SCTP_STRRESET_FAILED */ +#define SCTP_STREAM_RESET_FAILED 0x0008 /* SCTP_STRRESET_FAILED */ +#define SCTP_STREAM_CHANGED_DENIED 0x0010 + +/* + * Assoc reset event - subscribe to SCTP_ASSOC_RESET_EVENT + */ +struct sctp_assoc_reset_event { + uint16_t assocreset_type; + uint16_t assocreset_flags; + uint32_t assocreset_length; + sctp_assoc_t assocreset_assoc_id; + uint32_t assocreset_local_tsn; + uint32_t assocreset_remote_tsn; +}; + +#define SCTP_ASSOC_RESET_DENIED 0x0004 +#define SCTP_ASSOC_RESET_FAILED 0x0008 + +/* + * Stream change event - subscribe to SCTP_STREAM_CHANGE_EVENT + */ +struct sctp_stream_change_event { + uint16_t strchange_type; + uint16_t strchange_flags; + uint32_t strchange_length; + sctp_assoc_t strchange_assoc_id; + uint16_t strchange_instrms; + uint16_t strchange_outstrms; +}; + +#define SCTP_STREAM_CHANGE_DENIED 0x0004 +#define SCTP_STREAM_CHANGE_FAILED 0x0008 + /* SCTP notification event */ struct sctp_tlv { @@ -477,6 +505,9 @@ union sctp_notification { struct sctp_authkey_event sn_auth_event; struct sctp_sender_dry_event sn_sender_dry_event; struct sctp_stream_reset_event sn_strreset_event; + struct sctp_assoc_reset_event sn_assocreset_event; + struct sctp_stream_change_event sn_strchange_event; + }; /* notification types */ @@ -493,6 +524,9 @@ union sctp_notification { #define SCTP_STREAM_RESET_EVENT 0x0009 #define SCTP_SENDER_DRY_EVENT 0x000a #define SCTP_NOTIFICATIONS_STOPPED_EVENT 0x000b /* we don't send this */ +#define SCTP_ASSOC_RESET_EVENT 0x000c +#define SCTP_STREAM_CHANGE_EVENT 0x000d + /* * socket option structs */ @@ -707,19 +741,18 @@ struct sctp_blk_args { */ #define SCTP_MAX_EXPLICT_STR_RESET 1000 -#define SCTP_RESET_LOCAL_RECV 0x0001 -#define SCTP_RESET_LOCAL_SEND 0x0002 -#define SCTP_RESET_BOTH 0x0003 -#define SCTP_RESET_TSN 0x0004 -#define SCTP_RESET_ADD_STREAMS 0x0005 - -struct sctp_stream_reset { - sctp_assoc_t strrst_assoc_id; - uint16_t strrst_flags; - uint16_t strrst_num_streams; /* 0 == ALL */ - uint16_t strrst_list[]; /* list if strrst_num_streams is not 0 */ +struct sctp_reset_streams { + sctp_assoc_t srs_assoc_id; + uint16_t srs_flags; + uint16_t srs_number_streams; /* 0 == ALL */ + uint16_t srs_stream_list[]; /* list if strrst_num_streams is not 0 */ }; +struct sctp_add_streams { + sctp_assoc_t sas_assoc_id; + uint16_t sas_instrms; + uint16_t sas_outstrms; +}; struct sctp_get_nonce_values { sctp_assoc_t gn_assoc_id; diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 569ec9e8adf1..fd9c022e04bb 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -4088,17 +4088,52 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } break; } + case SCTP_ENABLE_STREAM_RESET: + { + struct sctp_assoc_value *av; + uint8_t set_value = 0; + SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, optsize); + if (av->assoc_value & (~SCTP_ENABLE_VALUE_MASK)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + error = EINVAL; + break; + } + set_value = av->assoc_value & SCTP_ENABLE_VALUE_MASK; + SCTP_FIND_STCB(inp, stcb, av->assoc_id); + if (stcb) { + stcb->asoc.local_strreset_support = set_value; + SCTP_TCB_UNLOCK(stcb); + } else { + if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) || + (inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL) || + (av->assoc_id == SCTP_FUTURE_ASSOC) || + (av->assoc_id == SCTP_ALL_ASSOC)) { + SCTP_INP_WLOCK(inp); + inp->local_strreset_support = set_value; + SCTP_INP_WUNLOCK(inp); + } + if ((av->assoc_id == SCTP_CURRENT_ASSOC) || + (av->assoc_id == SCTP_ALL_ASSOC)) { + SCTP_INP_RLOCK(inp); + LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) { + SCTP_TCB_LOCK(stcb); + stcb->asoc.local_strreset_support = set_value; + SCTP_TCB_UNLOCK(stcb); + } + SCTP_INP_RUNLOCK(inp); + } + } + break; + } case SCTP_RESET_STREAMS: { - struct sctp_stream_reset *strrst; - uint8_t send_in = 0, send_tsn = 0, send_out = 0, - addstream = 0; - uint16_t addstrmcnt = 0; - int i; + struct sctp_reset_streams *strrst; + int i, send_out = 0; + int send_in = 0; - SCTP_CHECK_AND_CAST(strrst, optval, struct sctp_stream_reset, optsize); - SCTP_FIND_STCB(inp, stcb, strrst->strrst_assoc_id); + SCTP_CHECK_AND_CAST(strrst, optval, struct sctp_reset_streams, optsize); + SCTP_FIND_STCB(inp, stcb, strrst->srs_assoc_id); if (stcb == NULL) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOENT); @@ -4107,13 +4142,19 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, } if (stcb->asoc.peer_supports_strreset == 0) { /* - * Peer does not support it, we return - * protocol not supported since this is true - * for this feature and this peer, not the - * socket request in general. + * Peer does not support the chunk type. */ - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EPROTONOSUPPORT); - error = EPROTONOSUPPORT; + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EOPNOTSUPP); + error = EOPNOTSUPP; + SCTP_TCB_UNLOCK(stcb); + break; + } + if (!(stcb->asoc.local_strreset_support & SCTP_ENABLE_RESET_STREAM_REQ)) { + /* + * User did not enable the operation. + */ + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EPERM); + error = EPERM; SCTP_TCB_UNLOCK(stcb); break; } @@ -4123,133 +4164,141 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, SCTP_TCB_UNLOCK(stcb); break; } - if (strrst->strrst_flags == SCTP_RESET_LOCAL_RECV) { + if (strrst->srs_flags & SCTP_STREAM_RESET_INCOMING) { send_in = 1; - } else if (strrst->strrst_flags == SCTP_RESET_LOCAL_SEND) { + } + if (strrst->srs_flags & SCTP_STREAM_RESET_OUTGOING) { send_out = 1; - } else if (strrst->strrst_flags == SCTP_RESET_BOTH) { - send_in = 1; - send_out = 1; - } else if (strrst->strrst_flags == SCTP_RESET_TSN) { - send_tsn = 1; - } else if (strrst->strrst_flags == SCTP_RESET_ADD_STREAMS) { - if (send_tsn || - send_in || - send_out) { - /* We can't do that and add streams */ - error = EINVAL; - goto skip_stuff; - } - if (stcb->asoc.stream_reset_outstanding) { - error = EBUSY; - goto skip_stuff; - } - addstream = 1; - /* We allocate here */ - addstrmcnt = strrst->strrst_num_streams; - if ((int)(addstrmcnt + stcb->asoc.streamoutcnt) > 0xffff) { - /* You can't have more than 64k */ - error = EINVAL; - goto skip_stuff; - } - if ((stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt) < addstrmcnt) { - /* Need to allocate more */ - struct sctp_stream_out *oldstream; - struct sctp_stream_queue_pending *sp, - *nsp; - - oldstream = stcb->asoc.strmout; - /* get some more */ - SCTP_MALLOC(stcb->asoc.strmout, struct sctp_stream_out *, - ((stcb->asoc.streamoutcnt + addstrmcnt) * sizeof(struct sctp_stream_out)), - SCTP_M_STRMO); - if (stcb->asoc.strmout == NULL) { - stcb->asoc.strmout = oldstream; - error = ENOMEM; - goto skip_stuff; - } - /* - * Ok now we proceed with copying - * the old out stuff and - * initializing the new stuff. - */ - SCTP_TCB_SEND_LOCK(stcb); - stcb->asoc.ss_functions.sctp_ss_clear(stcb, &stcb->asoc, 0, 1); - for (i = 0; i < stcb->asoc.streamoutcnt; i++) { - TAILQ_INIT(&stcb->asoc.strmout[i].outqueue); - stcb->asoc.strmout[i].next_sequence_sent = oldstream[i].next_sequence_sent; - stcb->asoc.strmout[i].last_msg_incomplete = oldstream[i].last_msg_incomplete; - stcb->asoc.strmout[i].stream_no = i; - 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) { - TAILQ_REMOVE(&oldstream[i].outqueue, sp, next); - TAILQ_INSERT_TAIL(&stcb->asoc.strmout[i].outqueue, sp, next); - } - /* - * Now move assoc pointers - * too - */ - if (stcb->asoc.last_out_stream == &oldstream[i]) { - stcb->asoc.last_out_stream = &stcb->asoc.strmout[i]; - } - if (stcb->asoc.locked_on_sending == &oldstream[i]) { - stcb->asoc.locked_on_sending = &stcb->asoc.strmout[i]; - } - } - /* now the new streams */ - stcb->asoc.ss_functions.sctp_ss_init(stcb, &stcb->asoc, 1); - for (i = stcb->asoc.streamoutcnt; i < (stcb->asoc.streamoutcnt + addstrmcnt); i++) { - stcb->asoc.strmout[i].next_sequence_sent = 0x0; - TAILQ_INIT(&stcb->asoc.strmout[i].outqueue); - 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.strm_realoutsize = stcb->asoc.streamoutcnt + addstrmcnt; - SCTP_FREE(oldstream, SCTP_M_STRMO); - } - SCTP_TCB_SEND_UNLOCK(stcb); - goto skip_stuff; - } else { + } + if ((send_in == 0) && (send_out == 0)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); error = EINVAL; SCTP_TCB_UNLOCK(stcb); break; } - for (i = 0; i < strrst->strrst_num_streams; i++) { + for (i = 0; i < strrst->srs_number_streams; i++) { if ((send_in) && - - (strrst->strrst_list[i] > stcb->asoc.streamincnt)) { + (strrst->srs_stream_list[i] > stcb->asoc.streamincnt)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); error = EINVAL; - goto get_out; + break; } if ((send_out) && - (strrst->strrst_list[i] > stcb->asoc.streamoutcnt)) { + (strrst->srs_stream_list[i] > stcb->asoc.streamoutcnt)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); error = EINVAL; - goto get_out; + break; } } - skip_stuff: if (error) { - get_out: SCTP_TCB_UNLOCK(stcb); break; } - error = sctp_send_str_reset_req(stcb, strrst->strrst_num_streams, - strrst->strrst_list, - send_out, (stcb->asoc.str_reset_seq_in - 3), - send_in, send_tsn, addstream, addstrmcnt); + error = sctp_send_str_reset_req(stcb, strrst->srs_number_streams, + strrst->srs_stream_list, + send_out, send_in, 0, 0, 0, 0, 0); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED); SCTP_TCB_UNLOCK(stcb); break; } + case SCTP_ADD_STREAMS: + { + struct sctp_add_streams *stradd; + uint8_t addstream = 0; + uint16_t add_o_strmcnt = 0; + uint16_t add_i_strmcnt = 0; + + SCTP_CHECK_AND_CAST(stradd, optval, struct sctp_add_streams, optsize); + SCTP_FIND_STCB(inp, stcb, stradd->sas_assoc_id); + if (stcb == NULL) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOENT); + error = ENOENT; + break; + } + if ((stradd->sas_outstrms == 0) && + (stradd->sas_instrms == 0)) { + error = EINVAL; + goto skip_stuff; + } + if (stradd->sas_outstrms) { + addstream = 1; + /* We allocate here */ + add_o_strmcnt = stradd->sas_outstrms; + if ((((int)add_o_strmcnt) + ((int)stcb->asoc.streamoutcnt)) > 0x0000ffff) { + /* You can't have more than 64k */ + error = EINVAL; + goto skip_stuff; + } + } + if (stradd->sas_instrms) { + int cnt; + + addstream |= 2; + /* + * We allocate inside + * sctp_send_str_reset_req() + */ + add_i_strmcnt = stradd->sas_instrms; + cnt = add_i_strmcnt; + cnt += stcb->asoc.streamincnt; + if (cnt > 0x0000ffff) { + /* You can't have more than 64k */ + error = EINVAL; + goto skip_stuff; + } + if (cnt > (int)stcb->asoc.max_inbound_streams) { + /* More than you are allowed */ + error = EINVAL; + goto skip_stuff; + } + } + error = sctp_send_str_reset_req(stcb, 0, NULL, 0, 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); + break; + } + case SCTP_RESET_ASSOC: + { + uint32_t *value; + + SCTP_CHECK_AND_CAST(value, optval, uint32_t, optsize); + SCTP_FIND_STCB(inp, stcb, (sctp_assoc_t) * value); + if (stcb == NULL) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOENT); + error = ENOENT; + break; + } + if (stcb->asoc.peer_supports_strreset == 0) { + /* + * Peer does not support the chunk type. + */ + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EOPNOTSUPP); + error = EOPNOTSUPP; + SCTP_TCB_UNLOCK(stcb); + break; + } + if (!(stcb->asoc.local_strreset_support & SCTP_ENABLE_RESET_ASSOC_REQ)) { + /* + * User did not enable the operation. + */ + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EPERM); + error = EPERM; + 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; + } + error = sctp_send_str_reset_req(stcb, 0, NULL, 0, 0, 1, 0, 0, 0, 0); + sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED); + SCTP_TCB_UNLOCK(stcb); + break; + } case SCTP_CONNECT_X: if (optsize < (sizeof(int) + sizeof(struct sockaddr_in))) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c index fde7d58a0cde..675c93dd4266 100644 --- a/sys/netinet/sctputil.c +++ b/sys/netinet/sctputil.c @@ -978,8 +978,8 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb, asoc->free_chunk_cnt = 0; asoc->iam_blocking = 0; - asoc->context = m->sctp_context; + asoc->local_strreset_support = m->local_strreset_support; asoc->def_send = m->def_send; asoc->delayed_ack = TICKS_TO_MSEC(m->sctp_ep.sctp_timeoutticks[SCTP_TIMER_RECV]); asoc->sack_freq = m->sctp_ep.sctp_sack_freq; @@ -3206,36 +3206,42 @@ sctp_notify_sender_dry_event(struct sctp_tcb *stcb, } -static void -sctp_notify_stream_reset_add(struct sctp_tcb *stcb, int number_entries, int flag) +void +sctp_notify_stream_reset_add(struct sctp_tcb *stcb, uint16_t numberin, uint16_t numberout, int flag) { struct mbuf *m_notify; struct sctp_queued_to_read *control; - struct sctp_stream_reset_event *strreset; + struct sctp_stream_change_event *stradd; int len; if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_STREAM_RESETEVNT)) { /* event not enabled */ return; } + if ((stcb->asoc.peer_req_out) && flag) { + /* Peer made the request, don't tell the local user */ + stcb->asoc.peer_req_out = 0; + return; + } + stcb->asoc.peer_req_out = 0; m_notify = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA); if (m_notify == NULL) /* no space left */ return; SCTP_BUF_LEN(m_notify) = 0; - len = sizeof(struct sctp_stream_reset_event) + (number_entries * sizeof(uint16_t)); + len = sizeof(struct sctp_stream_change_event); if (len > M_TRAILINGSPACE(m_notify)) { /* never enough room */ sctp_m_freem(m_notify); return; } - strreset = mtod(m_notify, struct sctp_stream_reset_event *); - strreset->strreset_type = SCTP_STREAM_RESET_EVENT; - strreset->strreset_flags = SCTP_STRRESET_ADD_STREAM | flag; - strreset->strreset_length = len; - strreset->strreset_assoc_id = sctp_get_associd(stcb); - strreset->strreset_list[0] = number_entries; - + stradd = mtod(m_notify, struct sctp_stream_change_event *); + stradd->strchange_type = SCTP_STREAM_CHANGE_EVENT; + stradd->strchange_flags = flag; + stradd->strchange_length = len; + stradd->strchange_assoc_id = sctp_get_associd(stcb); + stradd->strchange_instrms = numberin; + stradd->strchange_outstrms = numberout; SCTP_BUF_LEN(m_notify) = len; SCTP_BUF_NEXT(m_notify) = NULL; if (sctp_sbspace(&stcb->asoc, &stcb->sctp_socket->so_rcv) < SCTP_BUF_LEN(m_notify)) { @@ -3261,6 +3267,62 @@ sctp_notify_stream_reset_add(struct sctp_tcb *stcb, int number_entries, int flag &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); } +void +sctp_notify_stream_reset_tsn(struct sctp_tcb *stcb, uint32_t sending_tsn, uint32_t recv_tsn, int flag) +{ + struct mbuf *m_notify; + struct sctp_queued_to_read *control; + struct sctp_assoc_reset_event *strasoc; + int len; + + if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_STREAM_RESETEVNT)) { + /* event not enabled */ + return; + } + m_notify = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_DONTWAIT, 1, MT_DATA); + if (m_notify == NULL) + /* no space left */ + return; + SCTP_BUF_LEN(m_notify) = 0; + len = sizeof(struct sctp_assoc_reset_event); + if (len > M_TRAILINGSPACE(m_notify)) { + /* never enough room */ + sctp_m_freem(m_notify); + return; + } + strasoc = mtod(m_notify, struct sctp_assoc_reset_event *); + strasoc->assocreset_type = SCTP_ASSOC_RESET_EVENT; + strasoc->assocreset_flags = flag; + strasoc->assocreset_length = len; + strasoc->assocreset_assoc_id = sctp_get_associd(stcb); + strasoc->assocreset_local_tsn = sending_tsn; + strasoc->assocreset_remote_tsn = recv_tsn; + SCTP_BUF_LEN(m_notify) = len; + SCTP_BUF_NEXT(m_notify) = NULL; + if (sctp_sbspace(&stcb->asoc, &stcb->sctp_socket->so_rcv) < SCTP_BUF_LEN(m_notify)) { + /* no space */ + sctp_m_freem(m_notify); + return; + } + /* append to socket */ + control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination, + 0, 0, stcb->asoc.context, 0, 0, 0, + m_notify); + if (control == NULL) { + /* no memory */ + sctp_m_freem(m_notify); + return; + } + control->spec_flags = M_NOTIFICATION; + control->length = SCTP_BUF_LEN(m_notify); + /* not that we need this */ + control->tail_mbuf = m_notify; + sctp_add_to_readq(stcb->sctp_ep, stcb, + control, + &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, SCTP_SO_NOT_LOCKED); +} + + static void sctp_notify_stream_reset(struct sctp_tcb *stcb, @@ -3288,18 +3350,14 @@ sctp_notify_stream_reset(struct sctp_tcb *stcb, } strreset = mtod(m_notify, struct sctp_stream_reset_event *); strreset->strreset_type = SCTP_STREAM_RESET_EVENT; - if (number_entries == 0) { - strreset->strreset_flags = flag | SCTP_STRRESET_ALL_STREAMS; - } else { - strreset->strreset_flags = flag | SCTP_STRRESET_STREAM_LIST; - } + strreset->strreset_flags = flag; strreset->strreset_length = len; strreset->strreset_assoc_id = sctp_get_associd(stcb); if (number_entries) { int i; for (i = 0; i < number_entries; i++) { - strreset->strreset_list[i] = ntohs(list[i]); + strreset->strreset_stream_list[i] = ntohs(list[i]); } } SCTP_BUF_LEN(m_notify) = len; @@ -3439,27 +3497,19 @@ sctp_ulp_notify(uint32_t notification, struct sctp_tcb *stcb, break; case SCTP_NOTIFY_HB_RESP: break; - case SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK: - sctp_notify_stream_reset_add(stcb, error, SCTP_STRRESET_INBOUND_STR); - break; - case SCTP_NOTIFY_STR_RESET_ADD_OK: - sctp_notify_stream_reset_add(stcb, error, SCTP_STRRESET_OUTBOUND_STR); - break; - case SCTP_NOTIFY_STR_RESET_ADD_FAIL: - sctp_notify_stream_reset_add(stcb, error, (SCTP_STRRESET_FAILED | SCTP_STRRESET_OUTBOUND_STR)); - break; - case SCTP_NOTIFY_STR_RESET_SEND: - sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), SCTP_STRRESET_OUTBOUND_STR); + sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), SCTP_STREAM_RESET_INCOMING); break; case SCTP_NOTIFY_STR_RESET_RECV: - sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), SCTP_STRRESET_INBOUND_STR); + sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), SCTP_STREAM_RESET_OUTGOING); break; case SCTP_NOTIFY_STR_RESET_FAILED_OUT: - sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), (SCTP_STRRESET_OUTBOUND_STR | SCTP_STRRESET_FAILED)); + sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), + (SCTP_STREAM_RESET_OUTGOING | SCTP_STREAM_RESET_INCOMING)); break; case SCTP_NOTIFY_STR_RESET_FAILED_IN: - sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), (SCTP_STRRESET_INBOUND_STR | SCTP_STRRESET_FAILED)); + sctp_notify_stream_reset(stcb, error, ((uint16_t *) data), + (SCTP_STREAM_RESET_OUTGOING | SCTP_STREAM_RESET_INCOMING)); break; case SCTP_NOTIFY_ASCONF_ADD_IP: sctp_notify_peer_addr_change(stcb, SCTP_ADDR_ADDED, data, diff --git a/sys/netinet/sctputil.h b/sys/netinet/sctputil.h index bbaaaa409a74..495b17adcf6c 100644 --- a/sys/netinet/sctputil.h +++ b/sys/netinet/sctputil.h @@ -87,6 +87,12 @@ int sctp_init_asoc(struct sctp_inpcb *, struct sctp_tcb *, uint32_t, uint32_t); void sctp_fill_random_store(struct sctp_pcb *); +void +sctp_notify_stream_reset_add(struct sctp_tcb *stcb, uint16_t numberin, + uint16_t numberout, int flag); +void + sctp_notify_stream_reset_tsn(struct sctp_tcb *stcb, uint32_t sending_tsn, uint32_t recv_tsn, int flag); + void sctp_timer_start(int, struct sctp_inpcb *, struct sctp_tcb *, struct sctp_nets *);