summaryrefslogtreecommitdiffstats
path: root/sys/netinet
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2012-03-29 13:36:53 +0000
committerrrs <rrs@FreeBSD.org>2012-03-29 13:36:53 +0000
commitddfb5c5980b9ef2468b10441bf283e0215ee8340 (patch)
treee8748765c084ea52a09c31941d048f7e99380b2a /sys/netinet
parentf9d3dd9f9cd77a6a9c306d500cd4ab48dba96cc4 (diff)
downloadFreeBSD-src-ddfb5c5980b9ef2468b10441bf283e0215ee8340.zip
FreeBSD-src-ddfb5c5980b9ef2468b10441bf283e0215ee8340.tar.gz
Make stream our stream reset implementation
compliant to RFC6525. MFC after: 1 month
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/sctp.h18
-rw-r--r--sys/netinet/sctp_constants.h3
-rw-r--r--sys/netinet/sctp_header.h2
-rw-r--r--sys/netinet/sctp_input.c138
-rw-r--r--sys/netinet/sctp_output.c117
-rw-r--r--sys/netinet/sctp_output.h10
-rw-r--r--sys/netinet/sctp_pcb.h1
-rw-r--r--sys/netinet/sctp_peeloff.c2
-rw-r--r--sys/netinet/sctp_structs.h5
-rw-r--r--sys/netinet/sctp_uio.h73
-rw-r--r--sys/netinet/sctp_usrreq.c277
-rw-r--r--sys/netinet/sctputil.c112
-rw-r--r--sys/netinet/sctputil.h6
13 files changed, 549 insertions, 215 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h
index 192a79b..d815891 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 55e3be9..e53d699 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 9c285a7..1d92b65 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 bd3f26d..1f9ee89 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 daee937..603bd7a 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 01b2821..ba30947 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 d8e7484..325f768 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 87f586b..fb9391a 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 8685dfe..3711691 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 ec24984..87af9bf 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 569ec9e..fd9c022 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,129 +4164,137 @@ 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) {
- send_in = 1;
- } else if (strrst->strrst_flags == SCTP_RESET_LOCAL_SEND) {
- send_out = 1;
- } else if (strrst->strrst_flags == SCTP_RESET_BOTH) {
+ if (strrst->srs_flags & SCTP_STREAM_RESET_INCOMING) {
send_in = 1;
+ }
+ if (strrst->srs_flags & SCTP_STREAM_RESET_OUTGOING) {
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 */
+ }
+ 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->srs_number_streams; i++) {
+ if ((send_in) &&
+ (strrst->srs_stream_list[i] > stcb->asoc.streamincnt)) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
error = EINVAL;
- goto skip_stuff;
+ break;
}
- if (stcb->asoc.stream_reset_outstanding) {
- error = EBUSY;
- goto skip_stuff;
+ if ((send_out) &&
+ (strrst->srs_stream_list[i] > stcb->asoc.streamoutcnt)) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ error = EINVAL;
+ break;
}
+ }
+ if (error) {
+ 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);
+
+ 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 */
- addstrmcnt = strrst->strrst_num_streams;
- if ((int)(addstrmcnt + stcb->asoc.streamoutcnt) > 0xffff) {
+ 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 ((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 {
- 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++) {
- if ((send_in) &&
+ if (stradd->sas_instrms) {
+ int cnt;
- (strrst->strrst_list[i] > stcb->asoc.streamincnt)) {
- SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ 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 get_out;
+ goto skip_stuff;
}
- if ((send_out) &&
- (strrst->strrst_list[i] > stcb->asoc.streamoutcnt)) {
- SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ if (cnt > (int)stcb->asoc.max_inbound_streams) {
+ /* More than you are allowed */
error = EINVAL;
- goto get_out;
+ 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:
- if (error) {
- get_out:
+ 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;
}
- 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);
-
+ 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;
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index fde7d58..675c93d 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,97 @@ 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)) {
+ /* 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);
+}
+
+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)) {
@@ -3262,6 +3323,7 @@ sctp_notify_stream_reset_add(struct sctp_tcb *stcb, int number_entries, int flag
}
+
static void
sctp_notify_stream_reset(struct sctp_tcb *stcb,
int number_entries, uint16_t * list, int flag)
@@ -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 bbaaaa4..495b17a 100644
--- a/sys/netinet/sctputil.h
+++ b/sys/netinet/sctputil.h
@@ -88,6 +88,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 *);
OpenPOWER on IntegriCloud