]> err.no Git - linux-2.6/blobdiff - net/sctp/sm_make_chunk.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6
[linux-2.6] / net / sctp / sm_make_chunk.c
index 8d18f570c2e6186598225b330430402b9eab24a1..23ae37ec871167d17c37ddbdbb9d4095e26a4575 100644 (file)
@@ -65,8 +65,6 @@
 #include <net/sctp/sctp.h>
 #include <net/sctp/sm.h>
 
-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);
@@ -112,20 +110,16 @@ static const struct sctp_paramhdr prsctp_param = {
  * abort chunk.
  */
 void  sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code,
-                     const void *payload, size_t paylen)
+                     size_t paylen)
 {
        sctp_errhdr_t err;
-       int padlen;
        __u16 len;
 
        /* Cause code constants are now defined in network order.  */
        err.cause = cause_code;
        len = sizeof(sctp_errhdr_t) + paylen;
-       padlen = len % 4;
        err.length  = htons(len);
-       len += padlen;
        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)
@@ -785,8 +779,8 @@ struct sctp_chunk *sctp_make_abort_no_data(
 
        /* Put the tsn back into network byte order.  */
        payload = htonl(tsn);
-       sctp_init_cause(retval, SCTP_ERROR_NO_DATA, (const void *)&payload,
-                       sizeof(payload));
+       sctp_init_cause(retval, SCTP_ERROR_NO_DATA, sizeof(payload));
+       sctp_addto_chunk(retval, sizeof(payload), (const void *)&payload);
 
        /* RFC 2960 6.4 Multi-homed SCTP Endpoints
         *
@@ -828,7 +822,8 @@ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc,
                        goto err_copy;
        }
 
-       sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen);
+       sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, paylen);
+       sctp_addto_chunk(retval, paylen, payload);
 
        if (paylen)
                kfree(payload);
@@ -855,15 +850,17 @@ struct sctp_chunk *sctp_make_abort_violation(
        struct sctp_paramhdr phdr;
 
        retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen
-                                       + sizeof(sctp_chunkhdr_t));
+                                       + sizeof(sctp_paramhdr_t));
        if (!retval)
                goto end;
 
-       sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, payload, paylen);
+       sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, paylen
+                                       + sizeof(sctp_paramhdr_t));
 
        phdr.type = htons(chunk->chunk_hdr->type);
        phdr.length = chunk->chunk_hdr->length;
-       sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &phdr);
+       sctp_addto_chunk(retval, paylen, payload);
+       sctp_addto_param(retval, sizeof(sctp_paramhdr_t), &phdr);
 
 end:
        return retval;
@@ -960,7 +957,8 @@ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc,
        if (!retval)
                goto nodata;
 
-       sctp_init_cause(retval, cause_code, payload, paylen);
+       sctp_init_cause(retval, cause_code, paylen);
+       sctp_addto_chunk(retval, paylen, payload);
 
 nodata:
        return retval;
@@ -1133,7 +1131,7 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data)
        void *target;
        void *padding;
        int chunklen = ntohs(chunk->chunk_hdr->length);
-       int padlen = chunklen % 4;
+       int padlen = WORD_ROUND(chunklen) - chunklen;
 
        padding = skb_put(chunk->skb, padlen);
        target = skb_put(chunk->skb, len);
@@ -1148,6 +1146,25 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data)
        return target;
 }
 
+/* Append bytes to the end of a parameter.  Will panic if chunk is not big
+ * enough.
+ */
+void *sctp_addto_param(struct sctp_chunk *chunk, int len, const void *data)
+{
+       void *target;
+       int chunklen = ntohs(chunk->chunk_hdr->length);
+
+       target = skb_put(chunk->skb, len);
+
+       memcpy(target, data, len);
+
+       /* Adjust the chunk length field.  */
+       chunk->chunk_hdr->length = htons(chunklen + len);
+       chunk->chunk_end = skb_tail_pointer(chunk->skb);
+
+       return target;
+}
+
 /* Append bytes from user space to the end of a chunk.  Will panic if
  * chunk is not big enough.
  * Returns a kernel err value.
@@ -1179,25 +1196,36 @@ out:
  */
 void sctp_chunk_assign_ssn(struct sctp_chunk *chunk)
 {
+       struct sctp_datamsg *msg;
+       struct sctp_chunk *lchunk;
+       struct sctp_stream *stream;
        __u16 ssn;
        __u16 sid;
 
        if (chunk->has_ssn)
                return;
 
-       /* This is the last possible instant to assign a SSN. */
-       if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
-               ssn = 0;
-       } else {
-               sid = ntohs(chunk->subh.data_hdr->stream);
-               if (chunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
-                       ssn = sctp_ssn_next(&chunk->asoc->ssnmap->out, sid);
-               else
-                       ssn = sctp_ssn_peek(&chunk->asoc->ssnmap->out, sid);
-       }
+       /* All fragments will be on the same stream */
+       sid = ntohs(chunk->subh.data_hdr->stream);
+       stream = &chunk->asoc->ssnmap->out;
+
+       /* Now assign the sequence number to the entire message.
+        * All fragments must have the same stream sequence number.
+        */
+       msg = chunk->msg;
+       list_for_each_entry(lchunk, &msg->chunks, frag_list) {
+               if (lchunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
+                       ssn = 0;
+               } else {
+                       if (lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
+                               ssn = sctp_ssn_next(stream, sid);
+                       else
+                               ssn = sctp_ssn_peek(stream, sid);
+               }
 
-       chunk->subh.data_hdr->ssn = htons(ssn);
-       chunk->has_ssn = 1;
+               lchunk->subh.data_hdr->ssn = htons(ssn);
+               lchunk->has_ssn = 1;
+       }
 }
 
 /* Helper function to assign a TSN if needed.  This assumes that both
@@ -1454,7 +1482,6 @@ no_hmac:
                do_gettimeofday(&tv);
 
        if (!asoc && tv_lt(bear_cookie->expiration, tv)) {
-               __u16 len;
                /*
                 * Section 3.3.10.3 Stale Cookie Error (3)
                 *
@@ -1472,7 +1499,8 @@ no_hmac:
                        __be32 n = htonl(usecs);
 
                        sctp_init_cause(*errp, SCTP_ERROR_STALE_COOKIE,
-                                       &n, sizeof(n));
+                                       sizeof(n));
+                       sctp_addto_chunk(*errp, sizeof(n), &n);
                        *error = -SCTP_IERROR_STALE_COOKIE;
                } else
                        *error = -SCTP_IERROR_NOMEM;
@@ -1503,7 +1531,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, 1,
-                                  GFP_ATOMIC);
+                               GFP_ATOMIC);
        }
 
        retval->next_tsn = retval->c.initial_tsn;
@@ -1562,7 +1590,8 @@ static int sctp_process_missing_param(const struct sctp_association *asoc,
                report.num_missing = htonl(1);
                report.type = paramtype;
                sctp_init_cause(*errp, SCTP_ERROR_MISS_PARAM,
-                               &report, sizeof(report));
+                               sizeof(report));
+               sctp_addto_chunk(*errp, sizeof(report), &report);
        }
 
        /* Stop processing this chunk. */
@@ -1580,7 +1609,7 @@ static int sctp_process_inv_mandatory(const struct sctp_association *asoc,
                *errp = sctp_make_op_error_space(asoc, chunk, 0);
 
        if (*errp)
-               sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, NULL, 0);
+               sctp_init_cause(*errp, SCTP_ERROR_INV_PARAM, 0);
 
        /* Stop processing this chunk. */
        return 0;
@@ -1601,9 +1630,10 @@ static int sctp_process_inv_paramlength(const struct sctp_association *asoc,
                *errp = sctp_make_op_error_space(asoc, chunk, payload_len);
 
        if (*errp) {
-               sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION, error,
-                               sizeof(error));
-               sctp_addto_chunk(*errp, sizeof(sctp_paramhdr_t), param);
+               sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION,
+                               sizeof(error) + sizeof(sctp_paramhdr_t));
+               sctp_addto_chunk(*errp, sizeof(error), error);
+               sctp_addto_param(*errp, sizeof(sctp_paramhdr_t), param);
        }
 
        return 0;
@@ -1624,9 +1654,10 @@ static int sctp_process_hn_param(const struct sctp_association *asoc,
        if (!*errp)
                *errp = sctp_make_op_error_space(asoc, chunk, len);
 
-       if (*errp)
-               sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED,
-                               param.v, len);
+       if (*errp) {
+               sctp_init_cause(*errp, SCTP_ERROR_DNS_FAILED, len);
+               sctp_addto_chunk(*errp, len, param.v);
+       }
 
        /* Stop processing this chunk. */
        return 0;
@@ -1678,10 +1709,13 @@ static int sctp_process_unk_param(const struct sctp_association *asoc,
                        *errp = sctp_make_op_error_space(asoc, chunk,
                                        ntohs(chunk->chunk_hdr->length));
 
-               if (*errp)
+               if (*errp) {
                        sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
-                                       param.v,
                                        WORD_ROUND(ntohs(param.p->length)));
+                       sctp_addto_chunk(*errp,
+                                       WORD_ROUND(ntohs(param.p->length)),
+                                       param.v);
+               }
 
                break;
        case SCTP_PARAM_ACTION_SKIP:
@@ -1696,8 +1730,10 @@ static int sctp_process_unk_param(const struct sctp_association *asoc,
 
                if (*errp) {
                        sctp_init_cause(*errp, SCTP_ERROR_UNKNOWN_PARAM,
-                                       param.v,
                                        WORD_ROUND(ntohs(param.p->length)));
+                       sctp_addto_chunk(*errp,
+                                       WORD_ROUND(ntohs(param.p->length)),
+                                       param.v);
                } else {
                        /* If there is no memory for generating the ERROR
                         * report as specified, an ABORT will be triggered
@@ -1797,7 +1833,7 @@ int sctp_verify_init(const struct sctp_association *asoc,
         * VIOLATION error.  We build the ERROR chunk here and let the normal
         * error handling code build and send the packet.
         */
-       if (param.v < (void*)chunk->chunk_end - sizeof(sctp_paramhdr_t)) {
+       if (param.v != (void*)chunk->chunk_end) {
                sctp_process_inv_paramlength(asoc, param.p, chunk, errp);
                return 0;
        }
@@ -2463,6 +2499,52 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
        return SCTP_ERROR_NO_ERROR;
 }
 
+/* Verify the ASCONF packet before we process it.  */
+int sctp_verify_asconf(const struct sctp_association *asoc,
+                      struct sctp_paramhdr *param_hdr, void *chunk_end,
+                      struct sctp_paramhdr **errp) {
+       sctp_addip_param_t *asconf_param;
+       union sctp_params param;
+       int length, plen;
+
+       param.v = (sctp_paramhdr_t *) param_hdr;
+       while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) {
+               length = ntohs(param.p->length);
+               *errp = param.p;
+
+               if (param.v > chunk_end - length ||
+                   length < sizeof(sctp_paramhdr_t))
+                       return 0;
+
+               switch (param.p->type) {
+               case SCTP_PARAM_ADD_IP:
+               case SCTP_PARAM_DEL_IP:
+               case SCTP_PARAM_SET_PRIMARY:
+                       asconf_param = (sctp_addip_param_t *)param.v;
+                       plen = ntohs(asconf_param->param_hdr.length);
+                       if (plen < sizeof(sctp_addip_param_t) +
+                           sizeof(sctp_paramhdr_t))
+                               return 0;
+                       break;
+               case SCTP_PARAM_SUCCESS_REPORT:
+               case SCTP_PARAM_ADAPTATION_LAYER_IND:
+                       if (length != sizeof(sctp_addip_param_t))
+                               return 0;
+
+                       break;
+               default:
+                       break;
+               }
+
+               param.v += WORD_ROUND(length);
+       }
+
+       if (param.v != chunk_end)
+               return 0;
+
+       return 1;
+}
+
 /* Process an incoming ASCONF chunk with the next expected serial no. and
  * return an ASCONF_ACK chunk to be sent in response.
  */
@@ -2577,22 +2659,16 @@ static int sctp_asconf_param_success(struct sctp_association *asoc,
 
        switch (asconf_param->param_hdr.type) {
        case SCTP_PARAM_ADD_IP:
-               sctp_local_bh_disable();
-               sctp_write_lock(&asoc->base.addr_lock);
-               list_for_each(pos, &bp->address_list) {
-                       saddr = list_entry(pos, struct sctp_sockaddr_entry, list);
+               /* This is always done in BH context with a socket lock
+                * held, so the list can not change.
+                */
+               list_for_each_entry(saddr, &bp->address_list, list) {
                        if (sctp_cmp_addr_exact(&saddr->a, &addr))
                                saddr->use_as_src = 1;
                }
-               sctp_write_unlock(&asoc->base.addr_lock);
-               sctp_local_bh_enable();
                break;
        case SCTP_PARAM_DEL_IP:
-               sctp_local_bh_disable();
-               sctp_write_lock(&asoc->base.addr_lock);
-               retval = sctp_del_bind_addr(bp, &addr);
-               sctp_write_unlock(&asoc->base.addr_lock);
-               sctp_local_bh_enable();
+               retval = sctp_del_bind_addr(bp, &addr, call_rcu_bh);
                list_for_each(pos, &asoc->peer.transport_addr_list) {
                        transport = list_entry(pos, struct sctp_transport,
                                                 transports);