X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fsctp%2Fsm_make_chunk.c;h=0b1ddb1005acf3ba451faa15bf0ff8237f211a1f;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=c2ca732595fa025d5f723ad79ff0e4ba15578d7b;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index c2ca73259..0b1ddb100 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -65,7 +65,20 @@ #include #include -extern kmem_cache_t *sctp_chunk_cachep; +extern struct kmem_cache *sctp_chunk_cachep; + +SCTP_STATIC +struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc, + __u8 type, __u8 flags, int paylen); +static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const struct sctp_chunk *init_chunk, + int *cookie_len, + const __u8 *raw_addrs, int addrs_len); +static int sctp_process_param(struct sctp_association *asoc, + union sctp_params param, + const union sctp_addr *peer_addr, + gfp_t gfp); /* What was the inbound interface for this chunk? */ int sctp_chunk_iif(const struct sctp_chunk *chunk) @@ -98,7 +111,7 @@ static const struct sctp_paramhdr prsctp_param = { * provided chunk, as most cause codes will be embedded inside an * abort chunk. */ -void sctp_init_cause(struct sctp_chunk *chunk, __u16 cause_code, +void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, const void *payload, size_t paylen) { sctp_errhdr_t err; @@ -111,8 +124,8 @@ void sctp_init_cause(struct sctp_chunk *chunk, __u16 cause_code, padlen = len % 4; err.length = htons(len); len += padlen; - sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err); - chunk->subh.err_hdr = sctp_addto_chunk(chunk, paylen, payload); + 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) @@ -161,16 +174,17 @@ void sctp_init_cause(struct sctp_chunk *chunk, __u16 cause_code, */ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, const struct sctp_bind_addr *bp, - int gfp, int vparam_len) + gfp_t gfp, int vparam_len) { sctp_inithdr_t init; union sctp_params addrs; size_t chunksize; struct sctp_chunk *retval = NULL; int num_types, addrs_len = 0; - struct sctp_opt *sp; + struct sctp_sock *sp; sctp_supported_addrs_param_t sat; - __u16 types[2]; + __be16 types[2]; + sctp_adaptation_ind_param_t aiparam; /* RFC 2960 3.3.2 Initiation (INIT) (1) * @@ -196,6 +210,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, chunksize += sizeof(ecap_param); if (sctp_prsctp_enable) chunksize += sizeof(prsctp_param); + chunksize += sizeof(aiparam); chunksize += vparam_len; /* RFC 2960 3.3.2 Initiation (INIT) (1) @@ -234,15 +249,18 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param); 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); nodata: - if (addrs.v) - kfree(addrs.v); + kfree(addrs.v); return retval; } struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, const struct sctp_chunk *chunk, - int gfp, int unkparam_len) + gfp_t gfp, int unkparam_len) { sctp_inithdr_t initack; struct sctp_chunk *retval; @@ -251,6 +269,7 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, sctp_cookie_param_t *cookie; int cookie_len; size_t chunksize; + sctp_adaptation_ind_param_t aiparam; retval = NULL; @@ -284,6 +303,8 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, if (asoc->peer.prsctp_capable) chunksize += sizeof(prsctp_param); + chunksize += sizeof(aiparam); + /* Now allocate and fill out the chunk. */ retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize); if (!retval) @@ -302,6 +323,11 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, if (asoc->peer.prsctp_capable) 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(sctp_sk(asoc->base.sk)->adaptation_ind); + sctp_addto_chunk(retval, sizeof(aiparam), &aiparam); + /* We need to remove the const qualifier at this point. */ retval->asoc = (struct sctp_association *) asoc; @@ -320,8 +346,7 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, nomem_chunk: kfree(cookie); nomem_cookie: - if (addrs.v) - kfree(addrs.v); + kfree(addrs.v); return retval; } @@ -527,7 +552,7 @@ struct sctp_chunk *sctp_make_datafrag_empty(struct sctp_association *asoc, dp.ppid = sinfo->sinfo_ppid; /* Set the flags for an unordered send. */ - if (sinfo->sinfo_flags & MSG_UNORDERED) { + if (sinfo->sinfo_flags & SCTP_UNORDERED) { flags |= SCTP_DATA_UNORDERED; dp.ssn = 0; } else @@ -545,52 +570,6 @@ nodata: return retval; } -/* Make a DATA chunk for the given association. Populate the data - * payload. - */ -struct sctp_chunk *sctp_make_datafrag(struct sctp_association *asoc, - const struct sctp_sndrcvinfo *sinfo, - int data_len, const __u8 *data, - __u8 flags, __u16 ssn) -{ - struct sctp_chunk *retval; - - retval = sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, ssn); - if (retval) - sctp_addto_chunk(retval, data_len, data); - - return retval; -} - -/* Make a DATA chunk for the given association to ride on stream id - * 'stream', with a payload id of 'payload', and a body of 'data'. - */ -struct sctp_chunk *sctp_make_data(struct sctp_association *asoc, - const struct sctp_sndrcvinfo *sinfo, - int data_len, const __u8 *data) -{ - struct sctp_chunk *retval = NULL; - - retval = sctp_make_data_empty(asoc, sinfo, data_len); - if (retval) - sctp_addto_chunk(retval, data_len, data); - return retval; -} - -/* Make a DATA chunk for the given association to ride on stream id - * 'stream', with a payload id of 'payload', and a body big enough to - * hold 'data_len' octets of data. We use this version when we need - * to build the message AFTER allocating memory. - */ -struct sctp_chunk *sctp_make_data_empty(struct sctp_association *asoc, - const struct sctp_sndrcvinfo *sinfo, - int data_len) -{ - __u8 flags = SCTP_DATA_NOT_FRAG; - - return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, 0); -} - /* Create a selective ackowledgement (SACK) for the given * association. This reports on which TSN's we've seen to date, * including duplicates and gaps. @@ -729,7 +708,9 @@ struct sctp_chunk *sctp_make_shutdown_complete( struct sctp_chunk *retval; __u8 flags = 0; - /* Maybe set the T-bit if we have no association. */ + /* Set the T-bit if we have no association (vtag will be + * reflected) + */ flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T; retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_COMPLETE, flags, 0); @@ -751,7 +732,7 @@ struct sctp_chunk *sctp_make_shutdown_complete( } /* Create an ABORT. Note that we set the T bit if we have no - * association. + * association, except when responding to an INIT (sctpimpguide 2.41). */ struct sctp_chunk *sctp_make_abort(const struct sctp_association *asoc, const struct sctp_chunk *chunk, @@ -760,8 +741,16 @@ struct sctp_chunk *sctp_make_abort(const struct sctp_association *asoc, struct sctp_chunk *retval; __u8 flags = 0; - /* Maybe set the T-bit if we have no association. */ - flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T; + /* Set the T-bit if we have no association and 'chunk' is not + * an INIT (vtag will be reflected). + */ + if (!asoc) { + if (chunk && chunk->chunk_hdr && + chunk->chunk_hdr->type == SCTP_CID_INIT) + flags = 0; + else + flags = SCTP_CHUNK_FLAG_T; + } retval = sctp_make_chunk(asoc, SCTP_CID_ABORT, flags, hint); @@ -786,7 +775,7 @@ struct sctp_chunk *sctp_make_abort_no_data( const struct sctp_chunk *chunk, __u32 tsn) { struct sctp_chunk *retval; - __u32 payload; + __be32 payload; retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + sizeof(tsn)); @@ -817,38 +806,26 @@ no_mem: /* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - const struct msghdr *msg) + const struct msghdr *msg, + size_t paylen) { struct sctp_chunk *retval; - void *payload = NULL, *payoff; - size_t paylen = 0; - struct iovec *iov = NULL; - int iovlen = 0; - - if (msg) { - iov = msg->msg_iov; - iovlen = msg->msg_iovlen; - paylen = get_user_iov_size(iov, iovlen); - } + void *payload = NULL; + int err; - retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen); + retval = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t) + paylen); if (!retval) goto err_chunk; if (paylen) { /* Put the msg_iov together into payload. */ - payload = kmalloc(paylen, GFP_ATOMIC); + payload = kmalloc(paylen, GFP_KERNEL); if (!payload) goto err_payload; - payoff = payload; - for (; iovlen > 0; --iovlen) { - if (copy_from_user(payoff, iov->iov_base,iov->iov_len)) - goto err_copy; - payoff += iov->iov_len; - iov++; - } + err = memcpy_fromiovec(payload, msg->msg_iov, paylen); + if (err < 0) + goto err_copy; } sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen); @@ -867,6 +844,31 @@ err_chunk: return retval; } +/* Make an ABORT chunk with a PROTOCOL VIOLATION cause code. */ +struct sctp_chunk *sctp_make_abort_violation( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + const __u8 *payload, + const size_t paylen) +{ + struct sctp_chunk *retval; + struct sctp_paramhdr phdr; + + retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen + + sizeof(sctp_chunkhdr_t)); + if (!retval) + goto end; + + sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, payload, paylen); + + phdr.type = htons(chunk->chunk_hdr->type); + phdr.length = chunk->chunk_hdr->length; + sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &phdr); + +end: + return retval; +} + /* Make a HEARTBEAT chunk. */ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, const struct sctp_transport *transport, @@ -919,7 +921,7 @@ nodata: /* Create an Operation Error chunk with the specified space reserved. * This routine can be used for containing multiple causes in the chunk. */ -struct sctp_chunk *sctp_make_op_error_space( +static struct sctp_chunk *sctp_make_op_error_space( const struct sctp_association *asoc, const struct sctp_chunk *chunk, size_t size) @@ -949,7 +951,7 @@ nodata: /* Create an Operation Error chunk. */ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, const struct sctp_chunk *chunk, - __u16 cause_code, const void *payload, + __be16 cause_code, const void *payload, size_t paylen) { struct sctp_chunk *retval; @@ -977,7 +979,7 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, { struct sctp_chunk *retval; - retval = kmem_cache_alloc(sctp_chunk_cachep, SLAB_ATOMIC); + retval = kmem_cache_alloc(sctp_chunk_cachep, GFP_ATOMIC); if (!retval) goto nodata; @@ -987,6 +989,7 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, SCTP_DEBUG_PRINTK("chunkifying skb %p w/o an sk\n", skb); } + INIT_LIST_HEAD(&retval->list); retval->skb = skb; retval->asoc = (struct sctp_association *)asoc; retval->resent = 0; @@ -1020,7 +1023,6 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, SCTP_DBG_OBJCNT_INC(chunk); atomic_set(&retval->refcnt, 1); - nodata: return retval; } @@ -1048,6 +1050,7 @@ const union sctp_addr *sctp_source(const struct sctp_chunk *chunk) /* Create a new chunk, setting the type and flags headers from the * arguments, reserving enough space for a 'paylen' byte payload. */ +SCTP_STATIC struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc, __u8 type, __u8 flags, int paylen) { @@ -1100,8 +1103,7 @@ static void sctp_chunk_destroy(struct sctp_chunk *chunk) /* Possibly, free the chunk. */ void sctp_chunk_free(struct sctp_chunk *chunk) { - /* Make sure that we are not on any list. */ - skb_unlink((struct sk_buff *) chunk); + BUG_ON(!list_empty(&chunk->list)); list_del_init(&chunk->transmitted_list); /* Release our reference on the message tracker. */ @@ -1188,15 +1190,14 @@ void sctp_chunk_assign_ssn(struct sctp_chunk *chunk) if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) { ssn = 0; } else { - sid = htons(chunk->subh.data_hdr->stream); + 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); - ssn = htons(ssn); } - chunk->subh.data_hdr->ssn = ssn; + chunk->subh.data_hdr->ssn = htons(ssn); chunk->has_ssn = 1; } @@ -1217,7 +1218,8 @@ void sctp_chunk_assign_tsn(struct sctp_chunk *chunk) /* Create a CLOSED association to use with an incoming packet. */ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep, - struct sctp_chunk *chunk, int gfp) + struct sctp_chunk *chunk, + gfp_t gfp) { struct sctp_association *asoc; struct sk_buff *skb; @@ -1247,7 +1249,7 @@ fail: /* Build a cookie representing asoc. * This INCLUDES the param header needed to put the cookie in the INIT ACK. */ -sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, +static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *init_chunk, int *cookie_len, @@ -1260,7 +1262,12 @@ sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, unsigned int keylen; char *key; - headersize = sizeof(sctp_paramhdr_t) + SCTP_SECRET_SIZE; + /* Header size is static data prior to the actual cookie, including + * any padding. + */ + headersize = sizeof(sctp_paramhdr_t) + + (sizeof(struct sctp_signed_cookie) - + sizeof(struct sctp_cookie)); bodysize = sizeof(struct sctp_cookie) + ntohs(init_chunk->chunk_hdr->length) + addrs_len; @@ -1272,17 +1279,13 @@ sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, - (bodysize % SCTP_COOKIE_MULTIPLE); *cookie_len = headersize + bodysize; - retval = (sctp_cookie_param_t *)kmalloc(*cookie_len, GFP_ATOMIC); - - if (!retval) { - *cookie_len = 0; - goto nodata; - } - /* Clear this memory since we are sending this data structure * out on the network. */ - memset(retval, 0x00, *cookie_len); + retval = kzalloc(*cookie_len, GFP_ATOMIC); + if (!retval) + goto nodata; + cookie = (struct sctp_signed_cookie *) retval->body; /* Set up the parameter header. */ @@ -1297,6 +1300,9 @@ sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, /* Remember PR-SCTP capability. */ cookie->c.prsctp_capable = asoc->peer.prsctp_capable; + /* Save adaptation indication in the cookie. */ + cookie->c.adaptation_ind = asoc->peer.adaptation_ind; + /* Set an expiration time for the cookie. */ do_gettimeofday(&cookie->c.expiration); TIMEVAL_ADD(asoc->cookie_life, cookie->c.expiration); @@ -1310,40 +1316,57 @@ sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, ntohs(init_chunk->chunk_hdr->length), raw_addrs, addrs_len); if (sctp_sk(ep->base.sk)->hmac) { + struct hash_desc desc; + /* Sign the message. */ sg.page = virt_to_page(&cookie->c); sg.offset = (unsigned long)(&cookie->c) % PAGE_SIZE; sg.length = bodysize; keylen = SCTP_SECRET_SIZE; key = (char *)ep->secret_key[ep->current_key]; + desc.tfm = sctp_sk(ep->base.sk)->hmac; + desc.flags = 0; - sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, - &sg, 1, cookie->signature); + if (crypto_hash_setkey(desc.tfm, key, keylen) || + crypto_hash_digest(&desc, &sg, bodysize, cookie->signature)) + goto free_cookie; } -nodata: return retval; + +free_cookie: + kfree(retval); +nodata: + *cookie_len = 0; + return NULL; } /* Unpack the cookie from COOKIE ECHO chunk, recreating the association. */ struct sctp_association *sctp_unpack_cookie( const struct sctp_endpoint *ep, const struct sctp_association *asoc, - struct sctp_chunk *chunk, int gfp, + struct sctp_chunk *chunk, gfp_t gfp, int *error, struct sctp_chunk **errp) { struct sctp_association *retval = NULL; struct sctp_signed_cookie *cookie; struct sctp_cookie *bear_cookie; int headersize, bodysize, fixed_size; - __u8 digest[SCTP_SIGNATURE_SIZE]; + __u8 *digest = ep->digest; struct scatterlist sg; unsigned int keylen, len; char *key; sctp_scope_t scope; struct sk_buff *skb = chunk->skb; + struct timeval tv; + struct hash_desc desc; - headersize = sizeof(sctp_chunkhdr_t) + SCTP_SECRET_SIZE; + /* Header size is static data prior to the actual cookie, including + * any padding. + */ + headersize = sizeof(sctp_chunkhdr_t) + + (sizeof(struct sctp_signed_cookie) - + sizeof(struct sctp_cookie)); bodysize = ntohs(chunk->chunk_hdr->length) - headersize; fixed_size = headersize + sizeof(struct sctp_cookie); @@ -1372,17 +1395,25 @@ struct sctp_association *sctp_unpack_cookie( sg.offset = (unsigned long)(bear_cookie) % PAGE_SIZE; sg.length = bodysize; key = (char *)ep->secret_key[ep->current_key]; + desc.tfm = sctp_sk(ep->base.sk)->hmac; + desc.flags = 0; - memset(digest, 0x00, sizeof(digest)); - sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, &sg, - 1, digest); + memset(digest, 0x00, SCTP_SIGNATURE_SIZE); + if (crypto_hash_setkey(desc.tfm, key, keylen) || + crypto_hash_digest(&desc, &sg, bodysize, digest)) { + *error = -SCTP_IERROR_NOMEM; + goto fail; + } if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { /* Try the previous key. */ key = (char *)ep->secret_key[ep->last_key]; - memset(digest, 0x00, sizeof(digest)); - sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, - &sg, 1, digest); + memset(digest, 0x00, SCTP_SIGNATURE_SIZE); + if (crypto_hash_setkey(desc.tfm, key, keylen) || + crypto_hash_digest(&desc, &sg, bodysize, digest)) { + *error = -SCTP_IERROR_NOMEM; + goto fail; + } if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { /* Yikes! Still bad signature! */ @@ -1392,11 +1423,38 @@ struct sctp_association *sctp_unpack_cookie( } no_hmac: + /* IG Section 2.35.2: + * 3) Compare the port numbers and the verification tag contained + * within the COOKIE ECHO chunk to the actual port numbers and the + * verification tag within the SCTP common header of the received + * packet. If these values do not match the packet MUST be silently + * discarded, + */ + if (ntohl(chunk->sctp_hdr->vtag) != bear_cookie->my_vtag) { + *error = -SCTP_IERROR_BAD_TAG; + goto fail; + } + + if (chunk->sctp_hdr->source != bear_cookie->peer_addr.v4.sin_port || + ntohs(chunk->sctp_hdr->dest) != bear_cookie->my_port) { + *error = -SCTP_IERROR_BAD_PORTS; + goto fail; + } + /* Check to see if the cookie is stale. If there is already * an association, there is no need to check cookie's expiration * for init collision case of lost COOKIE ACK. + * If skb has been timestamped, then use the stamp, otherwise + * use current time. This introduces a small possibility that + * that a cookie may be considered expired, but his would only slow + * down the new association establishment instead of every packet. */ - if (!asoc && tv_lt(bear_cookie->expiration, skb->stamp)) { + if (sock_flag(ep->base.sk, SOCK_TIMESTAMP)) + skb_get_timestamp(skb, &tv); + else + do_gettimeofday(&tv); + + if (!asoc && tv_lt(bear_cookie->expiration, tv)) { __u16 len; /* * Section 3.3.10.3 Stale Cookie Error (3) @@ -1409,14 +1467,13 @@ no_hmac: len = ntohs(chunk->chunk_hdr->length); *errp = sctp_make_op_error_space(asoc, chunk, len); if (*errp) { - suseconds_t usecs = (skb->stamp.tv_sec - + suseconds_t usecs = (tv.tv_sec - bear_cookie->expiration.tv_sec) * 1000000L + - skb->stamp.tv_usec - - bear_cookie->expiration.tv_usec; + tv.tv_usec - bear_cookie->expiration.tv_usec; + __be32 n = htonl(usecs); - usecs = htonl(usecs); sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE, - &usecs, sizeof(usecs)); + &n, sizeof(n)); *error = -SCTP_IERROR_STALE_COOKIE; } else *error = -SCTP_IERROR_NOMEM; @@ -1446,7 +1503,7 @@ no_hmac: /* Also, add the destination address. */ if (list_empty(&retval->base.bind_addr.address_list)) { - sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, + sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 1, GFP_ATOMIC); } @@ -1455,6 +1512,7 @@ no_hmac: retval->addip_serial = retval->c.initial_tsn; retval->adv_peer_ack_point = retval->ctsn_ack_point; retval->peer.prsctp_capable = retval->c.prsctp_capable; + retval->peer.adaptation_ind = retval->c.adaptation_ind; /* The INIT stuff will be done by the side effects. */ return retval; @@ -1478,8 +1536,8 @@ malformed: ********************************************************************/ struct __sctp_missing { - __u32 num_missing; - __u16 type; + __be32 num_missing; + __be16 type; } __attribute__((packed)); /* @@ -1504,7 +1562,7 @@ static int sctp_process_missing_param(const struct sctp_association *asoc, if (*errp) { report.num_missing = htonl(1); report.type = paramtype; - sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, + sctp_init_cause(*errp, SCTP_ERROR_MISS_PARAM, &report, sizeof(report)); } @@ -1529,6 +1587,30 @@ static int sctp_process_inv_mandatory(const struct sctp_association *asoc, return 0; } +static int sctp_process_inv_paramlength(const struct sctp_association *asoc, + struct sctp_paramhdr *param, + const struct sctp_chunk *chunk, + struct sctp_chunk **errp) +{ + char error[] = "The following parameter had invalid length:"; + size_t payload_len = WORD_ROUND(sizeof(error)) + + sizeof(sctp_paramhdr_t); + + + /* Create an error chunk and fill it in with our payload. */ + if (!*errp) + *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); + } + + return 0; +} + + /* Do not attempt to handle the HOST_NAME parm. However, do * send back an indicator to the peer. */ @@ -1661,6 +1743,7 @@ static int sctp_verify_param(const struct sctp_association *asoc, case SCTP_PARAM_HEARTBEAT_INFO: case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: case SCTP_PARAM_ECN_CAPABLE: + case SCTP_PARAM_ADAPTATION_LAYER_IND: break; case SCTP_PARAM_HOST_NAME_ADDRESS: @@ -1692,7 +1775,9 @@ int sctp_verify_init(const struct sctp_association *asoc, /* Verify stream values are non-zero. */ if ((0 == peer_init->init_hdr.num_outbound_streams) || - (0 == peer_init->init_hdr.num_inbound_streams)) { + (0 == peer_init->init_hdr.num_inbound_streams) || + (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; @@ -1706,6 +1791,18 @@ int sctp_verify_init(const struct sctp_association *asoc, } /* for (loop through all parameters) */ + /* There is a possibility that a parameter length was bad and + * in that case we would have stoped walking the parameters. + * The current param.p would point at the bad one. + * Current consensus on the mailing list is to generate a PROTOCOL + * 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)) { + sctp_process_inv_paramlength(asoc, param.p, chunk, errp); + return 0; + } + /* The only missing mandatory param possible today is * the state cookie for an INIT-ACK chunk. */ @@ -1737,7 +1834,7 @@ int sctp_verify_init(const struct sctp_association *asoc, */ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, const union sctp_addr *peer_addr, - sctp_init_chunk_t *peer_init, int gfp) + sctp_init_chunk_t *peer_init, gfp_t gfp) { union sctp_params param; struct sctp_transport *transport; @@ -1754,9 +1851,10 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, * added as the primary transport. The source address seems to * be a a better choice than any of the embedded addresses. */ - if (peer_addr) - if(!sctp_assoc_add_peer(asoc, peer_addr, gfp)) + if (peer_addr) { + if(!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE)) goto nomem; + } /* Process the initialization parameters. */ @@ -1766,6 +1864,14 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, goto clean_up; } + /* 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); + if (transport->state == SCTP_UNKNOWN) { + sctp_assoc_rm_peer(asoc, transport); + } + } + /* The fixed INIT headers are always in network byte * order. */ @@ -1804,10 +1910,9 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, /* Copy cookie in case we need to resend COOKIE-ECHO. */ cookie = asoc->peer.cookie; if (cookie) { - asoc->peer.cookie = kmalloc(asoc->peer.cookie_len, gfp); + asoc->peer.cookie = kmemdup(cookie, asoc->peer.cookie_len, gfp); if (!asoc->peer.cookie) goto clean_up; - memcpy(asoc->peer.cookie, cookie, asoc->peer.cookie_len); } /* RFC 2960 7.2.1 The initial value of ssthresh MAY be arbitrarily @@ -1831,7 +1936,8 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, * stream sequence number shall be set to 0. */ - /* Allocate storage for the negotiated streams if it is not a temporary * association. + /* Allocate storage for the negotiated streams if it is not a temporary + * association. */ if (!asoc->temp) { int assoc_id; @@ -1877,6 +1983,9 @@ clean_up: list_del_init(pos); sctp_transport_free(transport); } + + asoc->peer.transport_count = 0; + nomem: return 0; } @@ -1893,8 +2002,10 @@ nomem: * work we do. In particular, we should not build transport * structures for the addresses. */ -int sctp_process_param(struct sctp_association *asoc, union sctp_params param, - const union sctp_addr *peer_addr, int gfp) +static int sctp_process_param(struct sctp_association *asoc, + union sctp_params param, + const union sctp_addr *peer_addr, + gfp_t gfp) { union sctp_addr addr; int i; @@ -1915,10 +2026,10 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param, /* Fall through. */ case SCTP_PARAM_IPV4_ADDRESS: af = sctp_get_af_specific(param_type2af(param.p->type)); - af->from_addr_param(&addr, param.addr, asoc->peer.port, 0); + af->from_addr_param(&addr, param.addr, htons(asoc->peer.port), 0); scope = sctp_scope(peer_addr); if (sctp_in_scope(&addr, scope)) - if (!sctp_assoc_add_peer(asoc, &addr, gfp)) + if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_UNCONFIRMED)) return 0; break; @@ -1989,6 +2100,10 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param, asoc->peer.ecn_capable = 1; break; + case SCTP_PARAM_ADAPTATION_LAYER_IND: + asoc->peer.adaptation_ind = param.aind->adaptation_ind; + break; + case SCTP_PARAM_FWD_TSN_SUPPORT: if (sctp_prsctp_enable) { asoc->peer.prsctp_capable = 1; @@ -2055,8 +2170,9 @@ __u32 sctp_generate_tsn(const struct sctp_endpoint *ep) * * Address Parameter and other parameter will not be wrapped in this function */ -struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, - union sctp_addr *addr, int vparam_len) +static struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, + union sctp_addr *addr, + int vparam_len) { sctp_addiphdr_t asconf; struct sctp_chunk *retval; @@ -2113,7 +2229,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, union sctp_addr *laddr, struct sockaddr *addrs, int addrcnt, - __u16 flags) + __be16 flags) { sctp_addip_param_t param; struct sctp_chunk *retval; @@ -2225,8 +2341,8 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, * * Create an ASCONF_ACK chunk with enough space for the parameter responses. */ -struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, - __u32 serial, int vparam_len) +static struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, + __u32 serial, int vparam_len) { sctp_addiphdr_t asconf; struct sctp_chunk *retval; @@ -2246,14 +2362,14 @@ struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, } /* Add response parameters to an ASCONF_ACK chunk. */ -static void sctp_add_asconf_response(struct sctp_chunk *chunk, __u32 crr_id, - __u16 err_code, sctp_addip_param_t *asconf_param) +static void sctp_add_asconf_response(struct sctp_chunk *chunk, __be32 crr_id, + __be16 err_code, sctp_addip_param_t *asconf_param) { sctp_addip_param_t ack_param; sctp_errhdr_t err_param; int asconf_param_len = 0; int err_param_len = 0; - __u16 response_type; + __be16 response_type; if (SCTP_ERROR_NO_ERROR == err_code) { response_type = SCTP_PARAM_SUCCESS_REPORT; @@ -2287,7 +2403,7 @@ static void sctp_add_asconf_response(struct sctp_chunk *chunk, __u32 crr_id, } /* Process a asconf parameter. */ -static __u16 sctp_process_asconf_param(struct sctp_association *asoc, +static __be16 sctp_process_asconf_param(struct sctp_association *asoc, struct sctp_chunk *asconf, sctp_addip_param_t *asconf_param) { @@ -2296,7 +2412,7 @@ static __u16 sctp_process_asconf_param(struct sctp_association *asoc, union sctp_addr addr; struct list_head *pos; union sctp_addr_param *addr_param; - + addr_param = (union sctp_addr_param *) ((void *)asconf_param + sizeof(sctp_addip_param_t)); @@ -2304,7 +2420,7 @@ static __u16 sctp_process_asconf_param(struct sctp_association *asoc, if (unlikely(!af)) return SCTP_ERROR_INV_PARAM; - af->from_addr_param(&addr, addr_param, asoc->peer.port, 0); + af->from_addr_param(&addr, addr_param, htons(asoc->peer.port), 0); switch (asconf_param->param_hdr.type) { case SCTP_PARAM_ADD_IP: /* ADDIP 4.3 D9) If an endpoint receives an ADD IP address @@ -2314,7 +2430,7 @@ static __u16 sctp_process_asconf_param(struct sctp_association *asoc, * Due to Resource Shortage'. */ - peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC); + peer = sctp_assoc_add_peer(asoc, &addr, GFP_ATOMIC, SCTP_UNCONFIRMED); if (!peer) return SCTP_ERROR_RSRC_LOW; @@ -2370,7 +2486,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, sctp_addip_param_t *asconf_param; struct sctp_chunk *asconf_ack; - __u16 err_code; + __be16 err_code; int length = 0; int chunk_len = asconf->skb->len; __u32 serial; @@ -2459,6 +2575,9 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, union sctp_addr addr; struct sctp_bind_addr *bp = &asoc->base.bind_addr; union sctp_addr_param *addr_param; + struct list_head *pos; + struct sctp_transport *transport; + struct sctp_sockaddr_entry *saddr; int retval = 0; addr_param = (union sctp_addr_param *) @@ -2466,13 +2585,17 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, /* We have checked the packet before, so we do not check again. */ af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type)); - af->from_addr_param(&addr, addr_param, bp->port, 0); + af->from_addr_param(&addr, addr_param, htons(bp->port), 0); switch (asconf_param->param_hdr.type) { case SCTP_PARAM_ADD_IP: sctp_local_bh_disable(); sctp_write_lock(&asoc->base.addr_lock); - retval = sctp_add_bind_addr(bp, &addr, GFP_ATOMIC); + list_for_each(pos, &bp->address_list) { + saddr = list_entry(pos, struct sctp_sockaddr_entry, 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; @@ -2482,6 +2605,13 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, retval = sctp_del_bind_addr(bp, &addr); sctp_write_unlock(&asoc->base.addr_lock); sctp_local_bh_enable(); + list_for_each(pos, &asoc->peer.transport_addr_list) { + transport = list_entry(pos, struct sctp_transport, + transports); + dst_release(transport->dst); + sctp_transport_route(transport, NULL, + sctp_sk(asoc->base.sk)); + } break; default: break; @@ -2499,7 +2629,7 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, * All TLVs after the failed response are considered unsuccessful unless a * specific success indication is present for the parameter. */ -static __u16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, +static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, sctp_addip_param_t *asconf_param, int no_err) { @@ -2507,7 +2637,7 @@ static __u16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, sctp_errhdr_t *err_param; int length; int asconf_ack_len = asconf_ack->skb->len; - __u16 err_code; + __be16 err_code; if (no_err) err_code = SCTP_ERROR_NO_ERROR; @@ -2563,7 +2693,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, int all_param_pass = 0; int no_err = 1; int retval = 0; - __u16 err_code = SCTP_ERROR_NO_ERROR; + __be16 err_code = SCTP_ERROR_NO_ERROR; /* Skip the chunkhdr and addiphdr from the last asconf sent and store * a pointer to address parameter. @@ -2637,8 +2767,12 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, asoc->addip_last_asconf = NULL; /* Send the next asconf chunk from the addip chunk queue. */ - asconf = (struct sctp_chunk *)__skb_dequeue(&asoc->addip_chunks); - if (asconf) { + if (!list_empty(&asoc->addip_chunk_list)) { + struct list_head *entry = asoc->addip_chunk_list.next; + asconf = list_entry(entry, struct sctp_chunk, list); + + list_del_init(entry); + /* Hold the chunk until an ASCONF_ACK is received. */ sctp_chunk_hold(asconf); if (sctp_primitive_ASCONF(asoc, asconf)) @@ -2664,7 +2798,6 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, hint = (nstreams + 1) * sizeof(__u32); - /* Maybe set the T-bit if we have no association. */ retval = sctp_make_chunk(asoc, SCTP_CID_FWD_TSN, 0, hint); if (!retval)