vici: Implement a terminate command to close IKE or CHILD_SAs
authorMartin Willi <martin@revosec.ch>
Tue, 11 Feb 2014 16:14:22 +0000 (17:14 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 7 May 2014 12:13:35 +0000 (14:13 +0200)
src/libcharon/plugins/vici/vici_control.c

index ecf1909..4a4ee0c 100644 (file)
@@ -19,6 +19,7 @@
 #include <inttypes.h>
 
 #include <daemon.h>
+#include <collections/array.h>
 
 typedef struct private_vici_control_t private_vici_control_t;
 
@@ -176,6 +177,122 @@ CALLBACK(initiate, vici_message_t*,
        }
 }
 
+CALLBACK(terminate, vici_message_t*,
+       private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
+{
+       enumerator_t *enumerator, *isas, *csas;
+       char *child, *ike;
+       u_int timeout, child_id, ike_id, current, *del, done = 0;
+       ike_sa_t *ike_sa;
+       child_sa_t *child_sa;
+       array_t *ids;
+       vici_message_t *reply;
+       log_info_t log = {
+               .dispatcher = this->dispatcher,
+               .id = id,
+       };
+
+       child = request->get_str(request, NULL, "child");
+       ike = request->get_str(request, NULL, "ike");
+       child_id = request->get_int(request, 0, "child-id");
+       ike_id = request->get_int(request, 0, "ike-id");
+       timeout = request->get_int(request, 0, "timeout");
+       log.level = request->get_int(request, 1, "loglevel");
+
+       if (!child && !ike && !ike_id && !child_id)
+       {
+               return send_reply(this, "missing terminate selector");
+       }
+
+       ids = array_create(sizeof(u_int), 0);
+
+       isas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
+       while (isas->enumerate(isas, &ike_sa))
+       {
+               if (child || child_id)
+               {
+                       if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
+                       {
+                               continue;
+                       }
+                       if (ike_id && ike_id != ike_sa->get_unique_id(ike_sa))
+                       {
+                               continue;
+                       }
+                       csas = ike_sa->create_child_sa_enumerator(ike_sa);
+                       while (csas->enumerate(csas, &child_sa))
+                       {
+                               if (child && !streq(child, child_sa->get_name(child_sa)))
+                               {
+                                       continue;
+                               }
+                               if (child_id && child_sa->get_reqid(child_sa) != child_id)
+                               {
+                                       continue;
+                               }
+                               current = child_sa->get_reqid(child_sa);
+                               array_insert(ids, ARRAY_TAIL, &current);
+                       }
+                       csas->destroy(csas);
+               }
+               else if (ike && streq(ike, ike_sa->get_name(ike_sa)))
+               {
+                       current = ike_sa->get_unique_id(ike_sa);
+                       array_insert(ids, ARRAY_TAIL, &current);
+               }
+               else if (ike_id && ike_id == ike_sa->get_unique_id(ike_sa))
+               {
+                       array_insert(ids, ARRAY_TAIL, &ike_id);
+               }
+       }
+       isas->destroy(isas);
+
+       enumerator = array_create_enumerator(ids);
+       while (enumerator->enumerate(enumerator, &del))
+       {
+               if (child || child_id)
+               {
+                       if (charon->controller->terminate_child(charon->controller, *del,
+                                               (controller_cb_t)log_vici, &log, timeout) == SUCCESS)
+                       {
+                               done++;
+                       }
+               }
+               else
+               {
+                       if (charon->controller->terminate_ike(charon->controller, *del,
+                                               (controller_cb_t)log_vici, &log, timeout) == SUCCESS)
+                       {
+                               done++;
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       if (array_count(ids) == 0)
+       {
+               reply = send_reply(this, "no matching SAs to terminate found");
+       }
+       else if (done < array_count(ids))
+       {
+               if (array_count(ids) == 1)
+               {
+                       reply = send_reply(this, "terminating SA failed");
+               }
+               else
+               {
+                       reply = send_reply(this, "terminated %u of %u SAs",
+                                                          done, array_count(ids));
+               }
+       }
+       else
+       {
+               reply = send_reply(this, NULL);
+       }
+       array_destroy(ids);
+       return reply;
+}
+
 static void manage_command(private_vici_control_t *this,
                                                   char *name, vici_command_cb_t cb, bool reg)
 {
@@ -189,6 +306,7 @@ static void manage_command(private_vici_control_t *this,
 static void manage_commands(private_vici_control_t *this, bool reg)
 {
        manage_command(this, "initiate", initiate, reg);
+       manage_command(this, "terminate", terminate, reg);
        this->dispatcher->manage_event(this->dispatcher, "control-log", reg);
 }