diff options
Diffstat (limited to 'net/sctp/stream.c')
| -rw-r--r-- | net/sctp/stream.c | 79 | 
1 files changed, 66 insertions, 13 deletions
diff --git a/net/sctp/stream.c b/net/sctp/stream.c index a11db21dc8a0..76ea66be0bbe 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -64,7 +64,7 @@ static void sctp_stream_outq_migrate(struct sctp_stream *stream,  		 */  		/* Mark as failed send. */ -		sctp_chunk_fail(ch, SCTP_ERROR_INV_STRM); +		sctp_chunk_fail(ch, (__force __u32)SCTP_ERROR_INV_STRM);  		if (asoc->peer.prsctp_capable &&  		    SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags))  			asoc->sent_cnt_removable--; @@ -254,6 +254,30 @@ static int sctp_send_reconf(struct sctp_association *asoc,  	return retval;  } +static bool sctp_stream_outq_is_empty(struct sctp_stream *stream, +				      __u16 str_nums, __be16 *str_list) +{ +	struct sctp_association *asoc; +	__u16 i; + +	asoc = container_of(stream, struct sctp_association, stream); +	if (!asoc->outqueue.out_qlen) +		return true; + +	if (!str_nums) +		return false; + +	for (i = 0; i < str_nums; i++) { +		__u16 sid = ntohs(str_list[i]); + +		if (stream->out[sid].ext && +		    !list_empty(&stream->out[sid].ext->outq)) +			return false; +	} + +	return true; +} +  int sctp_send_reset_streams(struct sctp_association *asoc,  			    struct sctp_reset_streams *params)  { @@ -317,6 +341,11 @@ int sctp_send_reset_streams(struct sctp_association *asoc,  	for (i = 0; i < str_nums; i++)  		nstr_list[i] = htons(str_list[i]); +	if (out && !sctp_stream_outq_is_empty(stream, str_nums, nstr_list)) { +		retval = -EAGAIN; +		goto out; +	} +  	chunk = sctp_make_strreset_req(asoc, str_nums, nstr_list, out, in);  	kfree(nstr_list); @@ -377,6 +406,9 @@ int sctp_send_reset_assoc(struct sctp_association *asoc)  	if (asoc->strreset_outstanding)  		return -EINPROGRESS; +	if (!sctp_outq_is_empty(&asoc->outqueue)) +		return -EAGAIN; +  	chunk = sctp_make_strreset_tsnreq(asoc);  	if (!chunk)  		return -ENOMEM; @@ -563,7 +595,7 @@ struct sctp_chunk *sctp_process_strreset_outreq(  		flags = SCTP_STREAM_RESET_INCOMING_SSN;  	} -	nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2; +	nums = (ntohs(param.p->length) - sizeof(*outreq)) / sizeof(__u16);  	if (nums) {  		str_p = outreq->list_of_streams;  		for (i = 0; i < nums; i++) { @@ -627,7 +659,7 @@ struct sctp_chunk *sctp_process_strreset_inreq(  		goto out;  	} -	nums = (ntohs(param.p->length) - sizeof(*inreq)) / 2; +	nums = (ntohs(param.p->length) - sizeof(*inreq)) / sizeof(__u16);  	str_p = inreq->list_of_streams;  	for (i = 0; i < nums; i++) {  		if (ntohs(str_p[i]) >= stream->outcnt) { @@ -636,6 +668,12 @@ struct sctp_chunk *sctp_process_strreset_inreq(  		}  	} +	if (!sctp_stream_outq_is_empty(stream, nums, str_p)) { +		result = SCTP_STRRESET_IN_PROGRESS; +		asoc->strreset_inseq--; +		goto err; +	} +  	chunk = sctp_make_strreset_req(asoc, nums, str_p, 1, 0);  	if (!chunk)  		goto out; @@ -687,12 +725,18 @@ struct sctp_chunk *sctp_process_strreset_tsnreq(  		i = asoc->strreset_inseq - request_seq - 1;  		result = asoc->strreset_result[i];  		if (result == SCTP_STRRESET_PERFORMED) { -			next_tsn = asoc->next_tsn; +			next_tsn = asoc->ctsn_ack_point + 1;  			init_tsn =  				sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1;  		}  		goto err;  	} + +	if (!sctp_outq_is_empty(&asoc->outqueue)) { +		result = SCTP_STRRESET_IN_PROGRESS; +		goto err; +	} +  	asoc->strreset_inseq++;  	if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) @@ -703,9 +747,10 @@ struct sctp_chunk *sctp_process_strreset_tsnreq(  		goto out;  	} -	/* G3: The same processing as though a SACK chunk with no gap report -	 *     and a cumulative TSN ACK of the Sender's Next TSN minus 1 were -	 *     received MUST be performed. +	/* G4: The same processing as though a FWD-TSN chunk (as defined in +	 *     [RFC3758]) with all streams affected and a new cumulative TSN +	 *     ACK of the Receiver's Next TSN minus 1 were received MUST be +	 *     performed.  	 */  	max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);  	sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen); @@ -720,10 +765,9 @@ struct sctp_chunk *sctp_process_strreset_tsnreq(  	sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL,  			 init_tsn, GFP_ATOMIC); -	/* G4: The same processing as though a FWD-TSN chunk (as defined in -	 *     [RFC3758]) with all streams affected and a new cumulative TSN -	 *     ACK of the Receiver's Next TSN minus 1 were received MUST be -	 *     performed. +	/* G3: The same processing as though a SACK chunk with no gap report +	 *     and a cumulative TSN ACK of the Sender's Next TSN minus 1 were +	 *     received MUST be performed.  	 */  	sctp_outq_free(&asoc->outqueue); @@ -927,7 +971,8 @@ struct sctp_chunk *sctp_process_strreset_resp(  		outreq = (struct sctp_strreset_outreq *)req;  		str_p = outreq->list_of_streams; -		nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2; +		nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / +		       sizeof(__u16);  		if (result == SCTP_STRRESET_PERFORMED) {  			if (nums) { @@ -956,7 +1001,8 @@ struct sctp_chunk *sctp_process_strreset_resp(  		inreq = (struct sctp_strreset_inreq *)req;  		str_p = inreq->list_of_streams; -		nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2; +		nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / +		       sizeof(__u16);  		*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,  			nums, str_p, GFP_ATOMIC); @@ -975,6 +1021,7 @@ struct sctp_chunk *sctp_process_strreset_resp(  		if (result == SCTP_STRRESET_PERFORMED) {  			__u32 mtsn = sctp_tsnmap_get_max_tsn_seen(  						&asoc->peer.tsn_map); +			LIST_HEAD(temp);  			sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn);  			sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); @@ -983,7 +1030,13 @@ struct sctp_chunk *sctp_process_strreset_resp(  					 SCTP_TSN_MAP_INITIAL,  					 stsn, GFP_ATOMIC); +			/* Clean up sacked and abandoned queues only. As the +			 * out_chunk_list may not be empty, splice it to temp, +			 * then get it back after sctp_outq_free is done. +			 */ +			list_splice_init(&asoc->outqueue.out_chunk_list, &temp);  			sctp_outq_free(&asoc->outqueue); +			list_splice_init(&temp, &asoc->outqueue.out_chunk_list);  			asoc->next_tsn = rtsn;  			asoc->ctsn_ack_point = asoc->next_tsn - 1;  |