Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / sctp / sm_sideeffect.c
index 83cee2a..c5beb2a 100644 (file)
 #include <net/sctp/sctp.h>
 #include <net/sctp/sm.h>
 
+static int sctp_cmd_interpreter(sctp_event_t event_type,
+                               sctp_subtype_t subtype,
+                               sctp_state_t state,
+                               struct sctp_endpoint *ep,
+                               struct sctp_association *asoc,
+                               void *event_arg,
+                               sctp_disposition_t status,
+                               sctp_cmd_seq_t *commands,
+                               gfp_t gfp);
+static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
+                            sctp_state_t state,
+                            struct sctp_endpoint *ep,
+                            struct sctp_association *asoc,
+                            void *event_arg,
+                            sctp_disposition_t status,
+                            sctp_cmd_seq_t *commands,
+                            gfp_t gfp);
+
 /********************************************************************
  * Helper functions
  ********************************************************************/
@@ -134,14 +152,17 @@ static void sctp_do_ecn_cwr_work(struct sctp_association *asoc,
 }
 
 /* Generate SACK if necessary.  We call this at the end of a packet.  */
-int sctp_gen_sack(struct sctp_association *asoc, int force,
-                 sctp_cmd_seq_t *commands)
+static int sctp_gen_sack(struct sctp_association *asoc, int force,
+                        sctp_cmd_seq_t *commands)
 {
        __u32 ctsn, max_tsn_seen;
        struct sctp_chunk *sack;
+       struct sctp_transport *trans = asoc->peer.last_data_from;
        int error = 0;
 
-       if (force)
+       if (force || 
+           (!trans && (asoc->param_flags & SPP_SACKDELAY_DISABLE)) ||
+           (trans && (trans->param_flags & SPP_SACKDELAY_DISABLE)))
                asoc->peer.sack_needed = 1;
 
        ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
@@ -171,7 +192,22 @@ int sctp_gen_sack(struct sctp_association *asoc, int force,
        if (!asoc->peer.sack_needed) {
                /* We will need a SACK for the next packet.  */
                asoc->peer.sack_needed = 1;
-               goto out;
+
+               /* Set the SACK delay timeout based on the
+                * SACK delay for the last transport
+                * data was received from, or the default
+                * for the association.
+                */
+               if (trans)
+                       asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = 
+                               trans->sackdelay;
+               else
+                       asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = 
+                               asoc->sackdelay;
+
+               /* Restart the SACK timer. */
+               sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+                               SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
        } else {
                if (asoc->a_rwnd > asoc->rwnd)
                        asoc->a_rwnd = asoc->rwnd;
@@ -187,7 +223,7 @@ int sctp_gen_sack(struct sctp_association *asoc, int force,
                sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
                                SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
        }
-out:
+
        return error;
 nomem:
        error = -ENOMEM;
@@ -276,31 +312,31 @@ out_unlock:
        sctp_association_put(asoc);
 }
 
-void sctp_generate_t1_cookie_event(unsigned long data)
+static void sctp_generate_t1_cookie_event(unsigned long data)
 {
        struct sctp_association *asoc = (struct sctp_association *) data;
        sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_COOKIE);
 }
 
-void sctp_generate_t1_init_event(unsigned long data)
+static void sctp_generate_t1_init_event(unsigned long data)
 {
        struct sctp_association *asoc = (struct sctp_association *) data;
        sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_INIT);
 }
 
-void sctp_generate_t2_shutdown_event(unsigned long data)
+static void sctp_generate_t2_shutdown_event(unsigned long data)
 {
        struct sctp_association *asoc = (struct sctp_association *) data;
        sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN);
 }
 
-void sctp_generate_t4_rto_event(unsigned long data)
+static void sctp_generate_t4_rto_event(unsigned long data)
 {
        struct sctp_association *asoc = (struct sctp_association *) data;
        sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T4_RTO);
 }
 
-void sctp_generate_t5_shutdown_guard_event(unsigned long data)
+static void sctp_generate_t5_shutdown_guard_event(unsigned long data)
 {
         struct sctp_association *asoc = (struct sctp_association *)data;
         sctp_generate_timeout_event(asoc,
@@ -308,7 +344,7 @@ void sctp_generate_t5_shutdown_guard_event(unsigned long data)
 
 } /* sctp_generate_t5_shutdown_guard_event() */
 
-void sctp_generate_autoclose_event(unsigned long data)
+static void sctp_generate_autoclose_event(unsigned long data)
 {
        struct sctp_association *asoc = (struct sctp_association *) data;
        sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE);
@@ -353,7 +389,7 @@ out_unlock:
 }
 
 /* Inject a SACK Timeout event into the state machine.  */
-void sctp_generate_sack_event(unsigned long data)
+static void sctp_generate_sack_event(unsigned long data)
 {
        struct sctp_association *asoc = (struct sctp_association *) data;
        sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK);
@@ -367,7 +403,7 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
        NULL,
        sctp_generate_t4_rto_event,
        sctp_generate_t5_shutdown_guard_event,
-       sctp_generate_heartbeat_event,
+       NULL,
        sctp_generate_sack_event,
        sctp_generate_autoclose_event,
 };
@@ -396,11 +432,13 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
         */
        asoc->overall_error_count++;
 
-       if (transport->active &&
-           (transport->error_count++ >= transport->error_threshold)) {
-               SCTP_DEBUG_PRINTK("transport_strike: transport "
-                                 "IP:%d.%d.%d.%d failed.\n",
-                                 NIPQUAD(transport->ipaddr.v4.sin_addr));
+       if (transport->state != SCTP_INACTIVE &&
+           (transport->error_count++ >= transport->pathmaxrxt)) {
+               SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p",
+                                        " transport IP: port:%d failed.\n",
+                                        asoc,
+                                        (&transport->ipaddr),
+                                        transport->ipaddr.v4.sin_port);
                sctp_assoc_control_transport(asoc, transport,
                                             SCTP_TRANSPORT_DOWN,
                                             SCTP_FAILED_THRESHOLD);
@@ -460,10 +498,6 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
        sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
                        SCTP_STATE(SCTP_STATE_CLOSED));
 
-       /* Set sk_err to ECONNRESET on a 1-1 style socket. */
-       if (!sctp_style(asoc->base.sk, UDP))
-               asoc->base.sk->sk_err = ECONNRESET; 
-
        /* SEND_FAILED sent later when cleaning up the association. */
        asoc->outqueue.error = error;
        sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
@@ -477,7 +511,8 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
 static int sctp_cmd_process_init(sctp_cmd_seq_t *commands,
                                 struct sctp_association *asoc,
                                 struct sctp_chunk *chunk,
-                                sctp_init_chunk_t *peer_init, int gfp)
+                                sctp_init_chunk_t *peer_init,
+                                gfp_t gfp)
 {
        int error;
 
@@ -529,6 +564,23 @@ static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds,
        }
 }
 
+/* Helper function to stop any pending T3-RTX timers */
+static void sctp_cmd_t3_rtx_timers_stop(sctp_cmd_seq_t *cmds,
+                                       struct sctp_association *asoc)
+{
+       struct sctp_transport *t;
+       struct list_head *pos;
+
+       list_for_each(pos, &asoc->peer.transport_addr_list) {
+               t = list_entry(pos, struct sctp_transport, transports);
+               if (timer_pending(&t->T3_rtx_timer) &&
+                   del_timer(&t->T3_rtx_timer)) {
+                       sctp_transport_put(t);
+               }
+       }
+}
+
+
 /* Helper function to update the heartbeat timer. */
 static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds,
                                     struct sctp_association *asoc,
@@ -558,7 +610,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
        /* Mark the destination transport address as active if it is not so
         * marked.
         */
-       if (!t->active)
+       if (t->state == SCTP_INACTIVE)
                sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
                                             SCTP_HEARTBEAT_SUCCESS);
 
@@ -630,8 +682,11 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
 
        asoc->state = state;
 
+       SCTP_DEBUG_PRINTK("sctp_cmd_new_state: asoc %p[%s]\n",
+                         asoc, sctp_state_tbl[state]);
+
        if (sctp_style(sk, TCP)) {
-               /* Change the sk->sk_state of a TCP-style socket that has 
+               /* Change the sk->sk_state of a TCP-style socket that has
                 * sucessfully completed a connect() call.
                 */
                if (sctp_state(asoc, ESTABLISHED) && sctp_sstate(sk, CLOSED))
@@ -643,6 +698,16 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
                        sk->sk_shutdown |= RCV_SHUTDOWN;
        }
 
+       if (sctp_state(asoc, COOKIE_WAIT)) {
+               /* Reset init timeouts since they may have been
+                * increased due to timer expirations.
+                */
+               asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] =
+                                               asoc->rto_initial;
+               asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] =
+                                               asoc->rto_initial;
+       }
+
        if (sctp_state(asoc, ESTABLISHED) ||
            sctp_state(asoc, CLOSED) ||
            sctp_state(asoc, SHUTDOWN_RECEIVED)) {
@@ -749,6 +814,35 @@ static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq,
        return;
 }
 
+/* Helper function to remove the association non-primary peer 
+ * transports.
+ */ 
+static void sctp_cmd_del_non_primary(struct sctp_association *asoc)
+{
+       struct sctp_transport *t;
+       struct list_head *pos;
+       struct list_head *temp;
+
+       list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
+               t = list_entry(pos, struct sctp_transport, transports);
+               if (!sctp_cmp_addr_exact(&t->ipaddr,
+                                        &asoc->peer.primary_addr)) {
+                       sctp_assoc_del_peer(asoc, &t->ipaddr);
+               }
+       }
+
+       return;
+}
+
+/* Helper function to set sk_err on a 1-1 style socket. */
+static void sctp_cmd_set_sk_err(struct sctp_association *asoc, int error)
+{
+       struct sock *sk = asoc->base.sk;
+
+       if (!sctp_style(sk, UDP))
+               sk->sk_err = error;
+}
+
 /* These three macros allow us to pull the debugging code out of the
  * main flow of sctp_do_sm() to keep attention focused on the real
  * functionality there.
@@ -782,7 +876,7 @@ int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
               struct sctp_endpoint *ep,
               struct sctp_association *asoc,
               void *event_arg,
-              int gfp)
+              gfp_t gfp)
 {
        sctp_cmd_seq_t commands;
        const sctp_sm_table_entry_t *state_fn;
@@ -820,14 +914,14 @@ int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
 /*****************************************************************
  * This the master state function side effect processing function.
  *****************************************************************/
-int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
-                     sctp_state_t state,
-                     struct sctp_endpoint *ep,
-                     struct sctp_association *asoc,
-                     void *event_arg,
-                     sctp_disposition_t status,
-                     sctp_cmd_seq_t *commands,
-                     int gfp)
+static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
+                            sctp_state_t state,
+                            struct sctp_endpoint *ep,
+                            struct sctp_association *asoc,
+                            void *event_arg,
+                            sctp_disposition_t status,
+                            sctp_cmd_seq_t *commands,
+                            gfp_t gfp)
 {
        int error;
 
@@ -907,11 +1001,15 @@ bail:
  ********************************************************************/
 
 /* This is the side-effect interpreter.  */
-int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
-                        sctp_state_t state, struct sctp_endpoint *ep,
-                        struct sctp_association *asoc, void *event_arg,
-                        sctp_disposition_t status, sctp_cmd_seq_t *commands,
-                        int gfp)
+static int sctp_cmd_interpreter(sctp_event_t event_type,
+                               sctp_subtype_t subtype,
+                               sctp_state_t state,
+                               struct sctp_endpoint *ep,
+                               struct sctp_association *asoc,
+                               void *event_arg,
+                               sctp_disposition_t status,
+                               sctp_cmd_seq_t *commands,
+                               gfp_t gfp)
 {
        int error = 0;
        int force;
@@ -1048,6 +1146,27 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
                        if (cmd->obj.ptr)
                                sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                                SCTP_CHUNK(cmd->obj.ptr));
+
+                       /* FIXME - Eventually come up with a cleaner way to
+                        * enabling COOKIE-ECHO + DATA bundling during 
+                        * multihoming stale cookie scenarios, the following 
+                        * command plays with asoc->peer.retran_path to 
+                        * avoid the problem of sending the COOKIE-ECHO and 
+                        * DATA in different paths, which could result 
+                        * in the association being ABORTed if the DATA chunk 
+                        * is processed first by the server.  Checking the
+                        * init error counter simply causes this command
+                        * to be executed only during failed attempts of
+                        * association establishment.
+                        */
+                       if ((asoc->peer.retran_path !=
+                            asoc->peer.primary_path) &&
+                           (asoc->init_err_counter > 0)) {
+                               sctp_add_cmd_sf(commands,
+                                               SCTP_CMD_FORCE_PRIM_RETRAN,
+                                               SCTP_NULL());
+                       }
+
                        break;
 
                case SCTP_CMD_GEN_SHUTDOWN:
@@ -1136,8 +1255,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
                case SCTP_CMD_TIMER_START:
                        timer = &asoc->timers[cmd->obj.to];
                        timeout = asoc->timeouts[cmd->obj.to];
-                       if (!timeout)
-                               BUG();
+                       BUG_ON(!timeout);
 
                        timer->expires = jiffies + timeout;
                        sctp_association_hold(asoc);
@@ -1157,18 +1275,67 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
                                sctp_association_put(asoc);
                        break;
 
+               case SCTP_CMD_INIT_CHOOSE_TRANSPORT:
+                       chunk = cmd->obj.ptr;
+                       t = sctp_assoc_choose_init_transport(asoc);
+                       asoc->init_last_sent_to = t;
+                       chunk->transport = t;
+                       t->init_sent_count++;
+                       break;
+
                case SCTP_CMD_INIT_RESTART:
                        /* Do the needed accounting and updates
                         * associated with restarting an initialization
-                        * timer.
+                        * timer. Only multiply the timeout by two if
+                        * all transports have been tried at the current
+                        * timeout.
+                        */
+                       t = asoc->init_last_sent_to;
+                       asoc->init_err_counter++;
+
+                       if (t->init_sent_count > (asoc->init_cycle + 1)) {
+                               asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] *= 2;
+                               if (asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] >
+                                   asoc->max_init_timeo) {
+                                       asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] =
+                                               asoc->max_init_timeo;
+                               }
+                               asoc->init_cycle++;
+                               SCTP_DEBUG_PRINTK(
+                                       "T1 INIT Timeout adjustment"
+                                       " init_err_counter: %d"
+                                       " cycle: %d"
+                                       " timeout: %ld\n",
+                                       asoc->init_err_counter,
+                                       asoc->init_cycle,
+                                       asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT]);
+                       }
+
+                       sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
+                                       SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
+                       break;
+
+               case SCTP_CMD_COOKIEECHO_RESTART:
+                       /* Do the needed accounting and updates
+                        * associated with restarting an initialization
+                        * timer. Only multiply the timeout by two if
+                        * all transports have been tried at the current
+                        * timeout.
                         */
-                       asoc->counters[SCTP_COUNTER_INIT_ERROR]++;
-                       asoc->timeouts[cmd->obj.to] *= 2;
-                       if (asoc->timeouts[cmd->obj.to] >
+                       asoc->init_err_counter++;
+
+                       asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] *= 2;
+                       if (asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] >
                            asoc->max_init_timeo) {
-                               asoc->timeouts[cmd->obj.to] =
+                               asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] =
                                        asoc->max_init_timeo;
                        }
+                       SCTP_DEBUG_PRINTK(
+                               "T1 COOKIE Timeout adjustment"
+                               " init_err_counter: %d"
+                               " timeout: %ld\n",
+                               asoc->init_err_counter,
+                               asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE]);
 
                        /* If we've sent any data bundled with
                         * COOKIE-ECHO we need to resend.
@@ -1181,7 +1348,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
 
                        sctp_add_cmd_sf(commands,
                                        SCTP_CMD_TIMER_RESTART,
-                                       SCTP_TO(cmd->obj.to));
+                                       SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE));
                        break;
 
                case SCTP_CMD_INIT_FAILED:
@@ -1193,12 +1360,13 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
                                              subtype, chunk, cmd->obj.u32);
                        break;
 
-               case SCTP_CMD_COUNTER_INC:
-                       asoc->counters[cmd->obj.counter]++;
+               case SCTP_CMD_INIT_COUNTER_INC:
+                       asoc->init_err_counter++;
                        break;
 
-               case SCTP_CMD_COUNTER_RESET:
-                       asoc->counters[cmd->obj.counter] = 0;
+               case SCTP_CMD_INIT_COUNTER_RESET:
+                       asoc->init_err_counter = 0;
+                       asoc->init_cycle = 0;
                        break;
 
                case SCTP_CMD_REPORT_DUP:
@@ -1282,6 +1450,22 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
                case SCTP_CMD_CLEAR_INIT_TAG:
                        asoc->peer.i.init_tag = 0;
                        break;
+               case SCTP_CMD_DEL_NON_PRIMARY:
+                       sctp_cmd_del_non_primary(asoc);
+                       break;
+               case SCTP_CMD_T3_RTX_TIMERS_STOP:
+                       sctp_cmd_t3_rtx_timers_stop(commands, asoc);
+                       break;
+               case SCTP_CMD_FORCE_PRIM_RETRAN:
+                       t = asoc->peer.retran_path;
+                       asoc->peer.retran_path = asoc->peer.primary_path;
+                       error = sctp_outq_uncork(&asoc->outqueue);
+                       local_cork = 0;
+                       asoc->peer.retran_path = t;
+                       break;
+               case SCTP_CMD_SET_SK_ERR:
+                       sctp_cmd_set_sk_err(asoc, cmd->obj.error);
+                       break;
                default:
                        printk(KERN_WARNING "Impossible command: %u, %p\n",
                               cmd->verb, cmd->obj.ptr);