summaryrefslogtreecommitdiffstats
path: root/sys/netinet/sctp_usrreq.c
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/sctp_usrreq.c
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/sctp_usrreq.c')
-rw-r--r--sys/netinet/sctp_usrreq.c277
1 files changed, 163 insertions, 114 deletions
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;
OpenPOWER on IntegriCloud