* abort chunk.
*/
void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code,
- const void *payload, size_t paylen)
+ size_t paylen)
{
sctp_errhdr_t err;
__u16 len;
len = sizeof(sctp_errhdr_t) + paylen;
err.length = htons(len);
chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err);
- sctp_addto_chunk(chunk, paylen, payload);
}
/* 3.3.2 Initiation (INIT) (1)
sctp_supported_addrs_param_t sat;
__be16 types[2];
sctp_adaptation_ind_param_t aiparam;
+ sctp_supported_ext_param_t ext_param;
+ int num_ext = 0;
+ __u8 extensions[3];
+ sctp_paramhdr_t *auth_chunks = NULL,
+ *auth_hmacs = NULL;
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN(num_types);
chunksize += sizeof(ecap_param);
- if (sctp_prsctp_enable)
+ if (sctp_prsctp_enable) {
chunksize += sizeof(prsctp_param);
+ extensions[num_ext] = SCTP_CID_FWD_TSN;
+ num_ext += 1;
+ }
+ /* ADDIP: Section 4.2.7:
+ * An implementation supporting this extension [ADDIP] MUST list
+ * the ASCONF,the ASCONF-ACK, and the AUTH chunks in its INIT and
+ * INIT-ACK parameters.
+ */
+ if (sctp_addip_enable) {
+ extensions[num_ext] = SCTP_CID_ASCONF;
+ extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
+ num_ext += 2;
+ }
+
chunksize += sizeof(aiparam);
chunksize += vparam_len;
+ /* Account for AUTH related parameters */
+ if (sctp_auth_enable) {
+ /* Add random parameter length*/
+ chunksize += sizeof(asoc->c.auth_random);
+
+ /* Add HMACS parameter length if any were defined */
+ auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs;
+ if (auth_hmacs->length)
+ chunksize += ntohs(auth_hmacs->length);
+ else
+ auth_hmacs = NULL;
+
+ /* Add CHUNKS parameter length */
+ auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks;
+ if (auth_chunks->length)
+ chunksize += ntohs(auth_chunks->length);
+ else
+ auth_hmacs = NULL;
+
+ extensions[num_ext] = SCTP_CID_AUTH;
+ num_ext += 1;
+ }
+
+ /* If we have any extensions to report, account for that */
+ if (num_ext)
+ chunksize += sizeof(sctp_supported_ext_param_t) + num_ext;
+
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
* Note 3: An INIT chunk MUST NOT contain more than one Host
sctp_addto_chunk(retval, num_types * sizeof(__u16), &types);
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
+
+ /* Add the supported extensions paramter. Be nice and add this
+ * fist before addiding the parameters for the extensions themselves
+ */
+ if (num_ext) {
+ ext_param.param_hdr.type = SCTP_PARAM_SUPPORTED_EXT;
+ ext_param.param_hdr.length =
+ htons(sizeof(sctp_supported_ext_param_t) + num_ext);
+ sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t),
+ &ext_param);
+ sctp_addto_chunk(retval, num_ext, extensions);
+ }
+
if (sctp_prsctp_enable)
sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
+
aiparam.param_hdr.type = SCTP_PARAM_ADAPTATION_LAYER_IND;
aiparam.param_hdr.length = htons(sizeof(aiparam));
aiparam.adaptation_ind = htonl(sp->adaptation_ind);
sctp_addto_chunk(retval, sizeof(aiparam), &aiparam);
+
+ /* Add SCTP-AUTH chunks to the parameter list */
+ if (sctp_auth_enable) {
+ sctp_addto_chunk(retval, sizeof(asoc->c.auth_random),
+ asoc->c.auth_random);
+ if (auth_hmacs)
+ sctp_addto_chunk(retval, ntohs(auth_hmacs->length),
+ auth_hmacs);
+ if (auth_chunks)
+ sctp_addto_chunk(retval, ntohs(auth_chunks->length),
+ auth_chunks);
+ }
nodata:
kfree(addrs.v);
return retval;
int cookie_len;
size_t chunksize;
sctp_adaptation_ind_param_t aiparam;
+ sctp_supported_ext_param_t ext_param;
+ int num_ext = 0;
+ __u8 extensions[3];
+ sctp_paramhdr_t *auth_chunks = NULL,
+ *auth_hmacs = NULL,
+ *auth_random = NULL;
retval = NULL;
chunksize += sizeof(ecap_param);
/* Tell peer that we'll do PR-SCTP only if peer advertised. */
- if (asoc->peer.prsctp_capable)
+ if (asoc->peer.prsctp_capable) {
chunksize += sizeof(prsctp_param);
+ extensions[num_ext] = SCTP_CID_FWD_TSN;
+ num_ext += 1;
+ }
+
+ if (sctp_addip_enable) {
+ extensions[num_ext] = SCTP_CID_ASCONF;
+ extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
+ num_ext += 2;
+ }
+ chunksize += sizeof(ext_param) + num_ext;
chunksize += sizeof(aiparam);
+ if (asoc->peer.auth_capable) {
+ auth_random = (sctp_paramhdr_t *)asoc->c.auth_random;
+ chunksize += ntohs(auth_random->length);
+
+ auth_hmacs = (sctp_paramhdr_t *)asoc->c.auth_hmacs;
+ if (auth_hmacs->length)
+ chunksize += ntohs(auth_hmacs->length);
+ else
+ auth_hmacs = NULL;
+
+ auth_chunks = (sctp_paramhdr_t *)asoc->c.auth_chunks;
+ if (auth_chunks->length)
+ chunksize += ntohs(auth_chunks->length);
+ else
+ auth_chunks = NULL;
+
+ extensions[num_ext] = SCTP_CID_AUTH;
+ num_ext += 1;
+ }
+
/* Now allocate and fill out the chunk. */
retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize);
if (!retval)
sctp_addto_chunk(retval, cookie_len, cookie);
if (asoc->peer.ecn_capable)
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
+ if (num_ext) {
+ ext_param.param_hdr.type = SCTP_PARAM_SUPPORTED_EXT;
+ ext_param.param_hdr.length =
+ htons(sizeof(sctp_supported_ext_param_t) + num_ext);
+ sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t),
+ &ext_param);
+ sctp_addto_chunk(retval, num_ext, extensions);
+ }
if (asoc->peer.prsctp_capable)
sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
aiparam.adaptation_ind = htonl(sctp_sk(asoc->base.sk)->adaptation_ind);
sctp_addto_chunk(retval, sizeof(aiparam), &aiparam);
+ if (asoc->peer.auth_capable) {
+ sctp_addto_chunk(retval, ntohs(auth_random->length),
+ auth_random);
+ if (auth_hmacs)
+ sctp_addto_chunk(retval, ntohs(auth_hmacs->length),
+ auth_hmacs);
+ if (auth_chunks)
+ sctp_addto_chunk(retval, ntohs(auth_chunks->length),
+ auth_chunks);
+ }
+
/* We need to remove the const qualifier at this point. */
retval->asoc = (struct sctp_association *) asoc;
/* Put the tsn back into network byte order. */
payload = htonl(tsn);
- sctp_init_cause(retval, SCTP_ERROR_NO_DATA, (const void *)&payload,
- sizeof(payload));
+ sctp_init_cause(retval, SCTP_ERROR_NO_DATA, sizeof(payload));
+ sctp_addto_chunk(retval, sizeof(payload), (const void *)&payload);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
goto err_copy;
}
- sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen);
+ sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, paylen);
+ sctp_addto_chunk(retval, paylen, payload);
if (paylen)
kfree(payload);
return retval;
}
+/* Append bytes to the end of a parameter. Will panic if chunk is not big
+ * enough.
+ */
+static void *sctp_addto_param(struct sctp_chunk *chunk, int len,
+ const void *data)
+{
+ void *target;
+ int chunklen = ntohs(chunk->chunk_hdr->length);
+
+ target = skb_put(chunk->skb, len);
+
+ memcpy(target, data, len);
+
+ /* Adjust the chunk length field. */
+ chunk->chunk_hdr->length = htons(chunklen + len);
+ chunk->chunk_end = skb_tail_pointer(chunk->skb);
+
+ return target;
+}
+
/* Make an ABORT chunk with a PROTOCOL VIOLATION cause code. */
struct sctp_chunk *sctp_make_abort_violation(
const struct sctp_association *asoc,
struct sctp_paramhdr phdr;
retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen
- + sizeof(sctp_chunkhdr_t));
+ + sizeof(sctp_paramhdr_t));
if (!retval)
goto end;
- sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, payload, paylen);
+ sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, paylen
+ + sizeof(sctp_paramhdr_t));
phdr.type = htons(chunk->chunk_hdr->type);
phdr.length = chunk->chunk_hdr->length;
- sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &phdr);
+ sctp_addto_chunk(retval, paylen, payload);
+ sctp_addto_param(retval, sizeof(sctp_paramhdr_t), &phdr);
end:
return retval;
if (!retval)
goto nodata;
- sctp_init_cause(retval, cause_code, payload, paylen);
+ sctp_init_cause(retval, cause_code, paylen);
+ sctp_addto_chunk(retval, paylen, payload);
nodata:
return retval;
void *target;
void *padding;
int chunklen = ntohs(chunk->chunk_hdr->length);
- int padlen = chunklen % 4;
+ int padlen = WORD_ROUND(chunklen) - chunklen;
padding = skb_put(chunk->skb, padlen);
target = skb_put(chunk->skb, len);
*/
void sctp_chunk_assign_ssn(struct sctp_chunk *chunk)
{
+ struct sctp_datamsg *msg;
+ struct sctp_chunk *lchunk;
+ struct sctp_stream *stream;
__u16 ssn;
__u16 sid;
if (chunk->has_ssn)
return;
- /* This is the last possible instant to assign a SSN. */
- if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
- ssn = 0;
- } else {
- sid = ntohs(chunk->subh.data_hdr->stream);
- if (chunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
- ssn = sctp_ssn_next(&chunk->asoc->ssnmap->out, sid);
- else
- ssn = sctp_ssn_peek(&chunk->asoc->ssnmap->out, sid);
- }
+ /* All fragments will be on the same stream */
+ sid = ntohs(chunk->subh.data_hdr->stream);
+ stream = &chunk->asoc->ssnmap->out;
- chunk->subh.data_hdr->ssn = htons(ssn);
- chunk->has_ssn = 1;
+ /* Now assign the sequence number to the entire message.
+ * All fragments must have the same stream sequence number.
+ */
+ msg = chunk->msg;
+ list_for_each_entry(lchunk, &msg->chunks, frag_list) {
+ if (lchunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
+ ssn = 0;
+ } else {
+ if (lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
+ ssn = sctp_ssn_next(stream, sid);
+ else
+ ssn = sctp_ssn_peek(stream, sid);
+ }
+
+ lchunk->subh.data_hdr->ssn = htons(ssn);
+ lchunk->has_ssn = 1;
+ }
}
/* Helper function to assign a TSN if needed. This assumes that both
__be32 n = htonl(usecs);
sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE,
- &n, sizeof(n));
+ sizeof(n));
+ sctp_addto_chunk(*errp, sizeof(n), &n);
*error = -SCTP_IERROR_STALE_COOKIE;
} else
*error = -SCTP_IERROR_NOMEM;
/* Also, add the destination address. */
if (list_empty(&retval->base.bind_addr.address_list)) {
sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 1,
- GFP_ATOMIC);
+ GFP_ATOMIC);
}
retval->next_tsn = retval->c.initial_tsn;
report.num_missing = htonl(1);
report.type = paramtype;
sctp_init_cause(*errp, SCTP_ERROR_MISS_PARAM,
- &report, sizeof(report));
+ sizeof(report));
+ sctp_addto_chunk(*errp, sizeof(report), &report);
}
/* Stop processing this chunk. */
*errp = sctp_make_op_error_space(asoc, chunk, 0);
if (*errp)
- sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, NULL, 0);
+ sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, 0);
/* Stop processing this chunk. */
return 0;
*errp = sctp_make_op_error_space(asoc, chunk, payload_len);
if (*errp) {
- sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION, error,
- sizeof(error));
- sctp_addto_chunk(*errp, sizeof(sctp_paramhdr_t), param);
+ sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION,
+ sizeof(error) + sizeof(sctp_paramhdr_t));
+ sctp_addto_chunk(*errp, sizeof(error), error);
+ sctp_addto_param(*errp, sizeof(sctp_paramhdr_t), param);
}
return 0;
if (!*errp)
*errp = sctp_make_op_error_space(asoc, chunk, len);
- if (*errp)
- sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED,
- param.v, len);
+ if (*errp) {
+ sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED, len);
+ sctp_addto_chunk(*errp, len, param.v);
+ }
/* Stop processing this chunk. */
return 0;
}
+static void sctp_process_ext_param(struct sctp_association *asoc,
+ union sctp_params param)
+{
+ __u16 num_ext = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
+ int i;
+
+ for (i = 0; i < num_ext; i++) {
+ switch (param.ext->chunks[i]) {
+ case SCTP_CID_FWD_TSN:
+ if (sctp_prsctp_enable &&
+ !asoc->peer.prsctp_capable)
+ asoc->peer.prsctp_capable = 1;
+ break;
+ case SCTP_CID_AUTH:
+ /* if the peer reports AUTH, assume that he
+ * supports AUTH.
+ */
+ asoc->peer.auth_capable = 1;
+ break;
+ case SCTP_CID_ASCONF:
+ case SCTP_CID_ASCONF_ACK:
+ /* don't need to do anything for ASCONF */
+ default:
+ break;
+ }
+ }
+}
+
/* RFC 3.2.1 & the Implementers Guide 2.2.
*
* The Parameter Types are encoded such that the
*errp = sctp_make_op_error_space(asoc, chunk,
ntohs(chunk->chunk_hdr->length));
- if (*errp)
+ if (*errp) {
sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
- param.v,
WORD_ROUND(ntohs(param.p->length)));
+ sctp_addto_chunk(*errp,
+ WORD_ROUND(ntohs(param.p->length)),
+ param.v);
+ }
break;
case SCTP_PARAM_ACTION_SKIP:
if (*errp) {
sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
- param.v,
WORD_ROUND(ntohs(param.p->length)));
+ sctp_addto_chunk(*errp,
+ WORD_ROUND(ntohs(param.p->length)),
+ param.v);
} else {
/* If there is no memory for generating the ERROR
* report as specified, an ABORT will be triggered
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
case SCTP_PARAM_ECN_CAPABLE:
case SCTP_PARAM_ADAPTATION_LAYER_IND:
+ case SCTP_PARAM_SUPPORTED_EXT:
break;
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);
+
case SCTP_PARAM_FWD_TSN_SUPPORT:
if (sctp_prsctp_enable)
break;
+ goto fallthrough;
+
+ case SCTP_PARAM_RANDOM:
+ if (!sctp_auth_enable)
+ goto fallthrough;
+
+ /* SCTP-AUTH: Secion 6.1
+ * If the random number is not 32 byte long the association
+ * MUST be aborted. The ABORT chunk SHOULD contain the error
+ * cause 'Protocol Violation'.
+ */
+ if (SCTP_AUTH_RANDOM_LENGTH !=
+ ntohs(param.p->length) - sizeof(sctp_paramhdr_t))
+ return sctp_process_inv_paramlength(asoc, param.p,
+ chunk, err_chunk);
+ break;
+
+ case SCTP_PARAM_CHUNKS:
+ if (!sctp_auth_enable)
+ goto fallthrough;
+
+ /* SCTP-AUTH: Section 3.2
+ * The CHUNKS parameter MUST be included once in the INIT or
+ * 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);
+ break;
+
+ case SCTP_PARAM_HMAC_ALGO:
+ if (!sctp_auth_enable)
+ break;
/* Fall Through */
+fallthrough:
default:
SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
ntohs(param.p->type), cid);
* 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 - sizeof(sctp_paramhdr_t)) {
+ if (param.v != (void*)chunk->chunk_end) {
sctp_process_inv_paramlength(asoc, param.p, chunk, errp);
return 0;
}
}
/* Process the initialization parameters. */
-
sctp_walk_params(param, peer_init, init_hdr.params) {
if (!sctp_process_param(asoc, param, peer_addr, gfp))
goto clean_up;
}
+ /* AUTH: After processing the parameters, make sure that we
+ * have all the required info to potentially do authentications.
+ */
+ if (asoc->peer.auth_capable && (!asoc->peer.peer_random ||
+ !asoc->peer.peer_hmacs))
+ asoc->peer.auth_capable = 0;
+
/* Walk list of transports, removing transports in the UNKNOWN state. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
asoc->peer.adaptation_ind = param.aind->adaptation_ind;
break;
+ case SCTP_PARAM_SUPPORTED_EXT:
+ sctp_process_ext_param(asoc, param);
+ break;
+
case SCTP_PARAM_FWD_TSN_SUPPORT:
if (sctp_prsctp_enable) {
asoc->peer.prsctp_capable = 1;
break;
}
/* Fall Through */
+ goto fall_through;
+
+ case SCTP_PARAM_RANDOM:
+ if (!sctp_auth_enable)
+ goto fall_through;
+
+ /* Save peer's random parameter */
+ asoc->peer.peer_random = kmemdup(param.p,
+ ntohs(param.p->length), gfp);
+ if (!asoc->peer.peer_random) {
+ retval = 0;
+ break;
+ }
+ break;
+
+ case SCTP_PARAM_HMAC_ALGO:
+ if (!sctp_auth_enable)
+ goto fall_through;
+
+ /* Save peer's HMAC list */
+ asoc->peer.peer_hmacs = kmemdup(param.p,
+ ntohs(param.p->length), gfp);
+ if (!asoc->peer.peer_hmacs) {
+ retval = 0;
+ break;
+ }
+
+ /* Set the default HMAC the peer requested*/
+ sctp_auth_asoc_set_default_hmac(asoc, param.hmac_algo);
+ break;
+
+ case SCTP_PARAM_CHUNKS:
+ if (!sctp_auth_enable)
+ goto fall_through;
+
+ asoc->peer.peer_chunks = kmemdup(param.p,
+ ntohs(param.p->length), gfp);
+ if (!asoc->peer.peer_chunks)
+ retval = 0;
+ break;
+fall_through:
default:
/* Any unrecognized parameters should have been caught
* and handled by sctp_verify_param() which should be
return SCTP_ERROR_NO_ERROR;
}
+/* Verify the ASCONF packet before we process it. */
+int sctp_verify_asconf(const struct sctp_association *asoc,
+ struct sctp_paramhdr *param_hdr, void *chunk_end,
+ struct sctp_paramhdr **errp) {
+ sctp_addip_param_t *asconf_param;
+ union sctp_params param;
+ int length, plen;
+
+ param.v = (sctp_paramhdr_t *) param_hdr;
+ while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) {
+ length = ntohs(param.p->length);
+ *errp = param.p;
+
+ if (param.v > chunk_end - length ||
+ length < sizeof(sctp_paramhdr_t))
+ return 0;
+
+ switch (param.p->type) {
+ case SCTP_PARAM_ADD_IP:
+ case SCTP_PARAM_DEL_IP:
+ case SCTP_PARAM_SET_PRIMARY:
+ asconf_param = (sctp_addip_param_t *)param.v;
+ plen = ntohs(asconf_param->param_hdr.length);
+ if (plen < sizeof(sctp_addip_param_t) +
+ sizeof(sctp_paramhdr_t))
+ return 0;
+ break;
+ case SCTP_PARAM_SUCCESS_REPORT:
+ case SCTP_PARAM_ADAPTATION_LAYER_IND:
+ if (length != sizeof(sctp_addip_param_t))
+ return 0;
+
+ break;
+ default:
+ break;
+ }
+
+ param.v += WORD_ROUND(length);
+ }
+
+ if (param.v != chunk_end)
+ return 0;
+
+ return 1;
+}
+
/* Process an incoming ASCONF chunk with the next expected serial no. and
* return an ASCONF_ACK chunk to be sent in response.
*/
switch (asconf_param->param_hdr.type) {
case SCTP_PARAM_ADD_IP:
- sctp_local_bh_disable();
- sctp_write_lock(&asoc->base.addr_lock);
- list_for_each(pos, &bp->address_list) {
- saddr = list_entry(pos, struct sctp_sockaddr_entry, list);
+ /* This is always done in BH context with a socket lock
+ * held, so the list can not change.
+ */
+ list_for_each_entry(saddr, &bp->address_list, list) {
if (sctp_cmp_addr_exact(&saddr->a, &addr))
saddr->use_as_src = 1;
}
- sctp_write_unlock(&asoc->base.addr_lock);
- sctp_local_bh_enable();
break;
case SCTP_PARAM_DEL_IP:
- sctp_local_bh_disable();
- sctp_write_lock(&asoc->base.addr_lock);
- retval = sctp_del_bind_addr(bp, &addr);
- sctp_write_unlock(&asoc->base.addr_lock);
- sctp_local_bh_enable();
+ retval = sctp_del_bind_addr(bp, &addr, call_rcu_bh);
list_for_each(pos, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport,
transports);