sctp: fasthandoff with ASCONF at mobile-node
[linux-3.10.git] / net / sctp / outqueue.c
index 3e9d8d2..14c2b06 100644 (file)
@@ -577,6 +577,13 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
         * try to send as much as possible.
         */
        list_for_each_entry_safe(chunk, chunk1, lqueue, transmitted_list) {
+               /* If the chunk is abandoned, move it to abandoned list. */
+               if (sctp_chunk_abandoned(chunk)) {
+                       list_del_init(&chunk->transmitted_list);
+                       sctp_insert_list(&q->abandoned,
+                                        &chunk->transmitted_list);
+                       continue;
+               }
 
                /* Make sure that Gap Acked TSNs are not retransmitted.  A
                 * simple approach is just to move such TSNs out of the
@@ -747,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
         */
 
        list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
+               /* RFC 5061, 5.3
+                * F1) This means that until such time as the ASCONF
+                * containing the add is acknowledged, the sender MUST
+                * NOT use the new IP address as a source for ANY SCTP
+                * packet except on carrying an ASCONF Chunk.
+                */
+               if (asoc->src_out_of_asoc_ok &&
+                   chunk->chunk_hdr->type != SCTP_CID_ASCONF)
+                       continue;
+
                list_del_init(&chunk->list);
 
                /* Pick the right transport to use. */
@@ -874,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
                }
        }
 
+       if (q->asoc->src_out_of_asoc_ok)
+               goto sctp_flush_out;
+
        /* Is it OK to send data chunks?  */
        switch (asoc->state) {
        case SCTP_STATE_COOKIE_ECHOED:
@@ -897,6 +917,8 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
                 * current cwnd).
                 */
                if (!list_empty(&q->retransmit)) {
+                       if (asoc->peer.retran_path->state == SCTP_UNCONFIRMED)
+                               goto sctp_flush_out;
                        if (transport == asoc->peer.retran_path)
                                goto retran;
 
@@ -969,6 +991,8 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
                            ((new_transport->state == SCTP_INACTIVE) ||
                             (new_transport->state == SCTP_UNCONFIRMED)))
                                new_transport = asoc->peer.active_path;
+                       if (new_transport->state == SCTP_UNCONFIRMED)
+                               continue;
 
                        /* Change packets if necessary.  */
                        if (new_transport != transport) {
@@ -1575,6 +1599,8 @@ static void sctp_check_transmitted(struct sctp_outq *q,
 #endif /* SCTP_DEBUG */
        if (transport) {
                if (bytes_acked) {
+                       struct sctp_association *asoc = transport->asoc;
+
                        /* We may have counted DATA that was migrated
                         * to this transport due to DEL-IP operation.
                         * Subtract those bytes, since the were never
@@ -1593,6 +1619,17 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                        transport->error_count = 0;
                        transport->asoc->overall_error_count = 0;
 
+                       /*
+                        * While in SHUTDOWN PENDING, we may have started
+                        * the T5 shutdown guard timer after reaching the
+                        * retransmission limit. Stop that timer as soon
+                        * as the receiver acknowledged any data.
+                        */
+                       if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING &&
+                           del_timer(&asoc->timers
+                               [SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD]))
+                                       sctp_association_put(asoc);
+
                        /* Mark the destination transport address as
                         * active if it is not so marked.
                         */
@@ -1622,10 +1659,15 @@ static void sctp_check_transmitted(struct sctp_outq *q,
                         * A sender is doing zero window probing when the
                         * receiver's advertised window is zero, and there is
                         * only one data chunk in flight to the receiver.
+                        *
+                        * Allow the association to timeout while in SHUTDOWN
+                        * PENDING or SHUTDOWN RECEIVED in case the receiver
+                        * stays in zero window mode forever.
                         */
                        if (!q->asoc->peer.rwnd &&
                            !list_empty(&tlist) &&
-                           (sack_ctsn+2 == q->asoc->next_tsn)) {
+                           (sack_ctsn+2 == q->asoc->next_tsn) &&
+                           q->asoc->state < SCTP_STATE_SHUTDOWN_PENDING) {
                                SCTP_DEBUG_PRINTK("%s: SACK received for zero "
                                                  "window probe: %u\n",
                                                  __func__, sack_ctsn);