SUNRPC: don't map EKEYEXPIRED to EACCES in call_refreshresult
[linux-3.10.git] / net / sctp / sm_make_chunk.c
index bd2a50b..cf579e7 100644 (file)
@@ -50,6 +50,8 @@
  * be incorporated into the next SCTP release.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/ip.h>
@@ -130,7 +132,7 @@ void  sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code,
  * abort chunk.  Differs from sctp_init_cause in that it won't oops
  * if there isn't enough space in the op error chunk
  */
-int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code,
+static int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code,
                      size_t paylen)
 {
        sctp_errhdr_t err;
@@ -196,6 +198,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
                             const struct sctp_bind_addr *bp,
                             gfp_t gfp, int vparam_len)
 {
+       struct net *net = sock_net(asoc->base.sk);
        sctp_inithdr_t init;
        union sctp_params addrs;
        size_t chunksize;
@@ -235,7 +238,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        chunksize += WORD_ROUND(SCTP_SAT_LEN(num_types));
        chunksize += sizeof(ecap_param);
 
-       if (sctp_prsctp_enable)
+       if (net->sctp.prsctp_enable)
                chunksize += sizeof(prsctp_param);
 
        /* ADDIP: Section 4.2.7:
@@ -243,7 +246,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
         *  the ASCONF,the ASCONF-ACK, and the AUTH  chunks in its INIT and
         *  INIT-ACK parameters.
         */
-       if (sctp_addip_enable) {
+       if (net->sctp.addip_enable) {
                extensions[num_ext] = SCTP_CID_ASCONF;
                extensions[num_ext+1] = SCTP_CID_ASCONF_ACK;
                num_ext += 2;
@@ -255,7 +258,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        chunksize += vparam_len;
 
        /* Account for AUTH related parameters */
-       if (sctp_auth_enable) {
+       if (net->sctp.auth_enable) {
                /* Add random parameter length*/
                chunksize += sizeof(asoc->c.auth_random);
 
@@ -329,7 +332,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
                sctp_addto_param(retval, num_ext, extensions);
        }
 
-       if (sctp_prsctp_enable)
+       if (net->sctp.prsctp_enable)
                sctp_addto_chunk(retval, sizeof(prsctp_param), &prsctp_param);
 
        if (sp->adaptation_ind) {
@@ -340,7 +343,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc,
        }
 
        /* Add SCTP-AUTH chunks to the parameter list */
-       if (sctp_auth_enable) {
+       if (net->sctp.auth_enable) {
                sctp_addto_chunk(retval, sizeof(asoc->c.auth_random),
                                 asoc->c.auth_random);
                if (auth_hmacs)
@@ -732,8 +735,10 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
        int len;
        __u32 ctsn;
        __u16 num_gabs, num_dup_tsns;
+       struct sctp_association *aptr = (struct sctp_association *)asoc;
        struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
        struct sctp_gap_ack_block gabs[SCTP_MAX_GABS];
+       struct sctp_transport *trans;
 
        memset(gabs, 0, sizeof(gabs));
        ctsn = sctp_tsnmap_get_ctsn(map);
@@ -799,10 +804,25 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
                                 gabs);
 
        /* Add the duplicate TSN information.  */
-       if (num_dup_tsns)
+       if (num_dup_tsns) {
+               aptr->stats.idupchunks += num_dup_tsns;
                sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns,
                                 sctp_tsnmap_get_dups(map));
-
+       }
+       /* Once we have a sack generated, check to see what our sack
+        * generation is, if its 0, reset the transports to 0, and reset
+        * the association generation to 1
+        *
+        * The idea is that zero is never used as a valid generation for the
+        * association so no transport will match after a wrap event like this,
+        * Until the next sack
+        */
+       if (++aptr->peer.sack_generation == 0) {
+               list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+                                   transports)
+                       trans->sack_generation = 0;
+               aptr->peer.sack_generation = 1;
+       }
 nodata:
        return retval;
 }
@@ -1071,22 +1091,49 @@ nodata:
        return retval;
 }
 
+struct sctp_chunk *sctp_make_violation_max_retrans(
+       const struct sctp_association *asoc,
+       const struct sctp_chunk *chunk)
+{
+       struct sctp_chunk *retval;
+       static const char error[] = "Association exceeded its max_retans count";
+       size_t payload_len = sizeof(error) + sizeof(sctp_errhdr_t);
+
+       retval = sctp_make_abort(asoc, chunk, payload_len);
+       if (!retval)
+               goto nodata;
+
+       sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, sizeof(error));
+       sctp_addto_chunk(retval, sizeof(error), error);
+
+nodata:
+       return retval;
+}
+
 /* Make a HEARTBEAT chunk.  */
 struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc,
-                                 const struct sctp_transport *transport,
-                                 const void *payload, const size_t paylen)
+                                 const struct sctp_transport *transport)
 {
-       struct sctp_chunk *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT,
-                                                   0, paylen);
+       struct sctp_chunk *retval;
+       sctp_sender_hb_info_t hbinfo;
+
+       retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT, 0, sizeof(hbinfo));
 
        if (!retval)
                goto nodata;
 
+       hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO;
+       hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
+       hbinfo.daddr = transport->ipaddr;
+       hbinfo.sent_at = jiffies;
+       hbinfo.hb_nonce = transport->hb_nonce;
+
        /* Cast away the 'const', as this is just telling the chunk
         * what transport it belongs to.
         */
        retval->transport = (struct sctp_transport *) transport;
-       retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload);
+       retval->subh.hbs_hdr = sctp_addto_chunk(retval, sizeof(hbinfo),
+                                               &hbinfo);
 
 nodata:
        return retval;
@@ -1154,7 +1201,7 @@ nodata:
  * specifically, max(asoc->pathmtu, SCTP_DEFAULT_MAXSEGMENT)
  * This is a helper function to allocate an error chunk for
  * for those invalid parameter codes in which we may not want
- * to report all the errors, if the incomming chunk is large
+ * to report all the errors, if the incoming chunk is large
  */
 static inline struct sctp_chunk *sctp_make_op_error_fixed(
        const struct sctp_association *asoc,
@@ -1542,8 +1589,6 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
        struct sctp_signed_cookie *cookie;
        struct scatterlist sg;
        int headersize, bodysize;
-       unsigned int keylen;
-       char *key;
 
        /* Header size is static data prior to the actual cookie, including
         * any padding.
@@ -1603,12 +1648,11 @@ static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep,
 
                /* Sign the message.  */
                sg_init_one(&sg, &cookie->c, bodysize);
-               keylen = SCTP_SECRET_SIZE;
-               key = (char *)ep->secret_key[ep->current_key];
                desc.tfm = sctp_sk(ep->base.sk)->hmac;
                desc.flags = 0;
 
-               if (crypto_hash_setkey(desc.tfm, key, keylen) ||
+               if (crypto_hash_setkey(desc.tfm, ep->secret_key,
+                                      sizeof(ep->secret_key)) ||
                    crypto_hash_digest(&desc, &sg, bodysize, cookie->signature))
                        goto free_cookie;
        }
@@ -1635,8 +1679,7 @@ struct sctp_association *sctp_unpack_cookie(
        int headersize, bodysize, fixed_size;
        __u8 *digest = ep->digest;
        struct scatterlist sg;
-       unsigned int keylen, len;
-       char *key;
+       unsigned int len;
        sctp_scope_t scope;
        struct sk_buff *skb = chunk->skb;
        struct timeval tv;
@@ -1671,34 +1714,21 @@ struct sctp_association *sctp_unpack_cookie(
                goto no_hmac;
 
        /* Check the signature.  */
-       keylen = SCTP_SECRET_SIZE;
        sg_init_one(&sg, bear_cookie, bodysize);
-       key = (char *)ep->secret_key[ep->current_key];
        desc.tfm = sctp_sk(ep->base.sk)->hmac;
        desc.flags = 0;
 
        memset(digest, 0x00, SCTP_SIGNATURE_SIZE);
-       if (crypto_hash_setkey(desc.tfm, key, keylen) ||
+       if (crypto_hash_setkey(desc.tfm, ep->secret_key,
+                              sizeof(ep->secret_key)) ||
            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, 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! */
-                       *error = -SCTP_IERROR_BAD_SIG;
-                       goto fail;
-               }
+               *error = -SCTP_IERROR_BAD_SIG;
+               goto fail;
        }
 
 no_hmac:
@@ -1817,7 +1847,7 @@ malformed:
 struct __sctp_missing {
        __be32 num_missing;
        __be16 type;
-}  __attribute__((packed));
+}  __packed;
 
 /*
  * Report a missing mandatory parameter.
@@ -1914,7 +1944,7 @@ static int sctp_process_hn_param(const struct sctp_association *asoc,
        return 0;
 }
 
-static int sctp_verify_ext_param(union sctp_params param)
+static int sctp_verify_ext_param(struct net *net, union sctp_params param)
 {
        __u16 num_ext = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
        int have_auth = 0;
@@ -1938,10 +1968,10 @@ static int sctp_verify_ext_param(union sctp_params param)
         * only if ADD-IP is turned on and we are not backward-compatible
         * mode.
         */
-       if (sctp_addip_noauth)
+       if (net->sctp.addip_noauth)
                return 1;
 
-       if (sctp_addip_enable && !have_auth && have_asconf)
+       if (net->sctp.addip_enable && !have_auth && have_asconf)
                return 0;
 
        return 1;
@@ -1950,13 +1980,14 @@ static int sctp_verify_ext_param(union sctp_params param)
 static void sctp_process_ext_param(struct sctp_association *asoc,
                                    union sctp_params param)
 {
+       struct net *net = sock_net(asoc->base.sk);
        __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 &&
+                           if (net->sctp.prsctp_enable &&
                                !asoc->peer.prsctp_capable)
                                    asoc->peer.prsctp_capable = 1;
                            break;
@@ -1964,12 +1995,12 @@ static void sctp_process_ext_param(struct sctp_association *asoc,
                            /* if the peer reports AUTH, assume that he
                             * supports AUTH.
                             */
-                           if (sctp_auth_enable)
+                           if (net->sctp.auth_enable)
                                    asoc->peer.auth_capable = 1;
                            break;
                    case SCTP_CID_ASCONF:
                    case SCTP_CID_ASCONF_ACK:
-                           if (sctp_addip_enable)
+                           if (net->sctp.addip_enable)
                                    asoc->peer.asconf_capable = 1;
                            break;
                    default:
@@ -2027,11 +2058,11 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc,
                        *errp = sctp_make_op_error_fixed(asoc, chunk);
 
                if (*errp) {
-                       sctp_init_cause_fixed(*errp, SCTP_ERROR_UNKNOWN_PARAM,
-                                       WORD_ROUND(ntohs(param.p->length)));
-                       sctp_addto_chunk_fixed(*errp,
-                                       WORD_ROUND(ntohs(param.p->length)),
-                                       param.v);
+                       if (!sctp_init_cause_fixed(*errp, SCTP_ERROR_UNKNOWN_PARAM,
+                                       WORD_ROUND(ntohs(param.p->length))))
+                               sctp_addto_chunk_fixed(*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
@@ -2055,7 +2086,8 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc,
  *     SCTP_IERROR_ERROR - stop processing, trigger an ERROR
  *     SCTP_IERROR_NO_ERROR - continue with the chunk
  */
-static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
+static sctp_ierror_t sctp_verify_param(struct net *net,
+                                       const struct sctp_association *asoc,
                                        union sctp_params param,
                                        sctp_cid_t cid,
                                        struct sctp_chunk *chunk,
@@ -2084,12 +2116,12 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
                break;
 
        case SCTP_PARAM_SUPPORTED_EXT:
-               if (!sctp_verify_ext_param(param))
+               if (!sctp_verify_ext_param(net, param))
                        return SCTP_IERROR_ABORT;
                break;
 
        case SCTP_PARAM_SET_PRIMARY:
-               if (sctp_addip_enable)
+               if (net->sctp.addip_enable)
                        break;
                goto fallthrough;
 
@@ -2100,12 +2132,12 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
                break;
 
        case SCTP_PARAM_FWD_TSN_SUPPORT:
-               if (sctp_prsctp_enable)
+               if (net->sctp.prsctp_enable)
                        break;
                goto fallthrough;
 
        case SCTP_PARAM_RANDOM:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fallthrough;
 
                /* SCTP-AUTH: Secion 6.1
@@ -2122,7 +2154,7 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
                break;
 
        case SCTP_PARAM_CHUNKS:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fallthrough;
 
                /* SCTP-AUTH: Section 3.2
@@ -2138,7 +2170,7 @@ static sctp_ierror_t sctp_verify_param(const struct sctp_association *asoc,
                break;
 
        case SCTP_PARAM_HMAC_ALGO:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fallthrough;
 
                hmacs = (struct sctp_hmac_algo_param *)param.p;
@@ -2172,7 +2204,7 @@ fallthrough:
 }
 
 /* Verify the INIT packet before we process it.  */
-int sctp_verify_init(const struct sctp_association *asoc,
+int sctp_verify_init(struct net *net, const struct sctp_association *asoc,
                     sctp_cid_t cid,
                     sctp_init_chunk_t *peer_init,
                     struct sctp_chunk *chunk,
@@ -2219,7 +2251,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
        /* Verify all the variable length parameters */
        sctp_walk_params(param, peer_init, init_hdr.params) {
 
-               result = sctp_verify_param(asoc, param, cid, chunk, errp);
+               result = sctp_verify_param(net, asoc, param, cid, chunk, errp);
                switch (result) {
                    case SCTP_IERROR_ABORT:
                    case SCTP_IERROR_NOMEM:
@@ -2240,14 +2272,18 @@ int sctp_verify_init(const struct sctp_association *asoc,
  * Returns 0 on failure, else success.
  * FIXME:  This is an association method.
  */
-int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
+int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk,
                      const union sctp_addr *peer_addr,
                      sctp_init_chunk_t *peer_init, gfp_t gfp)
 {
+       struct net *net = sock_net(asoc->base.sk);
        union sctp_params param;
        struct sctp_transport *transport;
        struct list_head *pos, *temp;
+       struct sctp_af *af;
+       union sctp_addr addr;
        char *cookie;
+       int src_match = 0;
 
        /* We must include the address that the INIT packet came from.
         * This is the only address that matters for an INIT packet.
@@ -2259,18 +2295,31 @@ 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, SCTP_ACTIVE))
-                       goto nomem;
-       }
+       if(!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE))
+               goto nomem;
+
+       if (sctp_cmp_addr_exact(sctp_source(chunk), peer_addr))
+               src_match = 1;
 
        /* Process the initialization parameters.  */
        sctp_walk_params(param, peer_init, init_hdr.params) {
+               if (!src_match && (param.p->type == SCTP_PARAM_IPV4_ADDRESS ||
+                   param.p->type == SCTP_PARAM_IPV6_ADDRESS)) {
+                       af = sctp_get_af_specific(param_type2af(param.p->type));
+                       af->from_addr_param(&addr, param.addr,
+                                           chunk->sctp_hdr->source, 0);
+                       if (sctp_cmp_addr_exact(sctp_source(chunk), &addr))
+                               src_match = 1;
+               }
 
                if (!sctp_process_param(asoc, param, peer_addr, gfp))
                        goto clean_up;
        }
 
+       /* source address of chunk may not match any valid address */
+       if (!src_match)
+               goto clean_up;
+
        /* AUTH: After processing the parameters, make sure that we
         * have all the required info to potentially do authentications.
         */
@@ -2284,7 +2333,7 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid,
         * also give us an option to silently ignore the packet, which
         * is what we'll do here.
         */
-       if (!sctp_addip_noauth &&
+       if (!net->sctp.addip_noauth &&
             (asoc->peer.asconf_capable && !asoc->peer.auth_capable)) {
                asoc->peer.addip_disabled_mask |= (SCTP_PARAM_ADD_IP |
                                                  SCTP_PARAM_DEL_IP |
@@ -2424,6 +2473,7 @@ static int sctp_process_param(struct sctp_association *asoc,
                              const union sctp_addr *peer_addr,
                              gfp_t gfp)
 {
+       struct net *net = sock_net(asoc->base.sk);
        union sctp_addr addr;
        int i;
        __u16 sat;
@@ -2452,13 +2502,13 @@ do_addr_param:
                af = sctp_get_af_specific(param_type2af(param.p->type));
                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_in_scope(net, &addr, scope))
                        if (!sctp_assoc_add_peer(asoc, &addr, gfp, SCTP_UNCONFIRMED))
                                return 0;
                break;
 
        case SCTP_PARAM_COOKIE_PRESERVATIVE:
-               if (!sctp_cookie_preserve_enable)
+               if (!net->sctp.cookie_preserve_enable)
                        break;
 
                stale = ntohl(param.life->lifespan_increment);
@@ -2538,7 +2588,7 @@ do_addr_param:
                break;
 
        case SCTP_PARAM_SET_PRIMARY:
-               if (!sctp_addip_enable)
+               if (!net->sctp.addip_enable)
                        goto fall_through;
 
                addr_param = param.v + sizeof(sctp_addip_param_t);
@@ -2565,7 +2615,7 @@ do_addr_param:
                break;
 
        case SCTP_PARAM_FWD_TSN_SUPPORT:
-               if (sctp_prsctp_enable) {
+               if (net->sctp.prsctp_enable) {
                        asoc->peer.prsctp_capable = 1;
                        break;
                }
@@ -2573,7 +2623,7 @@ do_addr_param:
                goto fall_through;
 
        case SCTP_PARAM_RANDOM:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fall_through;
 
                /* Save peer's random parameter */
@@ -2586,7 +2636,7 @@ do_addr_param:
                break;
 
        case SCTP_PARAM_HMAC_ALGO:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fall_through;
 
                /* Save peer's HMAC list */
@@ -2602,7 +2652,7 @@ do_addr_param:
                break;
 
        case SCTP_PARAM_CHUNKS:
-               if (!sctp_auth_enable)
+               if (!net->sctp.auth_enable)
                        goto fall_through;
 
                asoc->peer.peer_chunks = kmemdup(param.p,
@@ -2742,11 +2792,12 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
        int                     addr_param_len = 0;
        int                     totallen = 0;
        int                     i;
+       int                     del_pickup = 0;
 
        /* Get total length of all the address parameters. */
        addr_buf = addrs;
        for (i = 0; i < addrcnt; i++) {
-               addr = (union sctp_addr *)addr_buf;
+               addr = addr_buf;
                af = sctp_get_af_specific(addr->v4.sin_family);
                addr_param_len = af->to_addr_param(addr, &addr_param);
 
@@ -2754,6 +2805,13 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
                totallen += addr_param_len;
 
                addr_buf += af->sockaddr_len;
+               if (asoc->asconf_addr_del_pending && !del_pickup) {
+                       /* reuse the parameter length from the same scope one */
+                       totallen += paramlen;
+                       totallen += addr_param_len;
+                       del_pickup = 1;
+                       SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen);
+               }
        }
 
        /* Create an asconf chunk with the required length. */
@@ -2764,7 +2822,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
        /* Add the address parameters to the asconf chunk. */
        addr_buf = addrs;
        for (i = 0; i < addrcnt; i++) {
-               addr = (union sctp_addr *)addr_buf;
+               addr = addr_buf;
                af = sctp_get_af_specific(addr->v4.sin_family);
                addr_param_len = af->to_addr_param(addr, &addr_param);
                param.param_hdr.type = flags;
@@ -2776,6 +2834,17 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
 
                addr_buf += af->sockaddr_len;
        }
+       if (flags == SCTP_PARAM_ADD_IP && del_pickup) {
+               addr = asoc->asconf_addr_del_pending;
+               af = sctp_get_af_specific(addr->v4.sin_family);
+               addr_param_len = af->to_addr_param(addr, &addr_param);
+               param.param_hdr.type = SCTP_PARAM_DEL_IP;
+               param.param_hdr.length = htons(paramlen + addr_param_len);
+               param.crr_id = i;
+
+               sctp_addto_chunk(retval, paramlen, &param);
+               sctp_addto_chunk(retval, addr_param_len, &addr_param);
+       }
        return retval;
 }
 
@@ -2913,15 +2982,14 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
        union sctp_addr addr;
        union sctp_addr_param *addr_param;
 
-       addr_param = (union sctp_addr_param *)
-                       ((void *)asconf_param + sizeof(sctp_addip_param_t));
+       addr_param = (void *)asconf_param + sizeof(sctp_addip_param_t);
 
        if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP &&
            asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP &&
            asconf_param->param_hdr.type != SCTP_PARAM_SET_PRIMARY)
                return SCTP_ERROR_UNKNOWN_PARAM;
 
-       switch (addr_param->v4.param_hdr.type) {
+       switch (addr_param->p.type) {
        case SCTP_PARAM_IPV6_ADDRESS:
                if (!asoc->peer.ipv6_address)
                        return SCTP_ERROR_DNS_FAILED;
@@ -2934,7 +3002,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
                return SCTP_ERROR_DNS_FAILED;
        }
 
-       af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type));
+       af = sctp_get_af_specific(param_type2af(addr_param->p.type));
        if (unlikely(!af))
                return SCTP_ERROR_DNS_FAILED;
 
@@ -2971,6 +3039,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
                /* Start the heartbeat timer. */
                if (!mod_timer(&peer->hb_timer, sctp_transport_timeout(peer)))
                        sctp_transport_hold(peer);
+               asoc->new_transport = peer;
                break;
        case SCTP_PARAM_DEL_IP:
                /* ADDIP 4.3 D7) If a request is received to delete the
@@ -2988,7 +3057,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
                 * an Error Cause TLV set to the new error code 'Request to
                 * Delete Source IP Address'
                 */
-               if (sctp_cmp_addr_exact(sctp_source(asconf), &addr))
+               if (sctp_cmp_addr_exact(&asconf->source, &addr))
                        return SCTP_ERROR_DEL_SRC_IP;
 
                /* Section 4.2.2
@@ -3098,16 +3167,16 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
        /* Skip the address parameter and store a pointer to the first
         * asconf parameter.
         */
-       length = ntohs(addr_param->v4.param_hdr.length);
-       asconf_param = (sctp_addip_param_t *)((void *)addr_param + length);
+       length = ntohs(addr_param->p.length);
+       asconf_param = (void *)addr_param + length;
        chunk_len -= length;
 
        /* create an ASCONF_ACK chunk.
         * Based on the definitions of parameters, we know that the size of
-        * ASCONF_ACK parameters are less than or equal to the twice of ASCONF
+        * ASCONF_ACK parameters are less than or equal to the fourfold of ASCONF
         * parameters.
         */
-       asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 2);
+       asconf_ack = sctp_make_asconf_ack(asoc, serial, chunk_len * 4);
        if (!asconf_ack)
                goto done;
 
@@ -3140,8 +3209,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
 
                /* Move to the next ASCONF param. */
                length = ntohs(asconf_param->param_hdr.length);
-               asconf_param = (sctp_addip_param_t *)((void *)asconf_param +
-                                                     length);
+               asconf_param = (void *)asconf_param + length;
                chunk_len -= length;
        }
 
@@ -3171,11 +3239,10 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
        struct sctp_transport *transport;
        struct sctp_sockaddr_entry *saddr;
 
-       addr_param = (union sctp_addr_param *)
-                       ((void *)asconf_param + sizeof(sctp_addip_param_t));
+       addr_param = (void *)asconf_param + sizeof(sctp_addip_param_t);
 
        /* 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 = sctp_get_af_specific(param_type2af(addr_param->p.type));
        af->from_addr_param(&addr, addr_param, htons(bp->port), 0);
 
        switch (asconf_param->param_hdr.type) {
@@ -3191,22 +3258,23 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
                local_bh_enable();
                list_for_each_entry(transport, &asoc->peer.transport_addr_list,
                                transports) {
-                       if (transport->state == SCTP_ACTIVE)
-                               continue;
                        dst_release(transport->dst);
-                       sctp_transport_route(transport, NULL,
-                                            sctp_sk(asoc->base.sk));
+                       transport->dst = NULL;
                }
                break;
        case SCTP_PARAM_DEL_IP:
                local_bh_disable();
                sctp_del_bind_addr(bp, &addr);
+               if (asoc->asconf_addr_del_pending != NULL &&
+                   sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) {
+                       kfree(asoc->asconf_addr_del_pending);
+                       asoc->asconf_addr_del_pending = NULL;
+               }
                local_bh_enable();
                list_for_each_entry(transport, &asoc->peer.transport_addr_list,
                                transports) {
                        dst_release(transport->dst);
-                       sctp_transport_route(transport, NULL,
-                                            sctp_sk(asoc->base.sk));
+                       transport->dst = NULL;
                }
                break;
        default:
@@ -3256,8 +3324,7 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,
                                return SCTP_ERROR_NO_ERROR;
                        case SCTP_PARAM_ERR_CAUSE:
                                length = sizeof(sctp_addip_param_t);
-                               err_param = (sctp_errhdr_t *)
-                                          ((void *)asconf_ack_param + length);
+                               err_param = (void *)asconf_ack_param + length;
                                asconf_ack_len -= length;
                                if (asconf_ack_len > 0)
                                        return err_param->cause;
@@ -3270,8 +3337,7 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,
                }
 
                length = ntohs(asconf_ack_param->param_hdr.length);
-               asconf_ack_param = (sctp_addip_param_t *)
-                                       ((void *)asconf_ack_param + length);
+               asconf_ack_param = (void *)asconf_ack_param + length;
                asconf_ack_len -= length;
        }
 
@@ -3302,8 +3368,8 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
        /* Skip the address parameter in the last asconf sent and store a
         * pointer to the first asconf parameter.
         */
-       length = ntohs(addr_param->v4.param_hdr.length);
-       asconf_param = (sctp_addip_param_t *)((void *)addr_param + length);
+       length = ntohs(addr_param->p.length);
+       asconf_param = (void *)addr_param + length;
        asconf_len -= length;
 
        /* ADDIP 4.1
@@ -3354,11 +3420,15 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
                 * one.
                 */
                length = ntohs(asconf_param->param_hdr.length);
-               asconf_param = (sctp_addip_param_t *)((void *)asconf_param +
-                                                     length);
+               asconf_param = (void *)asconf_param + length;
                asconf_len -= length;
        }
 
+       if (no_err && asoc->src_out_of_asoc_ok) {
+               asoc->src_out_of_asoc_ok = 0;
+               sctp_transport_immediate_rtx(asoc->peer.primary_path);
+       }
+
        /* Free the cached last sent asconf chunk. */
        list_del_init(&asconf->transmitted_list);
        sctp_chunk_free(asconf);
@@ -3373,7 +3443,6 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
                                    struct sctp_fwdtsn_skip *skiplist)
 {
        struct sctp_chunk *retval = NULL;
-       struct sctp_fwdtsn_chunk *ftsn_chunk;
        struct sctp_fwdtsn_hdr ftsn_hdr;
        struct sctp_fwdtsn_skip skip;
        size_t hint;
@@ -3386,8 +3455,6 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
        if (!retval)
                return NULL;
 
-       ftsn_chunk = (struct sctp_fwdtsn_chunk *)retval->subh.fwdtsn_hdr;
-
        ftsn_hdr.new_cum_tsn = htonl(new_cum_tsn);
        retval->subh.fwdtsn_hdr =
                sctp_addto_chunk(retval, sizeof(ftsn_hdr), &ftsn_hdr);