diff options
author | rrs <rrs@FreeBSD.org> | 2012-03-29 13:36:53 +0000 |
---|---|---|
committer | rrs <rrs@FreeBSD.org> | 2012-03-29 13:36:53 +0000 |
commit | ddfb5c5980b9ef2468b10441bf283e0215ee8340 (patch) | |
tree | e8748765c084ea52a09c31941d048f7e99380b2a /sys/netinet/sctp_usrreq.c | |
parent | f9d3dd9f9cd77a6a9c306d500cd4ab48dba96cc4 (diff) | |
download | FreeBSD-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.c | 277 |
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; |