Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[pandora-kernel.git] / net / sctp / sm_sideeffect.c
index 4c5bed9..22e6702 100644 (file)
@@ -397,6 +397,41 @@ out_unlock:
        sctp_transport_put(transport);
 }
 
+/* Handle the timeout of the ICMP protocol unreachable timer.  Trigger
+ * the correct state machine transition that will close the association.
+ */
+void sctp_generate_proto_unreach_event(unsigned long data)
+{
+       struct sctp_transport *transport = (struct sctp_transport *) data;
+       struct sctp_association *asoc = transport->asoc;
+       
+       sctp_bh_lock_sock(asoc->base.sk);
+       if (sock_owned_by_user(asoc->base.sk)) {
+               SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__);
+
+               /* Try again later.  */
+               if (!mod_timer(&transport->proto_unreach_timer,
+                               jiffies + (HZ/20)))
+                       sctp_association_hold(asoc);
+               goto out_unlock;
+       }
+
+       /* Is this structure just waiting around for us to actually
+        * get destroyed?
+        */
+       if (asoc->base.dead)
+               goto out_unlock;
+
+       sctp_do_sm(SCTP_EVENT_T_OTHER,
+                  SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
+                  asoc->state, asoc->ep, asoc, transport, GFP_ATOMIC);
+
+out_unlock:
+       sctp_bh_unlock_sock(asoc->base.sk);
+       sctp_association_put(asoc);
+}
+
+
 /* Inject a SACK Timeout event into the state machine.  */
 static void sctp_generate_sack_event(unsigned long data)
 {
@@ -697,11 +732,15 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds,
 {
        struct sctp_transport *t;
 
-       t = sctp_assoc_choose_alter_transport(asoc,
+       if (chunk->transport)
+               t = chunk->transport;
+       else {
+               t = sctp_assoc_choose_alter_transport(asoc,
                                              asoc->shutdown_last_sent_to);
+               chunk->transport = t;
+       }
        asoc->shutdown_last_sent_to = t;
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
-       chunk->transport = t;
 }
 
 /* Helper function to change the state of an association. */
@@ -962,6 +1001,29 @@ static int sctp_cmd_send_msg(struct sctp_association *asoc,
 }
 
 
+/* Sent the next ASCONF packet currently stored in the association.
+ * This happens after the ASCONF_ACK was succeffully processed.
+ */
+static void sctp_cmd_send_asconf(struct sctp_association *asoc)
+{
+       /* Send the next asconf chunk from the addip chunk
+        * queue.
+        */
+       if (!list_empty(&asoc->addip_chunk_list)) {
+               struct list_head *entry = asoc->addip_chunk_list.next;
+               struct sctp_chunk *asconf = list_entry(entry,
+                                               struct sctp_chunk, list);
+               list_del_init(entry);
+
+               /* Hold the chunk until an ASCONF_ACK is received. */
+               sctp_chunk_hold(asconf);
+               if (sctp_primitive_ASCONF(asoc, asconf))
+                       sctp_chunk_free(asconf);
+               else
+                       asoc->addip_last_asconf = asconf;
+       }
+}
+
 
 /* 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
@@ -1617,6 +1679,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                        }
                        error = sctp_cmd_send_msg(asoc, cmd->obj.msg);
                        break;
+               case SCTP_CMD_SEND_NEXT_ASCONF:
+                       sctp_cmd_send_asconf(asoc);
+                       break;
                default:
                        printk(KERN_WARNING "Impossible command: %u, %p\n",
                               cmd->verb, cmd->obj.ptr);