sctp: implement receiver-side procedures for the Outgoing SSN Reset Request Parameter
authorXin Long <lucien.xin@gmail.com>
Fri, 17 Feb 2017 04:45:39 +0000 (12:45 +0800)
committerDavid S. Miller <davem@davemloft.net>
Sun, 19 Feb 2017 23:17:59 +0000 (18:17 -0500)
This patch is to implement Receiver-Side Procedures for the Outgoing
SSN Reset Request Parameter described in rfc6525 section 5.2.2.

Note that some checks must be after request_seq check, as even those
checks fail, strreset_inseq still has to be increase by 1.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/sm.h
net/sctp/stream.c

index 8a85dd7fc1ecc8722d8c0234f6151c008cd0e9df..c0f9bea4fa927cee124862f6ceb40b631182b6cf 100644 (file)
@@ -280,6 +280,12 @@ struct sctp_chunk *sctp_make_strreset_tsnresp(
 void sctp_chunk_assign_tsn(struct sctp_chunk *);
 void sctp_chunk_assign_ssn(struct sctp_chunk *);
 
+/* Prototypes for stream-processing functions.  */
+struct sctp_chunk *sctp_process_strreset_outreq(
+                               struct sctp_association *asoc,
+                               union sctp_params param,
+                               struct sctp_ulpevent **evp);
+
 /* Prototypes for statetable processing. */
 
 int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype,
index eb02490245ba3da16445d5e5d79f4e5c51bfb534..531fcc4cd11256eb2d44f1f64936b561d9eedbd1 100644 (file)
@@ -294,3 +294,116 @@ int sctp_send_add_streams(struct sctp_association *asoc,
 out:
        return retval;
 }
+
+static sctp_paramhdr_t *sctp_chunk_lookup_strreset_param(
+                       struct sctp_association *asoc, __u32 resp_seq)
+{
+       struct sctp_chunk *chunk = asoc->strreset_chunk;
+       struct sctp_reconf_chunk *hdr;
+       union sctp_params param;
+
+       if (ntohl(resp_seq) != asoc->strreset_outseq || !chunk)
+               return NULL;
+
+       hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr;
+       sctp_walk_params(param, hdr, params) {
+               /* sctp_strreset_tsnreq is actually the basic structure
+                * of all stream reconf params, so it's safe to use it
+                * to access request_seq.
+                */
+               struct sctp_strreset_tsnreq *req = param.v;
+
+               if (req->request_seq == resp_seq)
+                       return param.v;
+       }
+
+       return NULL;
+}
+
+struct sctp_chunk *sctp_process_strreset_outreq(
+                               struct sctp_association *asoc,
+                               union sctp_params param,
+                               struct sctp_ulpevent **evp)
+{
+       struct sctp_strreset_outreq *outreq = param.v;
+       struct sctp_stream *stream = asoc->stream;
+       __u16 i, nums, flags = 0, *str_p = NULL;
+       __u32 result = SCTP_STRRESET_DENIED;
+       __u32 request_seq;
+
+       request_seq = ntohl(outreq->request_seq);
+
+       if (ntohl(outreq->send_reset_at_tsn) >
+           sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) {
+               result = SCTP_STRRESET_IN_PROGRESS;
+               goto out;
+       }
+
+       if (request_seq > asoc->strreset_inseq) {
+               result = SCTP_STRRESET_ERR_BAD_SEQNO;
+               goto out;
+       } else if (request_seq == asoc->strreset_inseq) {
+               asoc->strreset_inseq++;
+       }
+
+       /* Check strreset_enable after inseq inc, as sender cannot tell
+        * the peer doesn't enable strreset after receiving response with
+        * result denied, as well as to keep consistent with bsd.
+        */
+       if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ))
+               goto out;
+
+       if (asoc->strreset_chunk) {
+               sctp_paramhdr_t *param_hdr;
+               struct sctp_transport *t;
+
+               param_hdr = sctp_chunk_lookup_strreset_param(
+                                       asoc, outreq->response_seq);
+               if (!param_hdr || param_hdr->type !=
+                                       SCTP_PARAM_RESET_IN_REQUEST) {
+                       /* same process with outstanding isn't 0 */
+                       result = SCTP_STRRESET_ERR_IN_PROGRESS;
+                       goto out;
+               }
+
+               asoc->strreset_outstanding--;
+               asoc->strreset_outseq++;
+
+               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;
+               }
+
+               flags = SCTP_STREAM_RESET_INCOMING_SSN;
+       }
+
+       nums = (ntohs(param.p->length) - sizeof(*outreq)) / 2;
+       if (nums) {
+               str_p = outreq->list_of_streams;
+               for (i = 0; i < nums; i++) {
+                       if (ntohs(str_p[i]) >= stream->incnt) {
+                               result = SCTP_STRRESET_ERR_WRONG_SSN;
+                               goto out;
+                       }
+               }
+
+               for (i = 0; i < nums; i++)
+                       stream->in[ntohs(str_p[i])].ssn = 0;
+       } else {
+               for (i = 0; i < stream->incnt; i++)
+                       stream->in[i].ssn = 0;
+       }
+
+       result = SCTP_STRRESET_PERFORMED;
+
+       *evp = sctp_ulpevent_make_stream_reset_event(asoc,
+               flags | SCTP_STREAM_RESET_OUTGOING_SSN, nums, str_p,
+               GFP_ATOMIC);
+
+out:
+       return sctp_make_strreset_resp(asoc, result, request_seq);
+}