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
This commit is contained in:
parent
b2a714f330
commit
606fc6cd55
@ -1886,6 +1886,7 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
|
||||
|
||||
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);
|
||||
|
@ -357,14 +357,17 @@ sctp_process_init(struct sctp_init_chunk *cp, struct sctp_tcb *stcb)
|
||||
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 *stcb, uint32_t number_entries, uint16_t
|
||||
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 sctp_tcb *stcb,
|
||||
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_tcb *stcb,
|
||||
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 sctp_tcb *stcb,
|
||||
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 sctp_tcb *stcb,
|
||||
}
|
||||
}
|
||||
}
|
||||
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 sctp_tcb *stcb,
|
||||
} 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 sctp_tcb *stcb,
|
||||
} 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_tcb *stcb,
|
||||
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(struct sctp_tcb *stcb, struct sctp_tmit_chunk
|
||||
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 {
|
||||
|
@ -7162,6 +7162,10 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb,
|
||||
}
|
||||
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 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb,
|
||||
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 *inp,
|
||||
#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, struct sctp_nets *net, uint32_t high_tsn, u
|
||||
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_tmit_chunk *chk,
|
||||
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
|
||||
@ -11635,6 +11679,68 @@ sctp_add_stream_reset_result(struct sctp_tmit_chunk *chk,
|
||||
return;
|
||||
}
|
||||
|
||||
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,
|
||||
@ -11732,20 +11838,86 @@ sctp_add_an_in_stream(struct sctp_tmit_chunk *chk,
|
||||
return;
|
||||
}
|
||||
|
||||
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 *stcb,
|
||||
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 *stcb,
|
||||
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,
|
||||
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,
|
||||
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 @@ sctp_lower_sosend(struct socket *so,
|
||||
/*-
|
||||
* 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);
|
||||
|
@ -170,18 +170,21 @@ void sctp_send_cwr(struct sctp_tcb *, struct sctp_nets *, uint32_t, uint8_t);
|
||||
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -4620,18 +4620,24 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
|
||||
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 optname, void *optval, size_t optsize,
|
||||
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 optname, void *optval, size_t optsize,
|
||||
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 optname, void *optval, size_t optsize,
|
||||
}
|
||||
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 optname, void *optval, size_t optsize,
|
||||
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;
|
||||
|
@ -1089,6 +1089,7 @@ sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
|
||||
#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 char *str SCTP_UNUSED, uint32_t a, uint32_
|
||||
|
||||
#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;
|
||||
|
Loading…
Reference in New Issue
Block a user