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:
rrs 2015-07-22 11:30:37 +00:00
parent b2a714f330
commit 606fc6cd55
7 changed files with 373 additions and 63 deletions

View File

@ -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); sctp_reset_in_stream(stcb, liste->number_entries, liste->list_of_streams);
TAILQ_REMOVE(&asoc->resetHead, liste, next_resp); 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); SCTP_FREE(liste, SCTP_M_STRESET);
/* sa_ignore FREED_MEMORY */ /* sa_ignore FREED_MEMORY */
liste = TAILQ_FIRST(&asoc->resetHead); liste = TAILQ_FIRST(&asoc->resetHead);

View File

@ -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); sctp_free_a_strmoq(stcb, sp, SCTP_SO_NOT_LOCKED);
/* sa_ignore FREED_MEMORY */ /* sa_ignore FREED_MEMORY */
} }
outs->state = SCTP_STREAM_CLOSED;
} }
} }
/* cut back the count */ /* cut back the count */
asoc->pre_open_streams = newcnt; asoc->pre_open_streams = newcnt;
} }
SCTP_TCB_SEND_UNLOCK(stcb); 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 */ /* EY - nr_sack: initialize highest tsn in nr_mapping_array */
asoc->highest_tsn_inside_nr_map = asoc->highest_tsn_inside_map; asoc->highest_tsn_inside_nr_map = asoc->highest_tsn_inside_map;
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MAP_LOGGING_ENABLE) { 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); 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 * struct sctp_stream_reset_request *
sctp_find_stream_reset(struct sctp_tcb *stcb, uint32_t seq, struct sctp_tmit_chunk **bchk) 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); type = ntohs(req_param->ph.param_type);
lparm_len = ntohs(req_param->ph.param_length); lparm_len = ntohs(req_param->ph.param_length);
if (type == SCTP_STR_RESET_OUT_REQUEST) { if (type == SCTP_STR_RESET_OUT_REQUEST) {
int no_clear = 0;
req_out_param = (struct sctp_stream_reset_out_request *)req_param; 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); number_entries = (lparm_len - sizeof(struct sctp_stream_reset_out_request)) / sizeof(uint16_t);
asoc->stream_reset_out_is_outstanding = 0; 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); sctp_reset_out_streams(stcb, number_entries, req_out_param->list_of_streams);
} else if (action == SCTP_STREAM_RESET_RESULT_DENIED) { } 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); 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 { } else {
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_FAILED_OUT, stcb, number_entries, req_out_param->list_of_streams, SCTP_SO_NOT_LOCKED); 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) { } else if (type == SCTP_STR_RESET_IN_REQUEST) {
req_in_param = (struct sctp_stream_reset_in_request *)req_param; 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); 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--; asoc->stream_reset_outstanding--;
if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) { if (action == SCTP_STREAM_RESET_RESULT_PERFORMED) {
/* Put the new streams into effect */ /* 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); sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, 0);
} else if (action == SCTP_STREAM_RESET_RESULT_DENIED) { } else if (action == SCTP_STREAM_RESET_RESULT_DENIED) {
sctp_notify_stream_reset_add(stcb, stcb->asoc.streamincnt, stcb->asoc.streamoutcnt, 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); 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) { } else if (stcb->asoc.stream_reset_out_is_outstanding == 0) {
len = ntohs(req->ph.param_length); len = ntohs(req->ph.param_length);
number_entries = ((len - sizeof(struct sctp_stream_reset_in_request)) / sizeof(uint16_t)); number_entries = ((len - sizeof(struct sctp_stream_reset_in_request)) / sizeof(uint16_t));
if (number_entries) {
for (i = 0; i < number_entries; i++) { for (i = 0; i < number_entries; i++) {
temp = ntohs(req->list_of_streams[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; 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; 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 { } else {
/* Can't do it, since we have sent one out */ /* Can't do it, since we have sent one out */
asoc->last_reset_action[0] = SCTP_STREAM_RESET_RESULT_ERR_IN_PROGRESS; 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]); sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]);
asoc->str_reset_seq_in++; asoc->str_reset_seq_in++;
} else if (asoc->str_reset_seq_in - 1 == seq) { } else if (asoc->str_reset_seq_in - 1 == seq) {
@ -3775,6 +3832,7 @@ sctp_handle_str_reset_request_in(struct sctp_tcb *stcb,
} else { } else {
sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_RESULT_ERR_BAD_SEQNO); sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_RESULT_ERR_BAD_SEQNO);
} }
sctp_send_stream_reset_out_if_possible(stcb);
} }
static int 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]); sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]);
return; return;
} }
liste->seq = seq;
liste->tsn = tsn; liste->tsn = tsn;
liste->number_entries = number_entries; liste->number_entries = number_entries;
memcpy(&liste->list_of_streams, req->list_of_streams, number_entries * sizeof(uint16_t)); memcpy(&liste->list_of_streams, req->list_of_streams, number_entries * sizeof(uint16_t));
TAILQ_INSERT_TAIL(&asoc->resetHead, liste, next_resp); 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]); sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]);
asoc->str_reset_seq_in++; 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; mychk += num_stream;
if (mychk < 0x10000) { if (mychk < 0x10000) {
stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_RESULT_PERFORMED; 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; stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_RESULT_DENIED;
} }
} else { } else {

View File

@ -7162,6 +7162,10 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb,
} }
atomic_subtract_int(&asoc->stream_queue_cnt, 1); atomic_subtract_int(&asoc->stream_queue_cnt, 1);
TAILQ_REMOVE(&strq->outqueue, sp, next); 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); stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up);
if (sp->net) { if (sp->net) {
sctp_free_remote_addr(sp->net); sctp_free_remote_addr(sp->net);
@ -7560,6 +7564,10 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb,
send_lock_up = 1; send_lock_up = 1;
} }
TAILQ_REMOVE(&strq->outqueue, sp, next); 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); stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp, send_lock_up);
if (sp->net) { if (sp->net) {
sctp_free_remote_addr(sp->net); sctp_free_remote_addr(sp->net);
@ -7787,7 +7795,7 @@ sctp_med_chunk_output(struct sctp_inpcb *inp,
#endif #endif
SCTP_TCB_LOCK_ASSERT(stcb); SCTP_TCB_LOCK_ASSERT(stcb);
hbflag = 0; hbflag = 0;
if ((control_only) || (asoc->stream_reset_outstanding)) if (control_only)
no_data_chunks = 1; no_data_chunks = 1;
else else
no_data_chunks = 0; no_data_chunks = 0;
@ -9856,7 +9864,9 @@ sctp_chunk_output(struct sctp_inpcb *inp,
unsigned int tot_frs = 0; unsigned int tot_frs = 0;
asoc = &stcb->asoc; asoc = &stcb->asoc;
do_it_again:
/* The Nagle algorithm is only applied when handling a send call. */ /* 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 (from_where == SCTP_OUTPUT_FROM_USR_SEND) {
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NODELAY)) { if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NODELAY)) {
nagle_on = 0; nagle_on = 0;
@ -10092,6 +10102,12 @@ sctp_chunk_output(struct sctp_inpcb *inp,
*/ */
if (stcb->asoc.ecn_echo_cnt_onq) if (stcb->asoc.ecn_echo_cnt_onq)
sctp_fix_ecn_echo(asoc); 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; 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++; asoc->ctrl_queue_cnt++;
} }
void static int
sctp_add_stream_reset_out(struct sctp_tmit_chunk *chk, sctp_add_stream_reset_out(struct sctp_tcb *stcb, struct sctp_tmit_chunk *chk,
int number_entries, uint16_t * list,
uint32_t seq, uint32_t resp_seq, uint32_t last_sent) uint32_t seq, uint32_t resp_seq, uint32_t last_sent)
{ {
uint16_t len, old_len, i; uint16_t len, old_len, i;
struct sctp_stream_reset_out_request *req_out; struct sctp_stream_reset_out_request *req_out;
struct sctp_chunkhdr *ch; struct sctp_chunkhdr *ch;
int at;
int number_entries = 0;
ch = mtod(chk->data, struct sctp_chunkhdr *); ch = mtod(chk->data, struct sctp_chunkhdr *);
old_len = len = SCTP_SIZE32(ntohs(ch->chunk_length)); old_len = len = SCTP_SIZE32(ntohs(ch->chunk_length));
/* get to new offset for the param. */ /* get to new offset for the param. */
req_out = (struct sctp_stream_reset_out_request *)((caddr_t)ch + len); req_out = (struct sctp_stream_reset_out_request *)((caddr_t)ch + len);
/* now how long will this param be? */ /* 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)); 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_type = htons(SCTP_STR_RESET_OUT_REQUEST);
req_out->ph.param_length = htons(len); req_out->ph.param_length = htons(len);
req_out->request_seq = htonl(seq); req_out->request_seq = htonl(seq);
req_out->response_seq = htonl(resp_seq); req_out->response_seq = htonl(resp_seq);
req_out->send_reset_at_tsn = htonl(last_sent); req_out->send_reset_at_tsn = htonl(last_sent);
at = 0;
if (number_entries) { if (number_entries) {
for (i = 0; i < number_entries; i++) { for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
req_out->list_of_streams[i] = htons(list[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) { 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->book_size_scale = 0;
chk->send_size = SCTP_SIZE32(chk->book_size); chk->send_size = SCTP_SIZE32(chk->book_size);
SCTP_BUF_LEN(chk->data) = chk->send_size; SCTP_BUF_LEN(chk->data) = chk->send_size;
return; return (1);
} }
static void static void
@ -11635,6 +11679,68 @@ sctp_add_stream_reset_result(struct sctp_tmit_chunk *chk,
return; 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 void
sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *chk, sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *chk,
uint32_t resp_seq, uint32_t result, uint32_t resp_seq, uint32_t result,
@ -11732,20 +11838,86 @@ sctp_add_an_in_stream(struct sctp_tmit_chunk *chk,
return; 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 int
sctp_send_str_reset_req(struct sctp_tcb *stcb, sctp_send_str_reset_req(struct sctp_tcb *stcb,
uint16_t number_entries, uint16_t * list, uint16_t number_entries, uint16_t * list,
uint8_t send_out_req,
uint8_t send_in_req, uint8_t send_in_req,
uint8_t send_tsn_req, uint8_t send_tsn_req,
uint8_t add_stream, uint8_t add_stream,
uint16_t adding_o, uint16_t adding_o,
uint16_t adding_i, uint8_t peer_asked) uint16_t adding_i, uint8_t peer_asked)
{ {
struct sctp_association *asoc; struct sctp_association *asoc;
struct sctp_tmit_chunk *chk; struct sctp_tmit_chunk *chk;
struct sctp_chunkhdr *ch; struct sctp_chunkhdr *ch;
int can_send_out_req = 0;
uint32_t seq; uint32_t seq;
asoc = &stcb->asoc; 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); SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EBUSY);
return (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)) { (add_stream == 0)) {
/* nothing to do */ /* nothing to do */
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL); SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (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 */ /* error, can't do that */
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL); SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EINVAL);
return (EINVAL); return (EINVAL);
} else if (send_in_req) {
can_send_out_req = 1;
} }
if (number_entries > (MCLBYTES - if (number_entries > (MCLBYTES -
SCTP_MIN_OVERHEAD - SCTP_MIN_OVERHEAD -
@ -11813,13 +11987,15 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
SCTP_BUF_LEN(chk->data) = chk->send_size; SCTP_BUF_LEN(chk->data) = chk->send_size;
seq = stcb->asoc.str_reset_seq_out; seq = stcb->asoc.str_reset_seq_out;
if (send_out_req) { if (can_send_out_req) {
sctp_add_stream_reset_out(chk, number_entries, list, int ret;
seq, (stcb->asoc.str_reset_seq_in - 1), (stcb->asoc.sending_seq - 1));
asoc->stream_reset_out_is_outstanding = 1; ret = sctp_add_stream_reset_out(stcb, chk, seq, (stcb->asoc.str_reset_seq_in - 1), (stcb->asoc.sending_seq - 1));
if (ret) {
seq++; seq++;
asoc->stream_reset_outstanding++; asoc->stream_reset_outstanding++;
} }
}
if ((add_stream & 1) && if ((add_stream & 1) &&
((stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt) < adding_o)) { ((stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt) < adding_o)) {
/* Need to allocate more */ /* Need to allocate more */
@ -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].next_sequence_send = oldstream[i].next_sequence_send;
stcb->asoc.strmout[i].last_msg_incomplete = oldstream[i].last_msg_incomplete; stcb->asoc.strmout[i].last_msg_incomplete = oldstream[i].last_msg_incomplete;
stcb->asoc.strmout[i].stream_no = i; 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]); stcb->asoc.ss_functions.sctp_ss_init_stream(&stcb->asoc.strmout[i], &oldstream[i]);
/* now anything on those queues? */ /* now anything on those queues? */
TAILQ_FOREACH_SAFE(sp, &oldstream[i].outqueue, next, nsp) { 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].stream_no = i;
stcb->asoc.strmout[i].last_msg_incomplete = 0; stcb->asoc.strmout[i].last_msg_incomplete = 0;
stcb->asoc.ss_functions.sctp_ss_init_stream(&stcb->asoc.strmout[i], NULL); 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; stcb->asoc.strm_realoutsize = stcb->asoc.streamoutcnt + adding_o;
SCTP_FREE(oldstream, SCTP_M_STRMO); SCTP_FREE(oldstream, SCTP_M_STRMO);
@ -12499,12 +12677,24 @@ sctp_lower_sosend(struct socket *so,
SCTP_ASOC_CREATE_UNLOCK(inp); SCTP_ASOC_CREATE_UNLOCK(inp);
create_lock_applied = 0; 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. * Can't queue any data while stream reset is underway.
*/ */
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EAGAIN); if (asoc->strmout[srcv->sinfo_stream].state > SCTP_STREAM_OPEN) {
error = EAGAIN; error = EAGAIN;
} else {
error = EINVAL;
}
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, error);
goto out_unlocked; goto out_unlocked;
} }
if ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT) || if ((SCTP_GET_STATE(asoc) == SCTP_STATE_COOKIE_WAIT) ||
@ -12643,13 +12833,6 @@ sctp_lower_sosend(struct socket *so,
SCTP_TCB_UNLOCK(stcb); SCTP_TCB_UNLOCK(stcb);
hold_tcblock = 0; 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) { if (asoc->strmout == NULL) {
/* huh? software error */ /* huh? software error */
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EFAULT); 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. * Ok, Nagle is set on and we have data outstanding.
* Don't send anything and let SACKs drive out the * 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) { if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_NAGLE_LOGGING_ENABLE) {
sctp_log_nagle_event(stcb, SCTP_NAGLE_APPLIED); sctp_log_nagle_event(stcb, SCTP_NAGLE_APPLIED);

View File

@ -170,18 +170,21 @@ void sctp_send_cwr(struct sctp_tcb *, struct sctp_nets *, uint32_t, uint8_t);
void void
sctp_add_stream_reset_out(struct sctp_tmit_chunk *, sctp_add_stream_reset_result(struct sctp_tmit_chunk *, uint32_t, uint32_t);
int, uint16_t *, uint32_t, uint32_t, uint32_t);
void 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 void
sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *, sctp_add_stream_reset_result_tsn(struct sctp_tmit_chunk *,
uint32_t, uint32_t, uint32_t, uint32_t); uint32_t, uint32_t, uint32_t, uint32_t);
int
sctp_send_stream_reset_out_if_possible(struct sctp_tcb *);
int 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); uint8_t, uint8_t, uint8_t, uint16_t, uint16_t, uint8_t);
void void

View File

@ -76,6 +76,7 @@ TAILQ_HEAD(sctpnetlisthead, sctp_nets);
struct sctp_stream_reset_list { struct sctp_stream_reset_list {
TAILQ_ENTRY(sctp_stream_reset_list) next_resp; TAILQ_ENTRY(sctp_stream_reset_list) next_resp;
uint32_t seq;
uint32_t tsn; uint32_t tsn;
uint32_t number_entries; uint32_t number_entries;
uint16_t list_of_streams[]; uint16_t list_of_streams[];
@ -580,11 +581,20 @@ union scheduling_parameters {
struct ss_fb fb; 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 */ /* This struct is used to track the traffic on outbound streams */
struct sctp_stream_out { struct sctp_stream_out {
struct sctp_streamhead outqueue; struct sctp_streamhead outqueue;
union scheduling_parameters ss_params; 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) #if defined(SCTP_DETAILED_STR_STATS)
uint32_t abandoned_unsent[SCTP_PR_SCTP_MAX + 1]; uint32_t abandoned_unsent[SCTP_PR_SCTP_MAX + 1];
uint32_t abandoned_sent[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 stream_no;
uint16_t next_sequence_send; /* next one I expect to send out */ uint16_t next_sequence_send; /* next one I expect to send out */
uint8_t last_msg_incomplete; uint8_t last_msg_incomplete;
uint8_t state;
}; };
/* used to keep track of the addresses yet to try to add/delete */ /* 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 hb_random_idx;
uint8_t default_dscp; uint8_t default_dscp;
uint8_t asconf_del_pending; /* asconf delete last addr pending */ 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 * 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 * together to cross check against the bit that we have yet to

View File

@ -4620,18 +4620,24 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
SCTP_TCB_UNLOCK(stcb); SCTP_TCB_UNLOCK(stcb);
break; break;
} }
if (strrst->srs_flags & SCTP_STREAM_RESET_INCOMING) {
send_in = 1;
if (stcb->asoc.stream_reset_outstanding) { if (stcb->asoc.stream_reset_outstanding) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EALREADY); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EALREADY);
error = EALREADY; error = EALREADY;
SCTP_TCB_UNLOCK(stcb); SCTP_TCB_UNLOCK(stcb);
break; break;
} }
if (strrst->srs_flags & SCTP_STREAM_RESET_INCOMING) {
send_in = 1;
} }
if (strrst->srs_flags & SCTP_STREAM_RESET_OUTGOING) { if (strrst->srs_flags & SCTP_STREAM_RESET_OUTGOING) {
send_out = 1; 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)) { if ((send_in == 0) && (send_out == 0)) {
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
error = EINVAL; error = EINVAL;
@ -4656,11 +4662,38 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
SCTP_TCB_UNLOCK(stcb); SCTP_TCB_UNLOCK(stcb);
break; break;
} }
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, error = sctp_send_str_reset_req(stcb, strrst->srs_number_streams,
strrst->srs_stream_list, strrst->srs_stream_list,
send_out, send_in, 0, 0, 0, 0, 0); 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); SCTP_TCB_UNLOCK(stcb);
break; break;
} }
@ -4730,7 +4763,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
goto skip_stuff; 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); sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED);
skip_stuff: skip_stuff:
SCTP_TCB_UNLOCK(stcb); SCTP_TCB_UNLOCK(stcb);
@ -4738,6 +4771,7 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize,
} }
case SCTP_RESET_ASSOC: case SCTP_RESET_ASSOC:
{ {
int i;
uint32_t *value; uint32_t *value;
SCTP_CHECK_AND_CAST(value, optval, uint32_t, optsize); 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); SCTP_TCB_UNLOCK(stcb);
break; 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_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_STRRST_REQ, SCTP_SO_LOCKED);
SCTP_TCB_UNLOCK(stcb); SCTP_TCB_UNLOCK(stcb);
break; break;

View File

@ -1089,6 +1089,7 @@ sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
#endif #endif
asoc->strmout[i].stream_no = i; asoc->strmout[i].stream_no = i;
asoc->strmout[i].last_msg_incomplete = 0; 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_stream(&asoc->strmout[i], NULL);
} }
asoc->ss_functions.sctp_ss_init(stcb, asoc, 0); 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 #endif
static void 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) const struct sockaddr *sa SCTP_UNUSED, void *ctx SCTP_UNUSED)
{ {
struct ip *iph; struct ip *iph;