X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fsctp%2Fsm_make_chunk.c;h=17b509282cf2067f6052374536ddb36c9fbf404a;hb=1a79f0f3331294fd3a9416fcabbf24470955e314;hp=b80ea903b610ec4ebc128cd0bb3c25a0f7d1d566;hpb=daddc0d38b3571bed170afa273a49a0eba090c1e;p=linux-2.6.git diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index b80ea903b..17b509282 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -67,6 +67,19 @@ extern kmem_cache_t *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) { @@ -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]; + sctp_adaption_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_ADAPTION_LAYER_IND; + aiparam.param_hdr.length = htons(sizeof(aiparam)); + aiparam.adaption_ind = htonl(sp->adaption_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_adaption_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_ADAPTION_LAYER_IND; + aiparam.param_hdr.length = htons(sizeof(aiparam)); + aiparam.adaption_ind = htonl(sctp_sk(asoc->base.sk)->adaption_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); @@ -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) @@ -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. */ @@ -1217,7 +1219,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 +1250,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 +1263,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,7 +1280,7 @@ 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); + retval = kmalloc(*cookie_len, GFP_ATOMIC); if (!retval) { *cookie_len = 0; @@ -1297,6 +1305,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 adaption indication in the cookie. */ + cookie->c.adaption_ind = asoc->peer.adaption_ind; + /* Set an expiration time for the cookie. */ do_gettimeofday(&cookie->c.expiration); TIMEVAL_ADD(asoc->cookie_life, cookie->c.expiration); @@ -1329,21 +1340,27 @@ nodata: 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; - 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); @@ -1373,14 +1390,14 @@ struct sctp_association *sctp_unpack_cookie( sg.length = bodysize; key = (char *)ep->secret_key[ep->current_key]; - memset(digest, 0x00, sizeof(digest)); + memset(digest, 0x00, SCTP_SIGNATURE_SIZE); sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, &sg, 1, digest); 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)); + memset(digest, 0x00, SCTP_SIGNATURE_SIZE); sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, &sg, 1, digest); @@ -1392,11 +1409,30 @@ 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 (ntohs(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 (!asoc && tv_lt(bear_cookie->expiration, skb->stamp)) { + skb_get_timestamp(skb, &tv); + if (!asoc && tv_lt(bear_cookie->expiration, tv)) { __u16 len; /* * Section 3.3.10.3 Stale Cookie Error (3) @@ -1409,10 +1445,9 @@ 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; usecs = htonl(usecs); sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE, @@ -1446,7 +1481,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 +1490,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.adaption_ind = retval->c.adaption_ind; /* The INIT stuff will be done by the side effects. */ return retval; @@ -1529,6 +1565,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 +1721,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_ADAPTION_LAYER_IND: break; case SCTP_PARAM_HOST_NAME_ADDRESS: @@ -1706,6 +1767,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 +1810,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; @@ -1755,7 +1828,7 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, * be a a better choice than any of the embedded addresses. */ if (peer_addr) - if(!sctp_assoc_add_peer(asoc, peer_addr, gfp)) + if(!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE)) goto nomem; /* Process the initialization parameters. */ @@ -1766,6 +1839,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. */ @@ -1831,26 +1912,31 @@ 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) { - sctp_assoc_t assoc_id; + int assoc_id; + int error; asoc->ssnmap = sctp_ssnmap_new(asoc->c.sinit_max_instreams, asoc->c.sinit_num_ostreams, gfp); if (!asoc->ssnmap) goto clean_up; - do { - if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) - goto clean_up; - spin_lock_bh(&sctp_assocs_id_lock); - assoc_id = (sctp_assoc_t)idr_get_new(&sctp_assocs_id, - (void *)asoc); - spin_unlock_bh(&sctp_assocs_id_lock); - } while (unlikely((int)assoc_id == -1)); + retry: + if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp))) + goto clean_up; + spin_lock_bh(&sctp_assocs_id_lock); + error = idr_get_new_above(&sctp_assocs_id, (void *)asoc, 1, + &assoc_id); + spin_unlock_bh(&sctp_assocs_id_lock); + if (error == -EAGAIN) + goto retry; + else if (error) + goto clean_up; - asoc->assoc_id = assoc_id; + asoc->assoc_id = (sctp_assoc_t) assoc_id; } /* ADDIP Section 4.1 ASCONF Chunk Procedures @@ -1873,6 +1959,9 @@ clean_up: list_del_init(pos); sctp_transport_free(transport); } + + asoc->peer.transport_count = 0; + nomem: return 0; } @@ -1889,8 +1978,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; @@ -1914,7 +2005,7 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param, af->from_addr_param(&addr, param.addr, 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; @@ -1985,6 +2076,10 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param, asoc->peer.ecn_capable = 1; break; + case SCTP_PARAM_ADAPTION_LAYER_IND: + asoc->peer.adaption_ind = param.aind->adaption_ind; + break; + case SCTP_PARAM_FWD_TSN_SUPPORT: if (sctp_prsctp_enable) { asoc->peer.prsctp_capable = 1; @@ -2051,8 +2146,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; @@ -2221,8 +2317,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; @@ -2310,7 +2406,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; @@ -2455,6 +2551,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 *) @@ -2468,7 +2567,11 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, 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; @@ -2478,6 +2581,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; @@ -2633,8 +2743,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)) @@ -2660,7 +2774,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)