Commit | Line | Data |
---|---|---|
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 | ||
36 | static 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 |
60 | static 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 |
95 | static 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 | ||
113 | static 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 |
132 | static 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 | ||
140 | static 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 | ||
148 | void 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 | } |