From owner-svn-src-all@FreeBSD.ORG Thu Mar 29 13:36:53 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id CB95C1065675; Thu, 29 Mar 2012 13:36:53 +0000 (UTC) (envelope-from rrs@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id B4BF38FC12; Thu, 29 Mar 2012 13:36:53 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q2TDar0s095127; Thu, 29 Mar 2012 13:36:53 GMT (envelope-from rrs@svn.freebsd.org) Received: (from rrs@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q2TDargs095114; Thu, 29 Mar 2012 13:36:53 GMT (envelope-from rrs@svn.freebsd.org) Message-Id: <201203291336.q2TDargs095114@svn.freebsd.org> From: Randall Stewart Date: Thu, 29 Mar 2012 13:36:53 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r233660 - head/sys/netinet X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 29 Mar 2012 13:36:53 -0000 Author: rrs Date: Thu Mar 29 13:36:53 2012 New Revision: 233660 URL: http://svn.freebsd.org/changeset/base/233660 Log: Make stream our stream reset implementation compliant to RFC6525. MFC after: 1 month Modified: head/sys/netinet/sctp.h head/sys/netinet/sctp_constants.h head/sys/netinet/sctp_header.h head/sys/netinet/sctp_input.c head/sys/netinet/sctp_output.c head/sys/netinet/sctp_output.h head/sys/netinet/sctp_pcb.h head/sys/netinet/sctp_peeloff.c head/sys/netinet/sctp_structs.h head/sys/netinet/sctp_uio.h head/sys/netinet/sctp_usrreq.c head/sys/netinet/sctputil.c head/sys/netinet/sctputil.h Modified: head/sys/netinet/sctp.h ============================================================================== --- head/sys/netinet/sctp.h Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp.h Thu Mar 29 13:36:53 2012 (r233660) @@ -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 */ Modified: head/sys/netinet/sctp_constants.h ============================================================================== --- head/sys/netinet/sctp_constants.h Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_constants.h Thu Mar 29 13:36:53 2012 (r233660) @@ -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 Modified: head/sys/netinet/sctp_header.h ============================================================================== --- head/sys/netinet/sctp_header.h Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_header.h Thu Mar 29 13:36:53 2012 (r233660) @@ -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 */ Modified: head/sys/netinet/sctp_input.c ============================================================================== --- head/sys/netinet/sctp_input.c Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_input.c Thu Mar 29 13:36:53 2012 (r233660) @@ -2790,6 +2790,7 @@ sctp_handle_cookie_echo(struct mbuf *m, 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 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_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 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 /* 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_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 /* 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 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 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 sc * 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 sc /* 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 sc 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 sc } } +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; Modified: head/sys/netinet/sctp_output.c ============================================================================== --- head/sys/netinet/sctp_output.c Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_output.c Thu Mar 29 13:36:53 2012 (r233660) @@ -11716,7 +11716,7 @@ sctp_add_stream_reset_result_tsn(struct } 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 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 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 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 asoc->stream_reset_outstanding++; } asoc->str_reset = chk; - /* insert the chunk for sending */ TAILQ_INSERT_TAIL(&asoc->control_send_queue, chk, Modified: head/sys/netinet/sctp_output.h ============================================================================== --- head/sys/netinet/sctp_output.h Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_output.h Thu Mar 29 13:36:53 2012 (r233660) @@ -194,15 +194,13 @@ sctp_add_stream_reset_result_tsn(struct 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, Modified: head/sys/netinet/sctp_pcb.h ============================================================================== --- head/sys/netinet/sctp_pcb.h Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_pcb.h Thu Mar 29 13:36:53 2012 (r233660) @@ -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; Modified: head/sys/netinet/sctp_peeloff.c ============================================================================== --- head/sys/netinet/sctp_peeloff.c Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_peeloff.c Thu Mar 29 13:36:53 2012 (r233660) @@ -127,6 +127,7 @@ sctp_do_peeloff(struct socket *head, str 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, sc 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 */ Modified: head/sys/netinet/sctp_structs.h ============================================================================== --- head/sys/netinet/sctp_structs.h Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_structs.h Thu Mar 29 13:36:53 2012 (r233660) @@ -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; /* Modified: head/sys/netinet/sctp_uio.h ============================================================================== --- head/sys/netinet/sctp_uio.h Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_uio.h Thu Mar 29 13:36:53 2012 (r233660) @@ -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; Modified: head/sys/netinet/sctp_usrreq.c ============================================================================== --- head/sys/netinet/sctp_usrreq.c Thu Mar 29 13:01:29 2012 (r233659) +++ head/sys/netinet/sctp_usrreq.c Thu Mar 29 13:36:53 2012 (r233660) @@ -4088,17 +4088,52 @@ sctp_setopt(struct socket *so, int optna } 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 optna } 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 optna 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; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***