diff options
Diffstat (limited to 'net/sctp/sm_make_chunk.c')
-rw-r--r-- | net/sctp/sm_make_chunk.c | 108 |
1 files changed, 77 insertions, 31 deletions
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 9d881a61ac02..30c1767186b8 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c | |||
@@ -58,6 +58,7 @@ | |||
58 | #include <linux/inet.h> | 58 | #include <linux/inet.h> |
59 | #include <linux/scatterlist.h> | 59 | #include <linux/scatterlist.h> |
60 | #include <linux/crypto.h> | 60 | #include <linux/crypto.h> |
61 | #include <linux/slab.h> | ||
61 | #include <net/sock.h> | 62 | #include <net/sock.h> |
62 | 63 | ||
63 | #include <linux/skbuff.h> | 64 | #include <linux/skbuff.h> |
@@ -107,7 +108,7 @@ static const struct sctp_paramhdr prsctp_param = { | |||
107 | cpu_to_be16(sizeof(struct sctp_paramhdr)), | 108 | cpu_to_be16(sizeof(struct sctp_paramhdr)), |
108 | }; | 109 | }; |
109 | 110 | ||
110 | /* A helper to initialize to initialize an op error inside a | 111 | /* A helper to initialize an op error inside a |
111 | * provided chunk, as most cause codes will be embedded inside an | 112 | * provided chunk, as most cause codes will be embedded inside an |
112 | * abort chunk. | 113 | * abort chunk. |
113 | */ | 114 | */ |
@@ -124,6 +125,29 @@ void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, | |||
124 | chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err); | 125 | chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err); |
125 | } | 126 | } |
126 | 127 | ||
128 | /* A helper to initialize an op error inside a | ||
129 | * provided chunk, as most cause codes will be embedded inside an | ||
130 | * abort chunk. Differs from sctp_init_cause in that it won't oops | ||
131 | * if there isn't enough space in the op error chunk | ||
132 | */ | ||
133 | int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code, | ||
134 | size_t paylen) | ||
135 | { | ||
136 | sctp_errhdr_t err; | ||
137 | __u16 len; | ||
138 | |||
139 | /* Cause code constants are now defined in network order. */ | ||
140 | err.cause = cause_code; | ||
141 | len = sizeof(sctp_errhdr_t) + paylen; | ||
142 | err.length = htons(len); | ||
143 | |||
144 | if (skb_tailroom(chunk->skb) > len) | ||
145 | return -ENOSPC; | ||
146 | chunk->subh.err_hdr = sctp_addto_chunk_fixed(chunk, | ||
147 | sizeof(sctp_errhdr_t), | ||
148 | &err); | ||
149 | return 0; | ||
150 | } | ||
127 | /* 3.3.2 Initiation (INIT) (1) | 151 | /* 3.3.2 Initiation (INIT) (1) |
128 | * | 152 | * |
129 | * This chunk is used to initiate a SCTP association between two | 153 | * This chunk is used to initiate a SCTP association between two |
@@ -207,7 +231,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, | |||
207 | sp = sctp_sk(asoc->base.sk); | 231 | sp = sctp_sk(asoc->base.sk); |
208 | num_types = sp->pf->supported_addrs(sp, types); | 232 | num_types = sp->pf->supported_addrs(sp, types); |
209 | 233 | ||
210 | chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types); | 234 | chunksize = sizeof(init) + addrs_len; |
235 | chunksize += WORD_ROUND(SCTP_SAT_LEN(num_types)); | ||
211 | chunksize += sizeof(ecap_param); | 236 | chunksize += sizeof(ecap_param); |
212 | 237 | ||
213 | if (sctp_prsctp_enable) | 238 | if (sctp_prsctp_enable) |
@@ -237,14 +262,14 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, | |||
237 | /* Add HMACS parameter length if any were defined */ | 262 | /* Add HMACS parameter length if any were defined */ |
238 | auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs; | 263 | auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs; |
239 | if (auth_hmacs->length) | 264 | if (auth_hmacs->length) |
240 | chunksize += ntohs(auth_hmacs->length); | 265 | chunksize += WORD_ROUND(ntohs(auth_hmacs->length)); |
241 | else | 266 | else |
242 | auth_hmacs = NULL; | 267 | auth_hmacs = NULL; |
243 | 268 | ||
244 | /* Add CHUNKS parameter length */ | 269 | /* Add CHUNKS parameter length */ |
245 | auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks; | 270 | auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks; |
246 | if (auth_chunks->length) | 271 | if (auth_chunks->length) |
247 | chunksize += ntohs(auth_chunks->length); | 272 | chunksize += WORD_ROUND(ntohs(auth_chunks->length)); |
248 | else | 273 | else |
249 | auth_chunks = NULL; | 274 | auth_chunks = NULL; |
250 | 275 | ||
@@ -254,7 +279,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, | |||
254 | 279 | ||
255 | /* If we have any extensions to report, account for that */ | 280 | /* If we have any extensions to report, account for that */ |
256 | if (num_ext) | 281 | if (num_ext) |
257 | chunksize += sizeof(sctp_supported_ext_param_t) + num_ext; | 282 | chunksize += WORD_ROUND(sizeof(sctp_supported_ext_param_t) + |
283 | num_ext); | ||
258 | 284 | ||
259 | /* RFC 2960 3.3.2 Initiation (INIT) (1) | 285 | /* RFC 2960 3.3.2 Initiation (INIT) (1) |
260 | * | 286 | * |
@@ -396,13 +422,13 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, | |||
396 | 422 | ||
397 | auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs; | 423 | auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs; |
398 | if (auth_hmacs->length) | 424 | if (auth_hmacs->length) |
399 | chunksize += ntohs(auth_hmacs->length); | 425 | chunksize += WORD_ROUND(ntohs(auth_hmacs->length)); |
400 | else | 426 | else |
401 | auth_hmacs = NULL; | 427 | auth_hmacs = NULL; |
402 | 428 | ||
403 | auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks; | 429 | auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks; |
404 | if (auth_chunks->length) | 430 | if (auth_chunks->length) |
405 | chunksize += ntohs(auth_chunks->length); | 431 | chunksize += WORD_ROUND(ntohs(auth_chunks->length)); |
406 | else | 432 | else |
407 | auth_chunks = NULL; | 433 | auth_chunks = NULL; |
408 | 434 | ||
@@ -411,7 +437,8 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, | |||
411 | } | 437 | } |
412 | 438 | ||
413 | if (num_ext) | 439 | if (num_ext) |
414 | chunksize += sizeof(sctp_supported_ext_param_t) + num_ext; | 440 | chunksize += WORD_ROUND(sizeof(sctp_supported_ext_param_t) + |
441 | num_ext); | ||
415 | 442 | ||
416 | /* Now allocate and fill out the chunk. */ | 443 | /* Now allocate and fill out the chunk. */ |
417 | retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize); | 444 | retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize); |
@@ -987,7 +1014,10 @@ static void *sctp_addto_param(struct sctp_chunk *chunk, int len, | |||
987 | 1014 | ||
988 | target = skb_put(chunk->skb, len); | 1015 | target = skb_put(chunk->skb, len); |
989 | 1016 | ||
990 | memcpy(target, data, len); | 1017 | if (data) |
1018 | memcpy(target, data, len); | ||
1019 | else | ||
1020 | memset(target, 0, len); | ||
991 | 1021 | ||
992 | /* Adjust the chunk length field. */ | 1022 | /* Adjust the chunk length field. */ |
993 | chunk->chunk_hdr->length = htons(chunklen + len); | 1023 | chunk->chunk_hdr->length = htons(chunklen + len); |
@@ -1125,20 +1155,40 @@ nodata: | |||
1125 | return retval; | 1155 | return retval; |
1126 | } | 1156 | } |
1127 | 1157 | ||
1158 | /* Create an Operation Error chunk of a fixed size, | ||
1159 | * specifically, max(asoc->pathmtu, SCTP_DEFAULT_MAXSEGMENT) | ||
1160 | * This is a helper function to allocate an error chunk for | ||
1161 | * for those invalid parameter codes in which we may not want | ||
1162 | * to report all the errors, if the incomming chunk is large | ||
1163 | */ | ||
1164 | static inline struct sctp_chunk *sctp_make_op_error_fixed( | ||
1165 | const struct sctp_association *asoc, | ||
1166 | const struct sctp_chunk *chunk) | ||
1167 | { | ||
1168 | size_t size = asoc ? asoc->pathmtu : 0; | ||
1169 | |||
1170 | if (!size) | ||
1171 | size = SCTP_DEFAULT_MAXSEGMENT; | ||
1172 | |||
1173 | return sctp_make_op_error_space(asoc, chunk, size); | ||
1174 | } | ||
1175 | |||
1128 | /* Create an Operation Error chunk. */ | 1176 | /* Create an Operation Error chunk. */ |
1129 | struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, | 1177 | struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, |
1130 | const struct sctp_chunk *chunk, | 1178 | const struct sctp_chunk *chunk, |
1131 | __be16 cause_code, const void *payload, | 1179 | __be16 cause_code, const void *payload, |
1132 | size_t paylen) | 1180 | size_t paylen, size_t reserve_tail) |
1133 | { | 1181 | { |
1134 | struct sctp_chunk *retval; | 1182 | struct sctp_chunk *retval; |
1135 | 1183 | ||
1136 | retval = sctp_make_op_error_space(asoc, chunk, paylen); | 1184 | retval = sctp_make_op_error_space(asoc, chunk, paylen + reserve_tail); |
1137 | if (!retval) | 1185 | if (!retval) |
1138 | goto nodata; | 1186 | goto nodata; |
1139 | 1187 | ||
1140 | sctp_init_cause(retval, cause_code, paylen); | 1188 | sctp_init_cause(retval, cause_code, paylen + reserve_tail); |
1141 | sctp_addto_chunk(retval, paylen, payload); | 1189 | sctp_addto_chunk(retval, paylen, payload); |
1190 | if (reserve_tail) | ||
1191 | sctp_addto_param(retval, reserve_tail, NULL); | ||
1142 | 1192 | ||
1143 | nodata: | 1193 | nodata: |
1144 | return retval; | 1194 | return retval; |
@@ -1365,6 +1415,18 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data) | |||
1365 | return target; | 1415 | return target; |
1366 | } | 1416 | } |
1367 | 1417 | ||
1418 | /* Append bytes to the end of a chunk. Returns NULL if there isn't sufficient | ||
1419 | * space in the chunk | ||
1420 | */ | ||
1421 | void *sctp_addto_chunk_fixed(struct sctp_chunk *chunk, | ||
1422 | int len, const void *data) | ||
1423 | { | ||
1424 | if (skb_tailroom(chunk->skb) > len) | ||
1425 | return sctp_addto_chunk(chunk, len, data); | ||
1426 | else | ||
1427 | return NULL; | ||
1428 | } | ||
1429 | |||
1368 | /* Append bytes from user space to the end of a chunk. Will panic if | 1430 | /* Append bytes from user space to the end of a chunk. Will panic if |
1369 | * chunk is not big enough. | 1431 | * chunk is not big enough. |
1370 | * Returns a kernel err value. | 1432 | * Returns a kernel err value. |
@@ -1968,13 +2030,12 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc, | |||
1968 | * returning multiple unknown parameters. | 2030 | * returning multiple unknown parameters. |
1969 | */ | 2031 | */ |
1970 | if (NULL == *errp) | 2032 | if (NULL == *errp) |
1971 | *errp = sctp_make_op_error_space(asoc, chunk, | 2033 | *errp = sctp_make_op_error_fixed(asoc, chunk); |
1972 | ntohs(chunk->chunk_hdr->length)); | ||
1973 | 2034 | ||
1974 | if (*errp) { | 2035 | if (*errp) { |
1975 | sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM, | 2036 | sctp_init_cause_fixed(*errp, SCTP_ERROR_UNKNOWN_PARAM, |
1976 | WORD_ROUND(ntohs(param.p->length))); | 2037 | WORD_ROUND(ntohs(param.p->length))); |
1977 | sctp_addto_chunk(*errp, | 2038 | sctp_addto_chunk_fixed(*errp, |
1978 | WORD_ROUND(ntohs(param.p->length)), | 2039 | WORD_ROUND(ntohs(param.p->length)), |
1979 | param.v); | 2040 | param.v); |
1980 | } else { | 2041 | } else { |
@@ -3309,21 +3370,6 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, | |||
3309 | sctp_chunk_free(asconf); | 3370 | sctp_chunk_free(asconf); |
3310 | asoc->addip_last_asconf = NULL; | 3371 | asoc->addip_last_asconf = NULL; |
3311 | 3372 | ||
3312 | /* Send the next asconf chunk from the addip chunk queue. */ | ||
3313 | if (!list_empty(&asoc->addip_chunk_list)) { | ||
3314 | struct list_head *entry = asoc->addip_chunk_list.next; | ||
3315 | asconf = list_entry(entry, struct sctp_chunk, list); | ||
3316 | |||
3317 | list_del_init(entry); | ||
3318 | |||
3319 | /* Hold the chunk until an ASCONF_ACK is received. */ | ||
3320 | sctp_chunk_hold(asconf); | ||
3321 | if (sctp_primitive_ASCONF(asoc, asconf)) | ||
3322 | sctp_chunk_free(asconf); | ||
3323 | else | ||
3324 | asoc->addip_last_asconf = asconf; | ||
3325 | } | ||
3326 | |||
3327 | return retval; | 3373 | return retval; |
3328 | } | 3374 | } |
3329 | 3375 | ||