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);
 
        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();
        }
                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);
 
        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();
        }
        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);
         * 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;
 
                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
         * 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));
 
         * 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
 
        }
 }
 
+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 = {
        /* 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)