sctp: implement validate_data for sctp_stream_interleave
authorXin Long <lucien.xin@gmail.com>
Fri, 8 Dec 2017 13:04:03 +0000 (21:04 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Dec 2017 16:23:04 +0000 (11:23 -0500)
validate_data is added as a member of sctp_stream_interleave, used
to validate ssn/chunk type for data or mid (message id)/chunk type
for idata, called in sctp_eat_data.

If this check fails, an abort packet will be sent, as said in
section 2.2.3 of RFC8260.

It also adds the process for idata in rx path. As Marcelo pointed
out, there's no need to add event table for idata, but just share
chunk_event_table with data's. It would drop data chunk for idata
and drop idata chunk for data by calling validate_data in
sctp_eat_data.

As last patch did, it also replaces sizeof(struct sctp_data_chunk)
with sctp_datachk_len for rx path.

After this patch, the idata can be accepted and delivered to ulp
layer.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/sm.h
include/net/sctp/stream_interleave.h
include/net/sctp/structs.h
net/sctp/sm_statefuns.c
net/sctp/sm_statetable.c
net/sctp/stream_interleave.c

index ca1db8997e5d8cff6e3729f2eea90e2a487b415e..0993b4953b3a3953468e2ba542f27fd9e21f1e89 100644 (file)
@@ -359,6 +359,12 @@ static inline __u16 sctp_data_size(struct sctp_chunk *chunk)
         typecheck(__u32, b) && \
         ((__s32)((a) - (b)) <= 0))
 
+/* Compare two MIDs */
+#define MID_lt(a, b)   \
+       (typecheck(__u32, a) && \
+        typecheck(__u32, b) && \
+        ((__s32)((a) - (b)) < 0))
+
 /* Compare two SSNs */
 #define SSN_lt(a,b)            \
        (typecheck(__u16, a) && \
index 99f399e7a8710dcd2a450e03da204bdced7fe133..d8d1b51e13625a2e98c7667a2c543eb967f6f428 100644 (file)
@@ -38,6 +38,7 @@ struct sctp_stream_interleave {
                                            const struct sctp_sndrcvinfo *sinfo,
                                            int len, __u8 flags, gfp_t gfp);
        void    (*assign_number)(struct sctp_chunk *chunk);
+       bool    (*validate_data)(struct sctp_chunk *chunk);
 };
 
 void sctp_stream_interleave_init(struct sctp_stream *stream);
index fd93973bb66767f60e431590fefd2e1fc18c7d48..be4cc73f3106a2da784e78be97acb6661c1aa083 100644 (file)
@@ -1382,7 +1382,11 @@ struct sctp_stream_out {
 };
 
 struct sctp_stream_in {
-       __u16   ssn;
+       union {
+               __u32 mid;
+               __u16 ssn;
+       };
+       __u32 fsn;
 };
 
 struct sctp_stream {
index 8f8ccded13e47c4b5403b5a27416b0d5707d1f33..c609c54099109e16c90e35966abbd74947307eb9 100644 (file)
@@ -3013,7 +3013,7 @@ enum sctp_disposition sctp_sf_eat_data_6_2(struct net *net,
                return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
-       if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_data_chunk)))
+       if (!sctp_chunk_length_valid(chunk, sctp_datachk_len(&asoc->stream)))
                return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
@@ -3034,7 +3034,7 @@ enum sctp_disposition sctp_sf_eat_data_6_2(struct net *net,
        case SCTP_IERROR_PROTO_VIOLATION:
                return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
                                               (u8 *)chunk->subh.data_hdr,
-                                              sizeof(struct sctp_datahdr));
+                                              sctp_datahdr_len(&asoc->stream));
        default:
                BUG();
        }
@@ -3133,7 +3133,7 @@ enum sctp_disposition sctp_sf_eat_data_fast_4_4(
                return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
        }
 
-       if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_data_chunk)))
+       if (!sctp_chunk_length_valid(chunk, sctp_datachk_len(&asoc->stream)))
                return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
                                                  commands);
 
@@ -3150,7 +3150,7 @@ enum sctp_disposition sctp_sf_eat_data_fast_4_4(
        case SCTP_IERROR_PROTO_VIOLATION:
                return sctp_sf_abort_violation(net, ep, asoc, chunk, commands,
                                               (u8 *)chunk->subh.data_hdr,
-                                              sizeof(struct sctp_datahdr));
+                                              sctp_datahdr_len(&asoc->stream));
        default:
                BUG();
        }
@@ -6244,14 +6244,12 @@ static int sctp_eat_data(const struct sctp_association *asoc,
        struct sctp_chunk *err;
        enum sctp_verb deliver;
        size_t datalen;
-       u8 ordered = 0;
-       u16 ssn, sid;
        __u32 tsn;
        int tmp;
 
        data_hdr = (struct sctp_datahdr *)chunk->skb->data;
        chunk->subh.data_hdr = data_hdr;
-       skb_pull(chunk->skb, sizeof(*data_hdr));
+       skb_pull(chunk->skb, sctp_datahdr_len(&asoc->stream));
 
        tsn = ntohl(data_hdr->tsn);
        pr_debug("%s: TSN 0x%x\n", __func__, tsn);
@@ -6299,7 +6297,7 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         * Actually, allow a little bit of overflow (up to a MTU).
         */
        datalen = ntohs(chunk->chunk_hdr->length);
-       datalen -= sizeof(struct sctp_data_chunk);
+       datalen -= sctp_datachk_len(&asoc->stream);
 
        deliver = SCTP_CMD_CHUNK_ULP;
 
@@ -6394,7 +6392,6 @@ static int sctp_eat_data(const struct sctp_association *asoc,
                SCTP_INC_STATS(net, SCTP_MIB_INORDERCHUNKS);
                if (chunk->asoc)
                        chunk->asoc->stats.iodchunks++;
-               ordered = 1;
        }
 
        /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
@@ -6405,8 +6402,7 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         * with cause set to "Invalid Stream Identifier" (See Section 3.3.10)
         * and discard the DATA chunk.
         */
-       sid = ntohs(data_hdr->stream);
-       if (sid >= asoc->stream.incnt) {
+       if (ntohs(data_hdr->stream) >= asoc->stream.incnt) {
                /* Mark tsn as received even though we drop it */
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
 
@@ -6427,8 +6423,7 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         * SSN is smaller then the next expected one.  If it is, it wrapped
         * and is invalid.
         */
-       ssn = ntohs(data_hdr->ssn);
-       if (ordered && SSN_lt(ssn, sctp_ssn_peek(&asoc->stream, in, sid)))
+       if (!asoc->stream.si->validate_data(chunk))
                return SCTP_IERROR_PROTO_VIOLATION;
 
        /* Send the data up to the user.  Note:  Schedule  the
index 79b6bee5b768d449ace9a2a4c48acbd0a02491f0..8c9bb4109f478276634d05fdeef48efec7eddcae 100644 (file)
@@ -985,6 +985,9 @@ static const struct sctp_sm_table_entry *sctp_chunk_event_lookup(
        if (state > SCTP_STATE_MAX)
                return &bug;
 
+       if (net->sctp.intl_enable && cid == SCTP_CID_I_DATA)
+               cid = SCTP_CID_DATA;
+
        if (cid <= SCTP_CID_BASE_MAX)
                return &chunk_event_table[cid][state];
 
index 3ac47e78c01330fdcbaef601a1e4c9e5780a5d4e..3d8733be6f7a0a239e72183726b267aa0c7d7856 100644 (file)
@@ -92,11 +92,49 @@ static void sctp_chunk_assign_mid(struct sctp_chunk *chunk)
        }
 }
 
+static bool sctp_validate_data(struct sctp_chunk *chunk)
+{
+       const struct sctp_stream *stream;
+       __u16 sid, ssn;
+
+       if (chunk->chunk_hdr->type != SCTP_CID_DATA)
+               return false;
+
+       if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+               return true;
+
+       stream = &chunk->asoc->stream;
+       sid = sctp_chunk_stream_no(chunk);
+       ssn = ntohs(chunk->subh.data_hdr->ssn);
+
+       return !SSN_lt(ssn, sctp_ssn_peek(stream, in, sid));
+}
+
+static bool sctp_validate_idata(struct sctp_chunk *chunk)
+{
+       struct sctp_stream *stream;
+       __u32 mid;
+       __u16 sid;
+
+       if (chunk->chunk_hdr->type != SCTP_CID_I_DATA)
+               return false;
+
+       if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+               return true;
+
+       stream = &chunk->asoc->stream;
+       sid = sctp_chunk_stream_no(chunk);
+       mid = ntohl(chunk->subh.idata_hdr->mid);
+
+       return !MID_lt(mid, sctp_mid_peek(stream, in, sid));
+}
+
 static struct sctp_stream_interleave sctp_stream_interleave_0 = {
        .data_chunk_len         = sizeof(struct sctp_data_chunk),
        /* DATA process functions */
        .make_datafrag          = sctp_make_datafrag_empty,
        .assign_number          = sctp_chunk_assign_ssn,
+       .validate_data          = sctp_validate_data,
 };
 
 static struct sctp_stream_interleave sctp_stream_interleave_1 = {
@@ -104,6 +142,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_1 = {
        /* I-DATA process functions */
        .make_datafrag          = sctp_make_idatafrag_empty,
        .assign_number          = sctp_chunk_assign_mid,
+       .validate_data          = sctp_validate_idata,
 };
 
 void sctp_stream_interleave_init(struct sctp_stream *stream)