forked from mirrors/linux
		
	sctp: implement receiver-side procedures for the Reconf Response Parameter
This patch is to implement Receiver-Side Procedures for the Re-configuration Response Parameter in rfc6525 section 5.2.7. sctp_process_strreset_resp would process the response for any kind of reconf request, and the stream reconf is applied only when the response result is success. Signed-off-by: Xin Long <lucien.xin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									c5c4ebb3ab
								
							
						
					
					
						commit
						11ae76e67a
					
				
					 3 changed files with 161 additions and 3 deletions
				
			
		| 
						 | 
					@ -305,6 +305,10 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in(
 | 
				
			||||||
				struct sctp_association *asoc,
 | 
									struct sctp_association *asoc,
 | 
				
			||||||
				union sctp_params param,
 | 
									union sctp_params param,
 | 
				
			||||||
				struct sctp_ulpevent **evp);
 | 
									struct sctp_ulpevent **evp);
 | 
				
			||||||
 | 
					struct sctp_chunk *sctp_process_strreset_resp(
 | 
				
			||||||
 | 
									struct sctp_association *asoc,
 | 
				
			||||||
 | 
									union sctp_params param,
 | 
				
			||||||
 | 
									struct sctp_ulpevent **evp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Prototypes for statetable processing. */
 | 
					/* Prototypes for statetable processing. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3881,9 +3881,9 @@ sctp_disposition_t sctp_sf_do_reconf(struct net *net,
 | 
				
			||||||
		else if (param.p->type == SCTP_PARAM_RESET_ADD_IN_STREAMS)
 | 
							else if (param.p->type == SCTP_PARAM_RESET_ADD_IN_STREAMS)
 | 
				
			||||||
			reply = sctp_process_strreset_addstrm_in(
 | 
								reply = sctp_process_strreset_addstrm_in(
 | 
				
			||||||
				(struct sctp_association *)asoc, param, &ev);
 | 
									(struct sctp_association *)asoc, param, &ev);
 | 
				
			||||||
		/* More handles for other types will be added here, by now it
 | 
							else if (param.p->type == SCTP_PARAM_RESET_RESPONSE)
 | 
				
			||||||
		 * just ignores other types.
 | 
								reply = sctp_process_strreset_resp(
 | 
				
			||||||
		 */
 | 
									(struct sctp_association *)asoc, param, &ev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (ev)
 | 
							if (ev)
 | 
				
			||||||
			sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
 | 
								sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -675,3 +675,157 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return chunk;
 | 
						return chunk;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct sctp_chunk *sctp_process_strreset_resp(
 | 
				
			||||||
 | 
									struct sctp_association *asoc,
 | 
				
			||||||
 | 
									union sctp_params param,
 | 
				
			||||||
 | 
									struct sctp_ulpevent **evp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sctp_strreset_resp *resp = param.v;
 | 
				
			||||||
 | 
						struct sctp_stream *stream = asoc->stream;
 | 
				
			||||||
 | 
						struct sctp_transport *t;
 | 
				
			||||||
 | 
						__u16 i, nums, flags = 0;
 | 
				
			||||||
 | 
						sctp_paramhdr_t *req;
 | 
				
			||||||
 | 
						__u32 result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0);
 | 
				
			||||||
 | 
						if (!req)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result = ntohl(resp->result);
 | 
				
			||||||
 | 
						if (result != SCTP_STRRESET_PERFORMED) {
 | 
				
			||||||
 | 
							/* if in progress, do nothing but retransmit */
 | 
				
			||||||
 | 
							if (result == SCTP_STRRESET_IN_PROGRESS)
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
							else if (result == SCTP_STRRESET_DENIED)
 | 
				
			||||||
 | 
								flags = SCTP_STREAM_RESET_DENIED;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								flags = SCTP_STREAM_RESET_FAILED;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) {
 | 
				
			||||||
 | 
							struct sctp_strreset_outreq *outreq;
 | 
				
			||||||
 | 
							__u16 *str_p = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							outreq = (struct sctp_strreset_outreq *)req;
 | 
				
			||||||
 | 
							nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (result == SCTP_STRRESET_PERFORMED) {
 | 
				
			||||||
 | 
								if (nums) {
 | 
				
			||||||
 | 
									str_p = outreq->list_of_streams;
 | 
				
			||||||
 | 
									for (i = 0; i < nums; i++)
 | 
				
			||||||
 | 
										stream->out[ntohs(str_p[i])].ssn = 0;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									for (i = 0; i < stream->outcnt; i++)
 | 
				
			||||||
 | 
										stream->out[i].ssn = 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								flags = SCTP_STREAM_RESET_OUTGOING_SSN;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < stream->outcnt; i++)
 | 
				
			||||||
 | 
								stream->out[i].state = SCTP_STREAM_OPEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
 | 
				
			||||||
 | 
								nums, str_p, GFP_ATOMIC);
 | 
				
			||||||
 | 
						} else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) {
 | 
				
			||||||
 | 
							struct sctp_strreset_inreq *inreq;
 | 
				
			||||||
 | 
							__u16 *str_p = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* if the result is performed, it's impossible for inreq */
 | 
				
			||||||
 | 
							if (result == SCTP_STRRESET_PERFORMED)
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							inreq = (struct sctp_strreset_inreq *)req;
 | 
				
			||||||
 | 
							nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							str_p = inreq->list_of_streams;
 | 
				
			||||||
 | 
							*evp = sctp_ulpevent_make_stream_reset_event(asoc, flags,
 | 
				
			||||||
 | 
								nums, str_p, GFP_ATOMIC);
 | 
				
			||||||
 | 
						} else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) {
 | 
				
			||||||
 | 
							struct sctp_strreset_resptsn *resptsn;
 | 
				
			||||||
 | 
							__u32 stsn, rtsn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* check for resptsn, as sctp_verify_reconf didn't do it*/
 | 
				
			||||||
 | 
							if (ntohs(param.p->length) != sizeof(*resptsn))
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							resptsn = (struct sctp_strreset_resptsn *)resp;
 | 
				
			||||||
 | 
							stsn = ntohl(resptsn->senders_next_tsn);
 | 
				
			||||||
 | 
							rtsn = ntohl(resptsn->receivers_next_tsn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (result == SCTP_STRRESET_PERFORMED) {
 | 
				
			||||||
 | 
								__u32 mtsn = sctp_tsnmap_get_max_tsn_seen(
 | 
				
			||||||
 | 
											&asoc->peer.tsn_map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn);
 | 
				
			||||||
 | 
								sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sctp_tsnmap_init(&asoc->peer.tsn_map,
 | 
				
			||||||
 | 
										 SCTP_TSN_MAP_INITIAL,
 | 
				
			||||||
 | 
										 stsn, GFP_ATOMIC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sctp_outq_free(&asoc->outqueue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								asoc->next_tsn = rtsn;
 | 
				
			||||||
 | 
								asoc->ctsn_ack_point = asoc->next_tsn - 1;
 | 
				
			||||||
 | 
								asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (i = 0; i < stream->outcnt; i++)
 | 
				
			||||||
 | 
									stream->out[i].ssn = 0;
 | 
				
			||||||
 | 
								for (i = 0; i < stream->incnt; i++)
 | 
				
			||||||
 | 
									stream->in[i].ssn = 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 0; i < stream->outcnt; i++)
 | 
				
			||||||
 | 
								stream->out[i].state = SCTP_STREAM_OPEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags,
 | 
				
			||||||
 | 
								stsn, rtsn, GFP_ATOMIC);
 | 
				
			||||||
 | 
						} else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) {
 | 
				
			||||||
 | 
							struct sctp_strreset_addstrm *addstrm;
 | 
				
			||||||
 | 
							__u16 number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							addstrm = (struct sctp_strreset_addstrm *)req;
 | 
				
			||||||
 | 
							nums = ntohs(addstrm->number_of_streams);
 | 
				
			||||||
 | 
							number = stream->outcnt - nums;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (result == SCTP_STRRESET_PERFORMED)
 | 
				
			||||||
 | 
								for (i = number; i < stream->outcnt; i++)
 | 
				
			||||||
 | 
									stream->out[i].state = SCTP_STREAM_OPEN;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								stream->outcnt = number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
 | 
				
			||||||
 | 
								0, nums, GFP_ATOMIC);
 | 
				
			||||||
 | 
						} else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) {
 | 
				
			||||||
 | 
							struct sctp_strreset_addstrm *addstrm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* if the result is performed, it's impossible for addstrm in
 | 
				
			||||||
 | 
							 * request.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (result == SCTP_STRRESET_PERFORMED)
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							addstrm = (struct sctp_strreset_addstrm *)req;
 | 
				
			||||||
 | 
							nums = ntohs(addstrm->number_of_streams);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*evp = sctp_ulpevent_make_stream_change_event(asoc, flags,
 | 
				
			||||||
 | 
								nums, 0, GFP_ATOMIC);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						asoc->strreset_outstanding--;
 | 
				
			||||||
 | 
						asoc->strreset_outseq++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* remove everything for this reconf request */
 | 
				
			||||||
 | 
						if (!asoc->strreset_outstanding) {
 | 
				
			||||||
 | 
							t = asoc->strreset_chunk->transport;
 | 
				
			||||||
 | 
							if (del_timer(&t->reconf_timer))
 | 
				
			||||||
 | 
								sctp_transport_put(t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sctp_chunk_put(asoc->strreset_chunk);
 | 
				
			||||||
 | 
							asoc->strreset_chunk = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue