ikev1: Accept Quick Mode DELETES while Quick Mode rekeying is active
authorMartin Willi <martin@revosec.ch>
Fri, 11 Jul 2014 09:59:01 +0000 (11:59 +0200)
committerMartin Willi <martin@revosec.ch>
Mon, 25 Aug 2014 07:53:02 +0000 (09:53 +0200)
If a peer immediately sends DELETE messages when completing Quick Mode rekeying,
the third Quick Mode message and the DELETE are sent simultaneously. This
implies that DELETE messages may arrive before the completing third Quick Mode
message.

Handle this case by ignoring the DELETE INFORMATIONAL in Quick Mode and let
the delete task handle it.

src/libcharon/sa/ikev1/tasks/quick_mode.c

index e627368..0d6be38 100644 (file)
@@ -1117,11 +1117,22 @@ METHOD(task_t, process_r, status_t,
                }
                case QM_NEGOTIATED:
                {
-                       if (message->get_exchange_type(message) == INFORMATIONAL_V1 ||
-                               has_notify_errors(this, message))
+                       if (has_notify_errors(this, message))
                        {
                                return SUCCESS;
                        }
+                       if (message->get_exchange_type(message) == INFORMATIONAL_V1)
+                       {
+                               if (message->get_payload(message, PLV1_DELETE))
+                               {
+                                       /* If the DELETE for a Quick Mode follows immediately
+                                        * after rekeying, we might receive it before the
+                                        * third completing Quick Mode message. Ignore it, as
+                                        * it gets handled by a separately queued delete task. */
+                                       return NEED_MORE;
+                               }
+                               return SUCCESS;
+                       }
                        if (!install(this))
                        {
                                ike_sa_t *ike_sa = this->ike_sa;
@@ -1198,6 +1209,14 @@ METHOD(task_t, build_r, status_t,
                        this->state = QM_NEGOTIATED;
                        return NEED_MORE;
                }
+               case QM_NEGOTIATED:
+                       if (message->get_exchange_type(message) == INFORMATIONAL_V1)
+                       {
+                               /* skip INFORMATIONAL response if we received a INFORMATIONAL
+                                * delete, see process_r() */
+                               return ALREADY_DONE;
+                       }
+                       /* fall */
                default:
                        return FAILED;
        }