Merge branch 'sii-m15w' into upstream
[pandora-kernel.git] / net / sctp / sm_statefuns.c
index 8cdba51..ead3f1b 100644 (file)
@@ -93,7 +93,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep,
 static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk);
 
 static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
-                                          __u16 error,
+                                          __u16 error, int sk_err,
                                           const struct sctp_association *asoc,
                                           struct sctp_transport *transport);
 
@@ -448,7 +448,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
        __u32 init_tag;
        struct sctp_chunk *err_chunk;
        struct sctp_packet *packet;
-       sctp_disposition_t ret;
+       __u16 error;
 
        if (!sctp_vtag_verify(chunk, asoc))
                return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
@@ -480,11 +480,9 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
                        goto nomem;
 
                sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
-               sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
-                               SCTP_STATE(SCTP_STATE_CLOSED));
-               SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
-               sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
-               return SCTP_DISPOSITION_DELETE_TCB;
+               return sctp_stop_t1_and_abort(commands, SCTP_ERROR_INV_PARAM,
+                                             ECONNREFUSED, asoc,
+                                             chunk->transport);
        }
 
        /* Verify the INIT chunk before processing it. */
@@ -511,27 +509,16 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
                                sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
                                                SCTP_PACKET(packet));
                                SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS);
-                               sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
-                                               SCTP_STATE(SCTP_STATE_CLOSED));
-                               sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
-                                               SCTP_NULL());
-                               return SCTP_DISPOSITION_CONSUME;
+                               error = SCTP_ERROR_INV_PARAM;
                        } else {
-                               sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
-                                               SCTP_STATE(SCTP_STATE_CLOSED));
-                               sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
-                                               SCTP_NULL());
-                               return SCTP_DISPOSITION_NOMEM;
+                               error = SCTP_ERROR_NO_RESOURCE;
                        }
                } else {
-                       ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
-                                                  commands);
-                       sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
-                                       SCTP_STATE(SCTP_STATE_CLOSED));
-                       sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
-                                       SCTP_NULL());
-                       return ret;
+                       sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands);
+                       error = SCTP_ERROR_INV_PARAM;
                }
+               return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED,
+                                               asoc, chunk->transport);
        }
 
        /* Tag the variable length parameters.  Note that we never
@@ -859,6 +846,7 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep,
        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;
 
        /* Send a heartbeat to our peer.  */
        paylen = sizeof(sctp_sender_hb_info_t);
@@ -886,6 +874,8 @@ 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) {
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ETIMEDOUT));
                /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_U32(SCTP_ERROR_NO_ERROR));
@@ -1030,6 +1020,12 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
                                                  commands);
 
        hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
+       /* Make sure that the length of the parameter is what we expect */
+       if (ntohs(hbinfo->param_hdr.length) !=
+                                   sizeof(sctp_sender_hb_info_t)) {
+               return SCTP_DISPOSITION_DISCARD;
+       }
+
        from_addr = hbinfo->daddr;
        link = sctp_assoc_lookup_paddr(asoc, &from_addr);
 
@@ -1053,6 +1049,10 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep,
                return SCTP_DISPOSITION_DISCARD;
        }
 
+       /* Validate the 64-bit random nonce. */
+       if (hbinfo->hb_nonce != link->hb_nonce)
+               return SCTP_DISPOSITION_DISCARD;
+
        max_interval = link->hbinterval + link->rto;
 
        /* Check if the timestamp looks valid.  */
@@ -2126,6 +2126,8 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep,
        int attempts = asoc->init_err_counter + 1;
 
        if (attempts > asoc->max_init_attempts) {
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ETIMEDOUT));
                sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
                                SCTP_U32(SCTP_ERROR_STALE_COOKIE));
                return SCTP_DISPOSITION_DELETE_TCB;
@@ -2262,6 +2264,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep,
        if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr))
                error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
 
+       sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET));
        /* ASSOC_FAILED will DELETE_TCB. */
        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error));
        SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
@@ -2306,7 +2309,8 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep,
        if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr))
                error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
 
-       return sctp_stop_t1_and_abort(commands, error, asoc, chunk->transport);
+       return sctp_stop_t1_and_abort(commands, error, ECONNREFUSED, asoc,
+                                     chunk->transport);
 }
 
 /*
@@ -2318,7 +2322,8 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep
                                        void *arg,
                                        sctp_cmd_seq_t *commands)
 {
-       return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR, asoc,
+       return sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR,
+                                     ENOPROTOOPT, asoc,
                                      (struct sctp_transport *)arg);
 }
 
@@ -2343,7 +2348,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep,
  * This is common code called by several sctp_sf_*_abort() functions above.
  */
 static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
-                                          __u16 error,
+                                          __u16 error, int sk_err,
                                           const struct sctp_association *asoc,
                                           struct sctp_transport *transport)
 {
@@ -2353,6 +2358,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands,
        SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
        sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                        SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+       sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(sk_err));
        /* CMD_INIT_FAILED will DELETE_TCB. */
        sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
                        SCTP_U32(error));
@@ -3336,6 +3342,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
                sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                                SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
                sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ECONNABORTED));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_U32(SCTP_ERROR_ASCONF_ACK));
                SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
@@ -3362,6 +3370,8 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep,
                 * processing the rest of the chunks in the packet.
                 */
                sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ECONNABORTED));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_U32(SCTP_ERROR_ASCONF_ACK));
                SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
@@ -3714,9 +3724,13 @@ static sctp_disposition_t sctp_sf_violation_chunklen(
        if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) {
                sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                                SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ECONNREFUSED));
                sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
                                SCTP_U32(SCTP_ERROR_PROTO_VIOLATION));
        } else {
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ECONNABORTED));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_U32(SCTP_ERROR_PROTO_VIOLATION));
                SCTP_DEC_STATS(SCTP_MIB_CURRESTAB);
@@ -4034,6 +4048,8 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(
         * TCB.  This is a departure from our typical NOMEM handling.
         */
 
+       sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                       SCTP_ERROR(ECONNABORTED));
        /* Delete the established association. */
        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                        SCTP_U32(SCTP_ERROR_USER_ABORT));
@@ -4175,6 +4191,8 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(
         * TCB.  This is a departure from our typical NOMEM handling.
         */
 
+       sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                       SCTP_ERROR(ECONNREFUSED));
        /* Delete the established association. */
        sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
                        SCTP_U32(SCTP_ERROR_USER_ABORT));
@@ -4543,6 +4561,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const struct sctp_endpoint *ep,
        struct sctp_transport *transport = arg;
 
        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. */
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_U32(SCTP_ERROR_NO_ERROR));
@@ -4662,6 +4682,8 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(const struct sctp_endpoint *ep,
                SCTP_DEBUG_PRINTK("Giving up on INIT, attempts: %d"
                                  " max_init_attempts: %d\n",
                                  attempts, asoc->max_init_attempts);
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ETIMEDOUT));
                sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
                                SCTP_U32(SCTP_ERROR_NO_ERROR));
                return SCTP_DISPOSITION_DELETE_TCB;
@@ -4711,6 +4733,8 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(const struct sctp_endpoint *ep
 
                sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
        } else {
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ETIMEDOUT));
                sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
                                SCTP_U32(SCTP_ERROR_NO_ERROR));
                return SCTP_DISPOSITION_DELETE_TCB;
@@ -4742,6 +4766,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
 
        SCTP_DEBUG_PRINTK("Timer T2 expired.\n");
        if (asoc->overall_error_count >= asoc->max_retrans) {
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ETIMEDOUT));
                /* Note:  CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_U32(SCTP_ERROR_NO_ERROR));
@@ -4817,6 +4843,8 @@ sctp_disposition_t sctp_sf_t4_timer_expire(
        if (asoc->overall_error_count >= asoc->max_retrans) {
                sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                                SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ETIMEDOUT));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_U32(SCTP_ERROR_NO_ERROR));
                SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
@@ -4870,6 +4898,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const struct sctp_endpoint *ep,
                goto nomem;
 
        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
+       sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                       SCTP_ERROR(ETIMEDOUT));
        sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                        SCTP_U32(SCTP_ERROR_NO_ERROR));
 
@@ -5253,7 +5283,6 @@ static int sctp_eat_data(const struct sctp_association *asoc,
        datalen -= sizeof(sctp_data_chunk_t);
 
        deliver = SCTP_CMD_CHUNK_ULP;
-       chunk->data_accepted = 1;
 
        /* Think about partial delivery. */
        if ((datalen >= asoc->rwnd) && (!asoc->ulpq.pd_mode)) {
@@ -5268,10 +5297,18 @@ static int sctp_eat_data(const struct sctp_association *asoc,
         * seems a bit troublesome in that frag_point varies based on
         * PMTU.  In cases, such as loopback, this might be a rather
         * large spill over.
+        * NOTE: If we have a full receive buffer here, we only renege if
+        * our receiver can still make progress without the tsn being
+        * received. We do this because in the event that the associations
+        * receive queue is empty we are filling a leading gap, and since
+        * reneging moves the gap to the end of the tsn stream, we are likely
+        * to stall again very shortly. Avoiding the renege when we fill a
+        * leading gap is a good heuristic for avoiding such steady state
+        * stalls.
         */
        if (!asoc->rwnd || asoc->rwnd_over ||
            (datalen > asoc->rwnd + asoc->frag_point) ||
-           rcvbuf_over) {
+           (rcvbuf_over && (!skb_queue_len(&sk->sk_receive_queue)))) {
 
                /* If this is the next TSN, consider reneging to make
                 * room.   Note: Playing nice with a confused sender.  A
@@ -5309,6 +5346,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
                 * processing the rest of the chunks in the packet.
                 */
                sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL());
+               sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR,
+                               SCTP_ERROR(ECONNABORTED));
                sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
                                SCTP_U32(SCTP_ERROR_NO_DATA));
                SCTP_INC_STATS(SCTP_MIB_ABORTEDS);
@@ -5322,6 +5361,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
        if (SCTP_CMD_CHUNK_ULP == deliver)
                sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
 
+       chunk->data_accepted = 1;
+
        /* Note: Some chunks may get overcounted (if we drop) or overcounted
         * if we renege and the chunk arrives again.
         */