|
@@ -1788,9 +1788,14 @@ static int sctp_process_inv_paramlength(const struct sctp_association *asoc,
|
|
|
sizeof(sctp_paramhdr_t);
|
|
|
|
|
|
|
|
|
+ /* This is a fatal error. Any accumulated non-fatal errors are
|
|
|
+ * not reported.
|
|
|
+ */
|
|
|
+ if (*errp)
|
|
|
+ sctp_chunk_free(*errp);
|
|
|
+
|
|
|
/* Create an error chunk and fill it in with our payload. */
|
|
|
- if (!*errp)
|
|
|
- *errp = sctp_make_op_error_space(asoc, chunk, payload_len);
|
|
|
+ *errp = sctp_make_op_error_space(asoc, chunk, payload_len);
|
|
|
|
|
|
if (*errp) {
|
|
|
sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION,
|
|
@@ -1813,9 +1818,15 @@ static int sctp_process_hn_param(const struct sctp_association *asoc,
|
|
|
{
|
|
|
__u16 len = ntohs(param.p->length);
|
|
|
|
|
|
- /* Make an ERROR chunk. */
|
|
|
- if (!*errp)
|
|
|
- *errp = sctp_make_op_error_space(asoc, chunk, len);
|
|
|
+ /* Processing of the HOST_NAME parameter will generate an
|
|
|
+ * ABORT. If we've accumulated any non-fatal errors, they
|
|
|
+ * would be unrecognized parameters and we should not include
|
|
|
+ * them in the ABORT.
|
|
|
+ */
|
|
|
+ if (*errp)
|
|
|
+ sctp_chunk_free(*errp);
|
|
|
+
|
|
|
+ *errp = sctp_make_op_error_space(asoc, chunk, len);
|
|
|
|
|
|
if (*errp) {
|
|
|
sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED, len);
|
|
@@ -1847,7 +1858,7 @@ static void sctp_process_ext_param(struct sctp_association *asoc,
|
|
|
break;
|
|
|
case SCTP_CID_ASCONF:
|
|
|
case SCTP_CID_ASCONF_ACK:
|
|
|
- asoc->peer.addip_capable = 1;
|
|
|
+ asoc->peer.asconf_capable = 1;
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
@@ -1862,56 +1873,40 @@ static void sctp_process_ext_param(struct sctp_association *asoc,
|
|
|
* taken if the processing endpoint does not recognize the
|
|
|
* Parameter Type.
|
|
|
*
|
|
|
- * 00 - Stop processing this SCTP chunk and discard it,
|
|
|
- * do not process any further chunks within it.
|
|
|
+ * 00 - Stop processing this parameter; do not process any further
|
|
|
+ * parameters within this chunk
|
|
|
*
|
|
|
- * 01 - Stop processing this SCTP chunk and discard it,
|
|
|
- * do not process any further chunks within it, and report
|
|
|
- * the unrecognized parameter in an 'Unrecognized
|
|
|
- * Parameter Type' (in either an ERROR or in the INIT ACK).
|
|
|
+ * 01 - Stop processing this parameter, do not process any further
|
|
|
+ * parameters within this chunk, and report the unrecognized
|
|
|
+ * parameter in an 'Unrecognized Parameter' ERROR chunk.
|
|
|
*
|
|
|
* 10 - Skip this parameter and continue processing.
|
|
|
*
|
|
|
* 11 - Skip this parameter and continue processing but
|
|
|
* report the unrecognized parameter in an
|
|
|
- * 'Unrecognized Parameter Type' (in either an ERROR or in
|
|
|
- * the INIT ACK).
|
|
|
+ * 'Unrecognized Parameter' ERROR chunk.
|
|
|
*
|
|
|
* Return value:
|
|
|
- * 0 - discard the chunk
|
|
|
- * 1 - continue with the chunk
|
|
|
+ * SCTP_IERROR_NO_ERROR - continue with the chunk
|
|
|
+ * SCTP_IERROR_ERROR - stop and report an error.
|
|
|
+ * SCTP_IERROR_NOMEME - out of memory.
|
|
|
*/
|
|
|
-static int sctp_process_unk_param(const struct sctp_association *asoc,
|
|
|
- union sctp_params param,
|
|
|
- struct sctp_chunk *chunk,
|
|
|
- struct sctp_chunk **errp)
|
|
|
+static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc,
|
|
|
+ union sctp_params param,
|
|
|
+ struct sctp_chunk *chunk,
|
|
|
+ struct sctp_chunk **errp)
|
|
|
{
|
|
|
- int retval = 1;
|
|
|
+ int retval = SCTP_IERROR_NO_ERROR;
|
|
|
|
|
|
switch (param.p->type & SCTP_PARAM_ACTION_MASK) {
|
|
|
case SCTP_PARAM_ACTION_DISCARD:
|
|
|
- retval = 0;
|
|
|
- break;
|
|
|
- case SCTP_PARAM_ACTION_DISCARD_ERR:
|
|
|
- retval = 0;
|
|
|
- /* Make an ERROR chunk, preparing enough room for
|
|
|
- * returning multiple unknown parameters.
|
|
|
- */
|
|
|
- if (NULL == *errp)
|
|
|
- *errp = sctp_make_op_error_space(asoc, chunk,
|
|
|
- ntohs(chunk->chunk_hdr->length));
|
|
|
-
|
|
|
- if (*errp) {
|
|
|
- sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
|
|
|
- WORD_ROUND(ntohs(param.p->length)));
|
|
|
- sctp_addto_chunk(*errp,
|
|
|
- WORD_ROUND(ntohs(param.p->length)),
|
|
|
- param.v);
|
|
|
- }
|
|
|
-
|
|
|
+ retval = SCTP_IERROR_ERROR;
|
|
|
break;
|
|
|
case SCTP_PARAM_ACTION_SKIP:
|
|
|
break;
|
|
|
+ case SCTP_PARAM_ACTION_DISCARD_ERR:
|
|
|
+ retval = SCTP_IERROR_ERROR;
|
|
|
+ /* Fall through */
|
|
|
case SCTP_PARAM_ACTION_SKIP_ERR:
|
|
|
/* Make an ERROR chunk, preparing enough room for
|
|
|
* returning multiple unknown parameters.
|
|
@@ -1932,9 +1927,8 @@ static int sctp_process_unk_param(const struct sctp_association *asoc,
|
|
|
* to the peer and the association won't be
|
|
|
* established.
|
|
|
*/
|
|
|
- retval = 0;
|
|
|
+ retval = SCTP_IERROR_NOMEM;
|
|
|
}
|
|
|
-
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
@@ -1943,18 +1937,20 @@ static int sctp_process_unk_param(const struct sctp_association *asoc,
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-/* Find unrecognized parameters in the chunk.
|
|
|
+/* Verify variable length parameters
|
|
|
* Return values:
|
|
|
- * 0 - discard the chunk
|
|
|
- * 1 - continue with the chunk
|
|
|
+ * SCTP_IERROR_ABORT - trigger an ABORT
|
|
|
+ * SCTP_IERROR_NOMEM - out of memory (abort)
|
|
|
+ * SCTP_IERROR_ERROR - stop processing, trigger an ERROR
|
|
|
+ * SCTP_IERROR_NO_ERROR - continue with the chunk
|
|
|
*/
|
|
|
-static int sctp_verify_param(const struct sctp_association *asoc,
|
|
|
- union sctp_params param,
|
|
|
- sctp_cid_t cid,
|
|
|
- struct sctp_chunk *chunk,
|
|
|
- struct sctp_chunk **err_chunk)
|
|
|
+static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
|
|
|
+ union sctp_params param,
|
|
|
+ sctp_cid_t cid,
|
|
|
+ struct sctp_chunk *chunk,
|
|
|
+ struct sctp_chunk **err_chunk)
|
|
|
{
|
|
|
- int retval = 1;
|
|
|
+ int retval = SCTP_IERROR_NO_ERROR;
|
|
|
|
|
|
/* FIXME - This routine is not looking at each parameter per the
|
|
|
* chunk type, i.e., unrecognized parameters should be further
|
|
@@ -1976,7 +1972,9 @@ static int sctp_verify_param(const struct sctp_association *asoc,
|
|
|
|
|
|
case SCTP_PARAM_HOST_NAME_ADDRESS:
|
|
|
/* Tell the peer, we won't support this param. */
|
|
|
- return sctp_process_hn_param(asoc, param, chunk, err_chunk);
|
|
|
+ sctp_process_hn_param(asoc, param, chunk, err_chunk);
|
|
|
+ retval = SCTP_IERROR_ABORT;
|
|
|
+ break;
|
|
|
|
|
|
case SCTP_PARAM_FWD_TSN_SUPPORT:
|
|
|
if (sctp_prsctp_enable)
|
|
@@ -1993,9 +1991,11 @@ static int sctp_verify_param(const struct sctp_association *asoc,
|
|
|
* cause 'Protocol Violation'.
|
|
|
*/
|
|
|
if (SCTP_AUTH_RANDOM_LENGTH !=
|
|
|
- ntohs(param.p->length) - sizeof(sctp_paramhdr_t))
|
|
|
- return sctp_process_inv_paramlength(asoc, param.p,
|
|
|
+ ntohs(param.p->length) - sizeof(sctp_paramhdr_t)) {
|
|
|
+ sctp_process_inv_paramlength(asoc, param.p,
|
|
|
chunk, err_chunk);
|
|
|
+ retval = SCTP_IERROR_ABORT;
|
|
|
+ }
|
|
|
break;
|
|
|
|
|
|
case SCTP_PARAM_CHUNKS:
|
|
@@ -2007,9 +2007,11 @@ static int sctp_verify_param(const struct sctp_association *asoc,
|
|
|
* INIT-ACK chunk if the sender wants to receive authenticated
|
|
|
* chunks. Its maximum length is 260 bytes.
|
|
|
*/
|
|
|
- if (260 < ntohs(param.p->length))
|
|
|
- return sctp_process_inv_paramlength(asoc, param.p,
|
|
|
- chunk, err_chunk);
|
|
|
+ if (260 < ntohs(param.p->length)) {
|
|
|
+ sctp_process_inv_paramlength(asoc, param.p,
|
|
|
+ chunk, err_chunk);
|
|
|
+ retval = SCTP_IERROR_ABORT;
|
|
|
+ }
|
|
|
break;
|
|
|
|
|
|
case SCTP_PARAM_HMAC_ALGO:
|
|
@@ -2020,8 +2022,7 @@ fallthrough:
|
|
|
default:
|
|
|
SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
|
|
|
ntohs(param.p->type), cid);
|
|
|
- return sctp_process_unk_param(asoc, param, chunk, err_chunk);
|
|
|
-
|
|
|
+ retval = sctp_process_unk_param(asoc, param, chunk, err_chunk);
|
|
|
break;
|
|
|
}
|
|
|
return retval;
|
|
@@ -2036,6 +2037,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
|
|
|
{
|
|
|
union sctp_params param;
|
|
|
int has_cookie = 0;
|
|
|
+ int result;
|
|
|
|
|
|
/* Verify stream values are non-zero. */
|
|
|
if ((0 == peer_init->init_hdr.num_outbound_streams) ||
|
|
@@ -2043,8 +2045,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
|
|
|
(0 == peer_init->init_hdr.init_tag) ||
|
|
|
(SCTP_DEFAULT_MINWINDOW > ntohl(peer_init->init_hdr.a_rwnd))) {
|
|
|
|
|
|
- sctp_process_inv_mandatory(asoc, chunk, errp);
|
|
|
- return 0;
|
|
|
+ return sctp_process_inv_mandatory(asoc, chunk, errp);
|
|
|
}
|
|
|
|
|
|
/* Check for missing mandatory parameters. */
|
|
@@ -2062,29 +2063,29 @@ int sctp_verify_init(const struct sctp_association *asoc,
|
|
|
* VIOLATION error. We build the ERROR chunk here and let the normal
|
|
|
* error handling code build and send the packet.
|
|
|
*/
|
|
|
- if (param.v != (void*)chunk->chunk_end) {
|
|
|
- sctp_process_inv_paramlength(asoc, param.p, chunk, errp);
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ if (param.v != (void*)chunk->chunk_end)
|
|
|
+ return sctp_process_inv_paramlength(asoc, param.p, chunk, errp);
|
|
|
|
|
|
/* The only missing mandatory param possible today is
|
|
|
* the state cookie for an INIT-ACK chunk.
|
|
|
*/
|
|
|
- if ((SCTP_CID_INIT_ACK == cid) && !has_cookie) {
|
|
|
- sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE,
|
|
|
- chunk, errp);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- /* Find unrecognized parameters. */
|
|
|
+ if ((SCTP_CID_INIT_ACK == cid) && !has_cookie)
|
|
|
+ return sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE,
|
|
|
+ chunk, errp);
|
|
|
|
|
|
+ /* Verify all the variable length parameters */
|
|
|
sctp_walk_params(param, peer_init, init_hdr.params) {
|
|
|
|
|
|
- if (!sctp_verify_param(asoc, param, cid, chunk, errp)) {
|
|
|
- if (SCTP_PARAM_HOST_NAME_ADDRESS == param.p->type)
|
|
|
+ result = sctp_verify_param(asoc, param, cid, chunk, errp);
|
|
|
+ switch (result) {
|
|
|
+ case SCTP_IERROR_ABORT:
|
|
|
+ case SCTP_IERROR_NOMEM:
|
|
|
return 0;
|
|
|
- else
|
|
|
+ case SCTP_IERROR_ERROR:
|
|
|
return 1;
|
|
|
+ case SCTP_IERROR_NO_ERROR:
|
|
|
+ default:
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
} /* for (loop through all parameters) */
|
|
@@ -2137,11 +2138,14 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
|
|
|
|
|
|
/* If the peer claims support for ADD-IP without support
|
|
|
* for AUTH, disable support for ADD-IP.
|
|
|
+ * Do this only if backward compatible mode is turned off.
|
|
|
*/
|
|
|
- if (asoc->peer.addip_capable && !asoc->peer.auth_capable) {
|
|
|
+ if (!sctp_addip_noauth &&
|
|
|
+ (asoc->peer.asconf_capable && !asoc->peer.auth_capable)) {
|
|
|
asoc->peer.addip_disabled_mask |= (SCTP_PARAM_ADD_IP |
|
|
|
SCTP_PARAM_DEL_IP |
|
|
|
SCTP_PARAM_SET_PRIMARY);
|
|
|
+ asoc->peer.asconf_capable = 0;
|
|
|
}
|
|
|
|
|
|
/* Walk list of transports, removing transports in the UNKNOWN state. */
|
|
@@ -2848,10 +2852,11 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
|
|
|
|
|
|
__be16 err_code;
|
|
|
int length = 0;
|
|
|
- int chunk_len = asconf->skb->len;
|
|
|
+ int chunk_len;
|
|
|
__u32 serial;
|
|
|
int all_param_pass = 1;
|
|
|
|
|
|
+ chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
|
|
|
hdr = (sctp_addiphdr_t *)asconf->skb->data;
|
|
|
serial = ntohl(hdr->serial);
|
|
|
|
|
@@ -2952,13 +2957,17 @@ static int sctp_asconf_param_success(struct sctp_association *asoc,
|
|
|
/* This is always done in BH context with a socket lock
|
|
|
* held, so the list can not change.
|
|
|
*/
|
|
|
+ local_bh_disable();
|
|
|
list_for_each_entry(saddr, &bp->address_list, list) {
|
|
|
if (sctp_cmp_addr_exact(&saddr->a, &addr))
|
|
|
saddr->use_as_src = 1;
|
|
|
}
|
|
|
+ local_bh_enable();
|
|
|
break;
|
|
|
case SCTP_PARAM_DEL_IP:
|
|
|
- retval = sctp_del_bind_addr(bp, &addr, call_rcu_bh);
|
|
|
+ local_bh_disable();
|
|
|
+ retval = sctp_del_bind_addr(bp, &addr);
|
|
|
+ local_bh_enable();
|
|
|
list_for_each(pos, &asoc->peer.transport_addr_list) {
|
|
|
transport = list_entry(pos, struct sctp_transport,
|
|
|
transports);
|
|
@@ -2990,7 +2999,7 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,
|
|
|
sctp_addip_param_t *asconf_ack_param;
|
|
|
sctp_errhdr_t *err_param;
|
|
|
int length;
|
|
|
- int asconf_ack_len = asconf_ack->skb->len;
|
|
|
+ int asconf_ack_len;
|
|
|
__be16 err_code;
|
|
|
|
|
|
if (no_err)
|
|
@@ -2998,6 +3007,9 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,
|
|
|
else
|
|
|
err_code = SCTP_ERROR_REQ_REFUSED;
|
|
|
|
|
|
+ asconf_ack_len = ntohs(asconf_ack->chunk_hdr->length) -
|
|
|
+ sizeof(sctp_chunkhdr_t);
|
|
|
+
|
|
|
/* Skip the addiphdr from the asconf_ack chunk and store a pointer to
|
|
|
* the first asconf_ack parameter.
|
|
|
*/
|