mmc: s3c6410: enable ADMA feature in 6410 sdhci controller
[safe/jmp/linux-2.6] / net / sctp / sm_statefuns.c
index 1c4e5d6..24b2cd5 100644 (file)
@@ -56,6 +56,7 @@
 #include <linux/ipv6.h>
 #include <linux/net.h>
 #include <linux/inet.h>
+#include <linux/slab.h>
 #include <net/sock.h>
 #include <net/inet_ecn.h>
 #include <linux/skbuff.h>
@@ -334,6 +335,15 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
        if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t)))
                return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
 
+       /* If the INIT is coming toward a closing socket, we'll send back
+        * and ABORT.  Essentially, this catches the race of INIT being
+        * backloged to the socket at the same time as the user isses close().
+        * Since the socket and all its associations are going away, we
+        * can treat this OOTB
+        */
+       if (sctp_sstate(ep->base.sk, CLOSING))
+               return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+
        /* Verify the INIT chunk before processing it. */
        err_chunk = NULL;
        if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
@@ -375,6 +385,11 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
        if (!new_asoc)
                goto nomem;
 
+       if (sctp_assoc_set_bind_addr_from_ep(new_asoc,
+                                            sctp_scope(sctp_source(chunk)),
+                                            GFP_ATOMIC) < 0)
+               goto nomem_init;
+
        /* The call, sctp_process_init(), can fail on memory allocation.  */
        if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
                               sctp_source(chunk),
@@ -392,9 +407,6 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
                len = ntohs(err_chunk->chunk_hdr->length) -
                        sizeof(sctp_chunkhdr_t);
 
-       if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0)
-               goto nomem_init;
-
        repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
        if (!repl)
                goto nomem_init;
@@ -962,7 +974,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
 {
        struct sctp_transport *transport = (struct sctp_transport *) arg;
 
-       if (asoc->overall_error_count > asoc->max_retrans) {
+       if (asoc->overall_error_count >= asoc->max_retrans) {
                sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
                                SCTP_ERROR(ETIMEDOUT));
                /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
@@ -985,12 +997,15 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
                                sctp_sf_heartbeat(ep, asoc, type, arg,
                                                  commands))
                        return SCTP_DISPOSITION_NOMEM;
+
                /* Set transport error counter and association error counter
                 * when sending heartbeat.
                 */
-               sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
+               sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_HB_SENT,
                                SCTP_TRANSPORT(transport));
        }
+       sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_IDLE,
+                       SCTP_TRANSPORT(transport));
        sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMER_UPDATE,
                        SCTP_TRANSPORT(transport));
 
@@ -1104,7 +1119,8 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
                return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
 
        /* Make sure that the HEARTBEAT-ACK chunk has a valid length.  */
-       if (!sctp_chunk_length_valid(chunk, sizeof(sctp_heartbeat_chunk_t)))
+       if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t) +
+                                           sizeof(sctp_sender_hb_info_t)))
                return sctp_sf_violation_chunklen(ep, asoc, type, arg,
                                                  commands);
 
@@ -1440,6 +1456,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
        if (!new_asoc)
                goto nomem;
 
+       if (sctp_assoc_set_bind_addr_from_ep(new_asoc,
+                               sctp_scope(sctp_source(chunk)), GFP_ATOMIC) < 0)
+               goto nomem;
+
        /* In the outbound INIT ACK the endpoint MUST copy its current
         * Verification Tag and Peers Verification tag into a reserved
         * place (local tie-tag and per tie-tag) within the state cookie.
@@ -1476,9 +1496,6 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
                        sizeof(sctp_chunkhdr_t);
        }
 
-       if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0)
-               goto nomem;
-
        repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
        if (!repl)
                goto nomem;
@@ -1705,7 +1722,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep,
 
                err = sctp_make_op_error(asoc, chunk,
                                         SCTP_ERROR_COOKIE_IN_SHUTDOWN,
-                                        NULL, 0);
+                                        NULL, 0, 0);
                if (err)
                        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                        SCTP_CHUNK(err));
@@ -2559,6 +2576,12 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep,
        chunk->subh.shutdown_hdr = sdh;
        ctsn = ntohl(sdh->cum_tsn_ack);
 
+       if (TSN_lt(ctsn, asoc->ctsn_ack_point)) {
+               SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn);
+               SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point);
+               return SCTP_DISPOSITION_DISCARD;
+       }
+
        /* If Cumulative TSN Ack beyond the max tsn currently
         * send, terminating the association and respond to the
         * sender with an ABORT.
@@ -2622,6 +2645,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep,
 {
        struct sctp_chunk *chunk = arg;
        sctp_shutdownhdr_t *sdh;
+       __u32 ctsn;
 
        if (!sctp_vtag_verify(chunk, asoc))
                return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
@@ -2633,12 +2657,19 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(const struct sctp_endpoint *ep,
                                                  commands);
 
        sdh = (sctp_shutdownhdr_t *)chunk->skb->data;
+       ctsn = ntohl(sdh->cum_tsn_ack);
+
+       if (TSN_lt(ctsn, asoc->ctsn_ack_point)) {
+               SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn);
+               SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point);
+               return SCTP_DISPOSITION_DISCARD;
+       }
 
        /* If Cumulative TSN Ack beyond the max tsn currently
         * send, terminating the association and respond to the
         * sender with an ABORT.
         */
-       if (!TSN_lt(ntohl(sdh->cum_tsn_ack), asoc->next_tsn))
+       if (!TSN_lt(ctsn, asoc->next_tsn))
                return sctp_sf_violation_ctsn(ep, asoc, type, arg, commands);
 
        /* verify, by checking the Cumulative TSN Ack field of the
@@ -2839,6 +2870,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
                                        sctp_cmd_seq_t *commands)
 {
        struct sctp_chunk *chunk = arg;
+       sctp_arg_t force = SCTP_NOFORCE();
        int error;
 
        if (!sctp_vtag_verify(chunk, asoc)) {
@@ -2865,10 +2897,16 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
                goto discard_force;
        case SCTP_IERROR_NO_DATA:
                goto consume;
+       case SCTP_IERROR_PROTO_VIOLATION:
+               return sctp_sf_abort_violation(ep, asoc, chunk, commands,
+                       (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
        default:
                BUG();
        }
 
+       if (chunk->chunk_hdr->flags & SCTP_DATA_SACK_IMM)
+               force = SCTP_FORCE();
+
        if (asoc->autoclose) {
                sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
                                SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE));
@@ -2897,7 +2935,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
         * more aggressive than the following algorithms allow.
         */
        if (chunk->end_of_packet)
-               sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE());
+               sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, force);
 
        return SCTP_DISPOSITION_CONSUME;
 
@@ -2922,7 +2960,7 @@ discard_force:
 
 discard_noforce:
        if (chunk->end_of_packet)
-               sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE());
+               sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, force);
 
        return SCTP_DISPOSITION_DISCARD;
 consume:
@@ -2975,6 +3013,9 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
                break;
        case SCTP_IERROR_NO_DATA:
                goto consume;
+       case SCTP_IERROR_PROTO_VIOLATION:
+               return sctp_sf_abort_violation(ep, asoc, chunk, commands,
+                       (u8 *)chunk->subh.data_hdr, sizeof(sctp_datahdr_t));
        default:
                BUG();
        }
@@ -3163,7 +3204,6 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
                                        sctp_cmd_seq_t *commands)
 {
        struct sctp_chunk *chunk = arg;
-       struct sctp_ulpevent *ev;
 
        if (!sctp_vtag_verify(chunk, asoc))
                return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
@@ -3173,21 +3213,10 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep,
                return sctp_sf_violation_chunklen(ep, asoc, type, arg,
                                                  commands);
 
-       while (chunk->chunk_end > chunk->skb->data) {
-               ev = sctp_ulpevent_make_remote_error(asoc, chunk, 0,
-                                                    GFP_ATOMIC);
-               if (!ev)
-                       goto nomem;
+       sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR,
+                       SCTP_CHUNK(chunk));
 
-               sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
-                               SCTP_ULPEVENT(ev));
-               sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR,
-                               SCTP_CHUNK(chunk));
-       }
        return SCTP_DISPOSITION_CONSUME;
-
-nomem:
-       return SCTP_DISPOSITION_NOMEM;
 }
 
 /*
@@ -3529,6 +3558,12 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
                asconf_ack = sctp_assoc_lookup_asconf_ack(asoc, hdr->serial);
                if (!asconf_ack)
                        return SCTP_DISPOSITION_DISCARD;
+
+               /* Reset the transport so that we select the correct one
+                * this time around.  This is to make sure that we don't
+                * accidentally use a stale transport that's been removed.
+                */
+               asconf_ack->transport = NULL;
        } else {
                /* ADDIP 5.2 E5) Otherwise, the ASCONF Chunk is discarded since
                 * it must be either a stale packet or from an attacker.
@@ -3543,7 +3578,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
         * To do this properly, we'll set the destination address of the chunk
         * and at the transmit time, will try look up the transport to use.
         * Since ASCONFs may be bundled, the correct transport may not be
-        * created untill we process the entire packet, thus this workaround.
+        * created until we process the entire packet, thus this workaround.
         */
        asconf_ack->dest = chunk->source;
        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(asconf_ack));
@@ -3641,8 +3676,14 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
                                SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
 
                if (!sctp_process_asconf_ack((struct sctp_association *)asoc,
-                                            asconf_ack))
+                                            asconf_ack)) {
+                       /* Successfully processed ASCONF_ACK.  We can
+                        * release the next asconf if we have one.
+                        */
+                       sctp_add_cmd_sf(commands, SCTP_CMD_SEND_NEXT_ASCONF,
+                                       SCTP_NULL());
                        return SCTP_DISPOSITION_CONSUME;
+               }
 
                abort = sctp_make_abort(asoc, asconf_ack,
                                        sizeof(sctp_errhdr_t));
@@ -3944,7 +3985,7 @@ sctp_disposition_t sctp_sf_eat_auth(const struct sctp_endpoint *ep,
                        err_chunk = sctp_make_op_error(asoc, chunk,
                                                        SCTP_ERROR_UNSUP_HMAC,
                                                        &auth_hdr->hmac_id,
-                                                       sizeof(__u16));
+                                                       sizeof(__u16), 0);
                        if (err_chunk) {
                                sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                                SCTP_CHUNK(err_chunk));
@@ -4036,7 +4077,8 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep,
                hdr = unk_chunk->chunk_hdr;
                err_chunk = sctp_make_op_error(asoc, unk_chunk,
                                               SCTP_ERROR_UNKNOWN_CHUNK, hdr,
-                                              WORD_ROUND(ntohs(hdr->length)));
+                                              WORD_ROUND(ntohs(hdr->length)),
+                                              0);
                if (err_chunk) {
                        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                        SCTP_CHUNK(err_chunk));
@@ -4055,7 +4097,8 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep,
                hdr = unk_chunk->chunk_hdr;
                err_chunk = sctp_make_op_error(asoc, unk_chunk,
                                               SCTP_ERROR_UNKNOWN_CHUNK, hdr,
-                                              WORD_ROUND(ntohs(hdr->length)));
+                                              WORD_ROUND(ntohs(hdr->length)),
+                                              0);
                if (err_chunk) {
                        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                        SCTP_CHUNK(err_chunk));
@@ -4268,9 +4311,9 @@ nomem:
 
 /*
  * Handle a protocol violation when the chunk length is invalid.
- * "Invalid" length is identified as smaller then the minimal length a
+ * "Invalid" length is identified as smaller than the minimal length a
  * given chunk can be.  For example, a SACK chunk has invalid length
- * if it's length is set to be smaller then the size of sctp_sack_chunk_t.
+ * if its length is set to be smaller than the size of sctp_sack_chunk_t.
  *
  * We inform the other end by sending an ABORT with a Protocol Violation
  * error code.
@@ -4300,7 +4343,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
 
 /*
  * Handle a protocol violation when the parameter length is invalid.
- * "Invalid" length is identified as smaller then the minimal length a
+ * "Invalid" length is identified as smaller than the minimal length a
  * given parameter can be.
  */
 static sctp_disposition_t sctp_sf_violation_paramlen(
@@ -4556,9 +4599,9 @@ sctp_disposition_t sctp_sf_do_prm_send(const struct sctp_endpoint *ep,
                                       void *arg,
                                       sctp_cmd_seq_t *commands)
 {
-       struct sctp_chunk *chunk = arg;
+       struct sctp_datamsg *msg = arg;
 
-       sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk));
+       sctp_add_cmd_sf(commands, SCTP_CMD_SEND_MSG, SCTP_DATAMSG(msg));
        return SCTP_DISPOSITION_CONSUME;
 }
 
@@ -4967,7 +5010,7 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat(
         *    to that address and not acknowledged within one RTO.
         *
         */
-       sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
+       sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_HB_SENT,
                        SCTP_TRANSPORT(arg));
        return SCTP_DISPOSITION_CONSUME;
 }
@@ -5442,9 +5485,13 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
        if (!reply)
                goto nomem;
 
-       /* Do some failure management (Section 8.2). */
-       sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
-                       SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
+       /* Do some failure management (Section 8.2).
+        * If we remove the transport an SHUTDOWN was last sent to, don't
+        * do failure management.
+        */
+       if (asoc->shutdown_last_sent_to)
+               sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
+                               SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
 
        /* Set the transport for the SHUTDOWN/ACK chunk and the timeout for
         * the T2-shutdown timer.
@@ -5481,7 +5528,9 @@ sctp_disposition_t sctp_sf_t4_timer_expire(
         * detection on the appropriate destination address as defined in
         * RFC2960 [5] section 8.1 and 8.2.
         */
-       sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport));
+       if (transport)
+               sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
+                               SCTP_TRANSPORT(transport));
 
        /* Reconfig T4 timer and transport. */
        sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T4, SCTP_CHUNK(chunk));
@@ -5851,6 +5900,9 @@ static int sctp_eat_data(const struct sctp_association *asoc,
        __u32 tsn;
        struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
        struct sock *sk = asoc->base.sk;
+       u16 ssn;
+       u16 sid;
+       u8 ordered = 0;
 
        data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data;
        skb_pull(chunk->skb, sizeof(sctp_datahdr_t));
@@ -5990,8 +6042,10 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         */
        if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
                SCTP_INC_STATS(SCTP_MIB_INUNORDERCHUNKS);
-       else
+       else {
                SCTP_INC_STATS(SCTP_MIB_INORDERCHUNKS);
+               ordered = 1;
+       }
 
        /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
         *
@@ -6001,19 +6055,33 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         * with cause set to "Invalid Stream Identifier" (See Section 3.3.10)
         * and discard the DATA chunk.
         */
-       if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) {
+       sid = ntohs(data_hdr->stream);
+       if (sid >= asoc->c.sinit_max_instreams) {
                /* Mark tsn as received even though we drop it */
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
 
                err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM,
                                         &data_hdr->stream,
-                                        sizeof(data_hdr->stream));
+                                        sizeof(data_hdr->stream),
+                                        sizeof(u16));
                if (err)
                        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                        SCTP_CHUNK(err));
                return SCTP_IERROR_BAD_STREAM;
        }
 
+       /* Check to see if the SSN is possible for this TSN.
+        * The biggest gap we can record is 4K wide.  Since SSNs wrap
+        * at an unsigned short, there is no way that an SSN can
+        * wrap and for a valid TSN.  We can simply check if the current
+        * SSN is smaller then the next expected one.  If it is, it wrapped
+        * and is invalid.
+        */
+       ssn = ntohs(data_hdr->ssn);
+       if (ordered && SSN_lt(ssn, sctp_ssn_peek(&asoc->ssnmap->in, sid))) {
+               return SCTP_IERROR_PROTO_VIOLATION;
+       }
+
        /* Send the data up to the user.  Note:  Schedule  the
         * SCTP_CMD_CHUNK_ULP cmd before the SCTP_CMD_GEN_SACK, as the SACK
         * chunk needs the updated rwnd.