sctp: implement validate_data for sctp_stream_interleave
[linux-block.git] / net / sctp / stream_interleave.c
CommitLineData
0c3f6f65
XL
1/* SCTP kernel implementation
2 * (C) Copyright Red Hat Inc. 2017
3 *
4 * This file is part of the SCTP kernel implementation
5 *
6 * These functions manipulate sctp stream queue/scheduling.
7 *
8 * This SCTP implementation is free software;
9 * you can redistribute it and/or modify it under the terms of
10 * the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * This SCTP implementation is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * ************************
17 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 * See the GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with GNU CC; see the file COPYING. If not, see
22 * <http://www.gnu.org/licenses/>.
23 *
24 * Please send any bug reports or fixes you make to the
25 * email addresched(es):
26 * lksctp developers <linux-sctp@vger.kernel.org>
27 *
28 * Written or modified by:
29 * Xin Long <lucien.xin@gmail.com>
30 */
31
32#include <net/sctp/sctp.h>
33#include <net/sctp/sm.h>
34#include <linux/sctp.h>
35
36static struct sctp_chunk *sctp_make_idatafrag_empty(
37 const struct sctp_association *asoc,
38 const struct sctp_sndrcvinfo *sinfo,
39 int len, __u8 flags, gfp_t gfp)
40{
41 struct sctp_chunk *retval;
42 struct sctp_idatahdr dp;
43
44 memset(&dp, 0, sizeof(dp));
45 dp.stream = htons(sinfo->sinfo_stream);
46
47 if (sinfo->sinfo_flags & SCTP_UNORDERED)
48 flags |= SCTP_DATA_UNORDERED;
49
50 retval = sctp_make_idata(asoc, flags, sizeof(dp) + len, gfp);
51 if (!retval)
52 return NULL;
53
54 retval->subh.idata_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp);
55 memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo));
56
57 return retval;
58}
59
668c9beb
XL
60static void sctp_chunk_assign_mid(struct sctp_chunk *chunk)
61{
62 struct sctp_stream *stream;
63 struct sctp_chunk *lchunk;
64 __u32 cfsn = 0;
65 __u16 sid;
66
67 if (chunk->has_mid)
68 return;
69
70 sid = sctp_chunk_stream_no(chunk);
71 stream = &chunk->asoc->stream;
72
73 list_for_each_entry(lchunk, &chunk->msg->chunks, frag_list) {
74 struct sctp_idatahdr *hdr;
75
76 lchunk->has_mid = 1;
77
78 if (lchunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
79 continue;
80
81 hdr = lchunk->subh.idata_hdr;
82
83 if (lchunk->chunk_hdr->flags & SCTP_DATA_FIRST_FRAG)
84 hdr->ppid = lchunk->sinfo.sinfo_ppid;
85 else
86 hdr->fsn = htonl(cfsn++);
87
88 if (lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
89 hdr->mid = htonl(sctp_mid_next(stream, out, sid));
90 else
91 hdr->mid = htonl(sctp_mid_peek(stream, out, sid));
92 }
93}
94
9d4ceaf1
XL
95static bool sctp_validate_data(struct sctp_chunk *chunk)
96{
97 const struct sctp_stream *stream;
98 __u16 sid, ssn;
99
100 if (chunk->chunk_hdr->type != SCTP_CID_DATA)
101 return false;
102
103 if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
104 return true;
105
106 stream = &chunk->asoc->stream;
107 sid = sctp_chunk_stream_no(chunk);
108 ssn = ntohs(chunk->subh.data_hdr->ssn);
109
110 return !SSN_lt(ssn, sctp_ssn_peek(stream, in, sid));
111}
112
113static bool sctp_validate_idata(struct sctp_chunk *chunk)
114{
115 struct sctp_stream *stream;
116 __u32 mid;
117 __u16 sid;
118
119 if (chunk->chunk_hdr->type != SCTP_CID_I_DATA)
120 return false;
121
122 if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
123 return true;
124
125 stream = &chunk->asoc->stream;
126 sid = sctp_chunk_stream_no(chunk);
127 mid = ntohl(chunk->subh.idata_hdr->mid);
128
129 return !MID_lt(mid, sctp_mid_peek(stream, in, sid));
130}
131
0c3f6f65
XL
132static struct sctp_stream_interleave sctp_stream_interleave_0 = {
133 .data_chunk_len = sizeof(struct sctp_data_chunk),
134 /* DATA process functions */
135 .make_datafrag = sctp_make_datafrag_empty,
668c9beb 136 .assign_number = sctp_chunk_assign_ssn,
9d4ceaf1 137 .validate_data = sctp_validate_data,
0c3f6f65
XL
138};
139
140static struct sctp_stream_interleave sctp_stream_interleave_1 = {
141 .data_chunk_len = sizeof(struct sctp_idata_chunk),
142 /* I-DATA process functions */
143 .make_datafrag = sctp_make_idatafrag_empty,
668c9beb 144 .assign_number = sctp_chunk_assign_mid,
9d4ceaf1 145 .validate_data = sctp_validate_idata,
0c3f6f65
XL
146};
147
148void sctp_stream_interleave_init(struct sctp_stream *stream)
149{
150 struct sctp_association *asoc;
151
152 asoc = container_of(stream, struct sctp_association, stream);
153 stream->si = asoc->intl_enable ? &sctp_stream_interleave_1
154 : &sctp_stream_interleave_0;
155}