Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sctp: implement validate_ftsn for sctp_stream_interleave

validate_ftsn is added as a member of sctp_stream_interleave, used to
validate ssn/chunk type for fwdtsn or mid (message id)/chunk type for
ifwdtsn, called in sctp_sf_eat_fwd_tsn, just as validate_data.

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

As ifwdtsn and fwdtsn chunks have different length, it also defines
ftsn_chunk_len for sctp_stream_interleave to describe the chunk size.
Then it replaces all sizeof(struct sctp_fwdtsn_chunk) with
sctp_ftsnchk_len.

It also adds the process for ifwdtsn in rx path. As Marcelo pointed
out, there's no need to add event table for ifwdtsn, but just share
prsctp_chunk_event_table with fwdtsn's. It would drop fwdtsn chunk
for ifwdtsn and drop ifwdtsn chunk for fwdtsn by calling validate_ftsn
in sctp_sf_eat_fwd_tsn.

After this patch, the ifwdtsn can be accepted.

Note that this patch also removes the sctp.intl_enable check for
idata chunks in sctp_chunk_event_lookup, as it will do this check
in validate_data later.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo R. Leitner <marcelo.leitner@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Xin Long and committed by
David S. Miller
0fc2ea92 8e0c3b73

+66 -18
+2
include/net/sctp/stream_interleave.h
··· 33 33 34 34 struct sctp_stream_interleave { 35 35 __u16 data_chunk_len; 36 + __u16 ftsn_chunk_len; 36 37 /* (I-)DATA process */ 37 38 struct sctp_chunk *(*make_datafrag)(const struct sctp_association *asoc, 38 39 const struct sctp_sndrcvinfo *sinfo, ··· 50 49 void (*abort_pd)(struct sctp_ulpq *ulpq, gfp_t gfp); 51 50 /* (I-)FORWARD-TSN process */ 52 51 void (*generate_ftsn)(struct sctp_outq *q, __u32 ctsn); 52 + bool (*validate_ftsn)(struct sctp_chunk *chunk); 53 53 }; 54 54 55 55 void sctp_stream_interleave_init(struct sctp_stream *stream);
+10
include/net/sctp/structs.h
··· 1443 1443 return stream->si->data_chunk_len - sizeof(struct sctp_chunkhdr); 1444 1444 } 1445 1445 1446 + static inline __u16 sctp_ftsnchk_len(const struct sctp_stream *stream) 1447 + { 1448 + return stream->si->ftsn_chunk_len; 1449 + } 1450 + 1451 + static inline __u16 sctp_ftsnhdr_len(const struct sctp_stream *stream) 1452 + { 1453 + return stream->si->ftsn_chunk_len - sizeof(struct sctp_chunkhdr); 1454 + } 1455 + 1446 1456 /* SCTP_GET_ASSOC_STATS counters */ 1447 1457 struct sctp_priv_assoc_stats { 1448 1458 /* Maximum observed rto in the association during subsequent
+8 -16
net/sctp/sm_statefuns.c
··· 3957 3957 { 3958 3958 struct sctp_fwdtsn_hdr *fwdtsn_hdr; 3959 3959 struct sctp_chunk *chunk = arg; 3960 - struct sctp_fwdtsn_skip *skip; 3961 3960 __u16 len; 3962 3961 __u32 tsn; 3963 3962 ··· 3970 3971 return sctp_sf_unk_chunk(net, ep, asoc, type, arg, commands); 3971 3972 3972 3973 /* Make sure that the FORWARD_TSN chunk has valid length. */ 3973 - if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_fwdtsn_chunk))) 3974 + if (!sctp_chunk_length_valid(chunk, sctp_ftsnchk_len(&asoc->stream))) 3974 3975 return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, 3975 3976 commands); 3976 3977 ··· 3989 3990 if (sctp_tsnmap_check(&asoc->peer.tsn_map, tsn) < 0) 3990 3991 goto discard_noforce; 3991 3992 3992 - /* Silently discard the chunk if stream-id is not valid */ 3993 - sctp_walk_fwdtsn(skip, chunk) { 3994 - if (ntohs(skip->stream) >= asoc->stream.incnt) 3995 - goto discard_noforce; 3996 - } 3993 + if (!asoc->stream.si->validate_ftsn(chunk)) 3994 + goto discard_noforce; 3997 3995 3998 3996 sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_FWDTSN, SCTP_U32(tsn)); 3999 - if (len > sizeof(struct sctp_fwdtsn_hdr)) 3997 + if (len > sctp_ftsnhdr_len(&asoc->stream)) 4000 3998 sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_FWDTSN, 4001 3999 SCTP_CHUNK(chunk)); 4002 4000 ··· 4024 4028 { 4025 4029 struct sctp_fwdtsn_hdr *fwdtsn_hdr; 4026 4030 struct sctp_chunk *chunk = arg; 4027 - struct sctp_fwdtsn_skip *skip; 4028 4031 __u16 len; 4029 4032 __u32 tsn; 4030 4033 ··· 4037 4042 return sctp_sf_unk_chunk(net, ep, asoc, type, arg, commands); 4038 4043 4039 4044 /* Make sure that the FORWARD_TSN chunk has a valid length. */ 4040 - if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_fwdtsn_chunk))) 4045 + if (!sctp_chunk_length_valid(chunk, sctp_ftsnchk_len(&asoc->stream))) 4041 4046 return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, 4042 4047 commands); 4043 4048 ··· 4056 4061 if (sctp_tsnmap_check(&asoc->peer.tsn_map, tsn) < 0) 4057 4062 goto gen_shutdown; 4058 4063 4059 - /* Silently discard the chunk if stream-id is not valid */ 4060 - sctp_walk_fwdtsn(skip, chunk) { 4061 - if (ntohs(skip->stream) >= asoc->stream.incnt) 4062 - goto gen_shutdown; 4063 - } 4064 + if (!asoc->stream.si->validate_ftsn(chunk)) 4065 + goto gen_shutdown; 4064 4066 4065 4067 sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_FWDTSN, SCTP_U32(tsn)); 4066 - if (len > sizeof(struct sctp_fwdtsn_hdr)) 4068 + if (len > sctp_ftsnhdr_len(&asoc->stream)) 4067 4069 sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_FWDTSN, 4068 4070 SCTP_CHUNK(chunk)); 4069 4071
+2 -2
net/sctp/sm_statetable.c
··· 985 985 if (state > SCTP_STATE_MAX) 986 986 return &bug; 987 987 988 - if (net->sctp.intl_enable && cid == SCTP_CID_I_DATA) 988 + if (cid == SCTP_CID_I_DATA) 989 989 cid = SCTP_CID_DATA; 990 990 991 991 if (cid <= SCTP_CID_BASE_MAX) 992 992 return &chunk_event_table[cid][state]; 993 993 994 994 if (net->sctp.prsctp_enable) { 995 - if (cid == SCTP_CID_FWD_TSN) 995 + if (cid == SCTP_CID_FWD_TSN || cid == SCTP_CID_I_FWD_TSN) 996 996 return &prsctp_chunk_event_table[0][state]; 997 997 } 998 998
+44
net/sctp/stream_interleave.c
··· 1153 1153 } 1154 1154 } 1155 1155 1156 + #define _sctp_walk_ifwdtsn(pos, chunk, end) \ 1157 + for (pos = chunk->subh.ifwdtsn_hdr->skip; \ 1158 + (void *)pos < (void *)chunk->subh.ifwdtsn_hdr->skip + (end); pos++) 1159 + 1160 + #define sctp_walk_ifwdtsn(pos, ch) \ 1161 + _sctp_walk_ifwdtsn((pos), (ch), ntohs((ch)->chunk_hdr->length) - \ 1162 + sizeof(struct sctp_ifwdtsn_chunk)) 1163 + 1164 + static bool sctp_validate_fwdtsn(struct sctp_chunk *chunk) 1165 + { 1166 + struct sctp_fwdtsn_skip *skip; 1167 + __u16 incnt; 1168 + 1169 + if (chunk->chunk_hdr->type != SCTP_CID_FWD_TSN) 1170 + return false; 1171 + 1172 + incnt = chunk->asoc->stream.incnt; 1173 + sctp_walk_fwdtsn(skip, chunk) 1174 + if (ntohs(skip->stream) >= incnt) 1175 + return false; 1176 + 1177 + return true; 1178 + } 1179 + 1180 + static bool sctp_validate_iftsn(struct sctp_chunk *chunk) 1181 + { 1182 + struct sctp_ifwdtsn_skip *skip; 1183 + __u16 incnt; 1184 + 1185 + if (chunk->chunk_hdr->type != SCTP_CID_I_FWD_TSN) 1186 + return false; 1187 + 1188 + incnt = chunk->asoc->stream.incnt; 1189 + sctp_walk_ifwdtsn(skip, chunk) 1190 + if (ntohs(skip->stream) >= incnt) 1191 + return false; 1192 + 1193 + return true; 1194 + } 1195 + 1156 1196 static struct sctp_stream_interleave sctp_stream_interleave_0 = { 1157 1197 .data_chunk_len = sizeof(struct sctp_data_chunk), 1198 + .ftsn_chunk_len = sizeof(struct sctp_fwdtsn_chunk), 1158 1199 /* DATA process functions */ 1159 1200 .make_datafrag = sctp_make_datafrag_empty, 1160 1201 .assign_number = sctp_chunk_assign_ssn, ··· 1207 1166 .abort_pd = sctp_ulpq_abort_pd, 1208 1167 /* FORWARD-TSN process functions */ 1209 1168 .generate_ftsn = sctp_generate_fwdtsn, 1169 + .validate_ftsn = sctp_validate_fwdtsn, 1210 1170 }; 1211 1171 1212 1172 static struct sctp_stream_interleave sctp_stream_interleave_1 = { 1213 1173 .data_chunk_len = sizeof(struct sctp_idata_chunk), 1174 + .ftsn_chunk_len = sizeof(struct sctp_ifwdtsn_chunk), 1214 1175 /* I-DATA process functions */ 1215 1176 .make_datafrag = sctp_make_idatafrag_empty, 1216 1177 .assign_number = sctp_chunk_assign_mid, ··· 1224 1181 .abort_pd = sctp_intl_abort_pd, 1225 1182 /* I-FORWARD-TSN process functions */ 1226 1183 .generate_ftsn = sctp_generate_iftsn, 1184 + .validate_ftsn = sctp_validate_iftsn, 1227 1185 }; 1228 1186 1229 1187 void sctp_stream_interleave_init(struct sctp_stream *stream)