From 851d60484ea9f3d61d133fbd0eecea7eff015b3c Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 3 Nov 2010 15:12:05 +0100 Subject: [PATCH] Added a stroke rekey command to trigger IKE/CHILD_SA rekeying manually --- src/libcharon/plugins/stroke/stroke_control.c | 134 ++++++++++++++---- src/libcharon/plugins/stroke/stroke_control.h | 7 + src/libcharon/plugins/stroke/stroke_socket.c | 14 ++ src/stroke/stroke.c | 17 +++ src/stroke/stroke_keywords.h | 1 + src/stroke/stroke_keywords.txt | 1 + src/stroke/stroke_msg.h | 4 +- 7 files changed, 150 insertions(+), 28 deletions(-) diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index e0398ba78d..11c1103a2e 100644 --- a/src/libcharon/plugins/stroke/stroke_control.c +++ b/src/libcharon/plugins/stroke/stroke_control.c @@ -17,6 +17,8 @@ #include #include +#include +#include typedef struct private_stroke_control_t private_stroke_control_t; @@ -137,76 +139,91 @@ static void initiate(private_stroke_control_t *this, stroke_msg_t *msg, FILE *ou } /** - * Implementation of stroke_control_t.terminate. + * Parse a terminate/rekey specifier */ -static void terminate(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) +static bool parse_specifier(char *string, u_int32_t *id, + char **name, bool *child, bool *all) { - char *string, *pos = NULL, *name = NULL; - u_int32_t id = 0; - bool child, all = FALSE; int len; - ike_sa_t *ike_sa; - enumerator_t *enumerator; - linked_list_t *ike_list, *child_list; - stroke_log_info_t info; - uintptr_t del; + char *pos = NULL; - string = msg->terminate.name; + *id = 0; + *name = NULL; + *all = FALSE; len = strlen(string); if (len < 1) { - DBG1(DBG_CFG, "error parsing string"); - return; + return FALSE; } switch (string[len-1]) { case '}': - child = TRUE; + *child = TRUE; pos = strchr(string, '{'); break; case ']': - child = FALSE; + *child = FALSE; pos = strchr(string, '['); break; default: - name = string; - child = FALSE; + *name = string; + *child = FALSE; break; } - if (name) + if (*name) { /* is a single name */ } else if (pos == string + len - 2) { /* is name[] or name{} */ string[len-2] = '\0'; - name = string; + *name = string; } else { if (!pos) { - DBG1(DBG_CFG, "error parsing string"); - return; + return FALSE; } if (*(pos + 1) == '*') { /* is name[*] */ - all = TRUE; + *all = TRUE; *pos = '\0'; - name = string; + *name = string; } else { /* is name[123] or name{23} */ - id = atoi(pos + 1); - if (id == 0) + *id = atoi(pos + 1); + if (*id == 0) { - DBG1(DBG_CFG, "error parsing string"); - return; + return FALSE; } } } + return TRUE; +} + +/** + * Implementation of stroke_control_t.terminate. + */ +static void terminate(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) +{ + char *name; + u_int32_t id; + bool child, all; + ike_sa_t *ike_sa; + enumerator_t *enumerator; + linked_list_t *ike_list, *child_list; + stroke_log_info_t info; + uintptr_t del; + + if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all)) + { + DBG1(DBG_CFG, "error parsing specifier string"); + return; + } info.out = out; info.level = msg->output_verbosity; @@ -293,6 +310,68 @@ static void terminate(private_stroke_control_t *this, stroke_msg_t *msg, FILE *o child_list->destroy(child_list); } +/** + * Implementation of stroke_control_t.rekey. + */ +static void rekey(private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) +{ + char *name; + u_int32_t id; + bool child, all, finished = FALSE; + ike_sa_t *ike_sa; + enumerator_t *enumerator; + + if (!parse_specifier(msg->terminate.name, &id, &name, &child, &all)) + { + DBG1(DBG_CFG, "error parsing specifier string"); + return; + } + enumerator = charon->controller->create_ike_sa_enumerator(charon->controller); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + child_sa_t *child_sa; + iterator_t *children; + + if (child) + { + children = ike_sa->create_child_sa_iterator(ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + if ((name && streq(name, child_sa->get_name(child_sa))) || + (id && id == child_sa->get_reqid(child_sa))) + { + lib->processor->queue_job(lib->processor, + (job_t*)rekey_child_sa_job_create( + child_sa->get_reqid(child_sa), + child_sa->get_protocol(child_sa), + child_sa->get_spi(child_sa, TRUE))); + if (!all) + { + finished = TRUE; + break; + } + } + } + children->destroy(children); + } + else if ((name && streq(name, ike_sa->get_name(ike_sa))) || + (id && id == ike_sa->get_unique_id(ike_sa))) + { + lib->processor->queue_job(lib->processor, + (job_t*)rekey_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE)); + if (!all) + { + finished = TRUE; + } + } + if (finished) + { + break; + } + } + enumerator->destroy(enumerator); +} + /** * Implementation of stroke_control_t.terminate_srcip. */ @@ -486,6 +565,7 @@ stroke_control_t *stroke_control_create() this->public.initiate = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))initiate; this->public.terminate = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))terminate; this->public.terminate_srcip = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))terminate_srcip; + this->public.rekey = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))rekey; this->public.purge_ike = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))purge_ike; this->public.route = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))route; this->public.unroute = (void(*)(stroke_control_t*, stroke_msg_t *msg, FILE *out))unroute; diff --git a/src/libcharon/plugins/stroke/stroke_control.h b/src/libcharon/plugins/stroke/stroke_control.h index 9b49bdc31b..869aab3d31 100644 --- a/src/libcharon/plugins/stroke/stroke_control.h +++ b/src/libcharon/plugins/stroke/stroke_control.h @@ -53,6 +53,13 @@ struct stroke_control_t { */ void (*terminate_srcip)(stroke_control_t *this, stroke_msg_t *msg, FILE *out); + /** + * Rekey a connection. + * + * @param msg stroke message + */ + void (*rekey)(stroke_control_t *this, stroke_msg_t *msg, FILE *out); + /** * Delete IKE_SAs without a CHILD_SA. * diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 0a5110fd38..2e321f8b06 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -245,6 +245,17 @@ static void stroke_terminate_srcip(private_stroke_socket_t *this, this->control->terminate_srcip(this->control, msg, out); } +/** + * rekey a connection by name/id + */ +static void stroke_rekey(private_stroke_socket_t *this, stroke_msg_t *msg, FILE *out) +{ + pop_string(msg, &msg->terminate.name); + DBG1(DBG_CFG, "received stroke: rekey '%s'", msg->rekey.name); + + this->control->rekey(this->control, msg, out); +} + /** * route a policy (install SPD entries) */ @@ -510,6 +521,9 @@ static job_requeue_t process(stroke_job_context_t *ctx) case STR_TERMINATE_SRCIP: stroke_terminate_srcip(this, msg, out); break; + case STR_REKEY: + stroke_rekey(this, msg, out); + break; case STR_STATUS: stroke_status(this, msg, out, FALSE); break; diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c index 103617f08a..7c27e124af 100644 --- a/src/stroke/stroke.c +++ b/src/stroke/stroke.c @@ -197,6 +197,16 @@ static int terminate_connection_srcip(char *start, char *end) return send_stroke_msg(&msg); } +static int rekey_connection(char *name) +{ + stroke_msg_t msg; + + msg.type = STR_REKEY; + msg.length = offsetof(stroke_msg_t, buffer); + msg.rekey.name = push_string(&msg, name); + return send_stroke_msg(&msg); +} + static int route_connection(char *name) { stroke_msg_t msg; @@ -443,6 +453,13 @@ int main(int argc, char *argv[]) } res = terminate_connection_srcip(argv[2], argc > 3 ? argv[3] : NULL); break; + case STROKE_REKEY: + if (argc < 3) + { + exit_usage("\"rekey\" needs a connection name"); + } + res = rekey_connection(argv[2]); + break; case STROKE_ROUTE: if (argc < 3) { diff --git a/src/stroke/stroke_keywords.h b/src/stroke/stroke_keywords.h index 4a38265365..a57415e921 100644 --- a/src/stroke/stroke_keywords.h +++ b/src/stroke/stroke_keywords.h @@ -25,6 +25,7 @@ typedef enum { STROKE_UP, STROKE_DOWN, STROKE_DOWN_SRCIP, + STROKE_REKEY, STROKE_LOGLEVEL, STROKE_STATUS, STROKE_STATUSALL, diff --git a/src/stroke/stroke_keywords.txt b/src/stroke/stroke_keywords.txt index 0b80929852..7633da4578 100644 --- a/src/stroke/stroke_keywords.txt +++ b/src/stroke/stroke_keywords.txt @@ -32,6 +32,7 @@ unroute, STROKE_UNROUTE up, STROKE_UP down, STROKE_DOWN down-srcip, STROKE_DOWN_SRCIP +rekey, STROKE_REKEY loglevel, STROKE_LOGLEVEL status, STROKE_STATUS statusall, STROKE_STATUSALL diff --git a/src/stroke/stroke_msg.h b/src/stroke/stroke_msg.h index 9466cf0b0e..1abaf6c615 100644 --- a/src/stroke/stroke_msg.h +++ b/src/stroke/stroke_msg.h @@ -183,6 +183,8 @@ struct stroke_msg_t { STR_TERMINATE, /* terminate connection by peers srcip/virtual ip */ STR_TERMINATE_SRCIP, + /* rekey a connection */ + STR_REKEY, /* show connection status */ STR_STATUS, /* show verbose connection status */ @@ -215,7 +217,7 @@ struct stroke_msg_t { /* data for STR_INITIATE, STR_ROUTE, STR_UP, STR_DOWN, ... */ struct { char *name; - } initiate, route, unroute, terminate, status, del_conn, del_ca; + } initiate, route, unroute, terminate, rekey, status, del_conn, del_ca; /* data for STR_TERMINATE_SRCIP */ struct { -- 2.47.2