/* * Copyright (C) 2008 Thomas Kallenberg * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #define _GNU_SOURCE #include #include #include #include #include #include #include "uci_control.h" #include #include #include #define FIFO_FILE "/var/run/charon.fifo" typedef struct private_uci_control_t private_uci_control_t; /** * private data of uci_control_t */ struct private_uci_control_t { /** * Public part */ uci_control_t public; /** * Job */ callback_job_t *job; }; /** * write answer to fifo */ static void write_fifo(private_uci_control_t *this, char *format, ...) { va_list args; FILE *out; out = fopen(FIFO_FILE, "w"); if (out) { va_start(args, format); vfprintf(out, format, args); va_end(args); fclose(out); } else { DBG1(DBG_CFG, "writing to UCI fifo failed: %s", strerror(errno)); } } /** * print IKE_SA status information */ static void status(private_uci_control_t *this, char *name) { enumerator_t *configs, *sas; iterator_t *children; ike_sa_t *ike_sa; child_sa_t *child_sa; peer_cfg_t *peer_cfg; char buf[2048]; FILE *out = NULL; configs = charon->backends->create_peer_cfg_enumerator(charon->backends, NULL, NULL, NULL, NULL); while (configs->enumerate(configs, &peer_cfg)) { if (name && !streq(name, peer_cfg->get_name(peer_cfg))) { continue; } sas = charon->controller->create_ike_sa_enumerator(charon->controller); while (sas->enumerate(sas, &ike_sa)) { if (!streq(ike_sa->get_name(ike_sa), peer_cfg->get_name(peer_cfg))) { continue; } if (!out) { out = fmemopen(buf, sizeof(buf), "w"); if (!out) { continue; } } fprintf(out, "%-8s %-20D %-16H ", ike_sa->get_name(ike_sa), ike_sa->get_other_id(ike_sa), ike_sa->get_other_host(ike_sa)); children = ike_sa->create_child_sa_iterator(ike_sa); while (children->iterate(children, (void**)&child_sa)) { fprintf(out, "%#R", child_sa->get_traffic_selectors(child_sa, FALSE)); } children->destroy(children); fprintf(out, "\n"); } sas->destroy(sas); } configs->destroy(configs); if (out) { fclose(out); write_fifo(this, "%s", buf); } else { write_fifo(this, ""); } } /** * Initiate an IKE_SA */ static void initiate(private_uci_control_t *this, char *name) { peer_cfg_t *peer_cfg; child_cfg_t *child_cfg; enumerator_t *enumerator; peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name); if (peer_cfg) { enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); if (enumerator->enumerate(enumerator, &child_cfg) && charon->controller->initiate(charon->controller, peer_cfg, child_cfg->get_ref(child_cfg), controller_cb_empty, NULL) == SUCCESS) { write_fifo(this, "connection '%s' established\n", name); } else { write_fifo(this, "establishing connection '%s' failed\n", name); } enumerator->destroy(enumerator); } else { write_fifo(this, "no connection named '%s' found\n", name); } } /** * terminate an IKE_SA */ static void terminate(private_uci_control_t *this, char *name) { enumerator_t *enumerator; ike_sa_t *ike_sa; u_int id; enumerator = charon->controller->create_ike_sa_enumerator(charon->controller); while (enumerator->enumerate(enumerator, &ike_sa)) { if (streq(name, ike_sa->get_name(ike_sa))) { id = ike_sa->get_unique_id(ike_sa); enumerator->destroy(enumerator); charon->controller->terminate_ike(charon->controller, id, controller_cb_empty, NULL); write_fifo(this, "connection '%s' terminated\n", name); return; } } enumerator->destroy(enumerator); write_fifo(this, "no active connection named '%s'\n", name); } /** * dispatch control request */ static void process(private_uci_control_t *this, char *message) { enumerator_t* enumerator; enumerator = enumerator_create_token(message, " \n", ""); if (enumerator->enumerate(enumerator, &message)) { if (streq(message, "status")) { if (enumerator->enumerate(enumerator, &message)) { status(this, message); } else { status(this, NULL); } } else if (streq(message, "up") && enumerator->enumerate(enumerator, &message)) { initiate(this, message); } else if (streq(message, "down") && enumerator->enumerate(enumerator, &message)) { terminate(this, message); } else { write_fifo(this, "usage: status [] | up | down \n" " status format: name peer-id peer-addr tunnel(s)\n"); } } enumerator->destroy(enumerator); } /** * read from fifo */ static job_requeue_t receive(private_uci_control_t *this) { char message[128]; int len; bool oldstate; FILE *in; memset(message, 0, sizeof(message)); oldstate = thread_cancelability(TRUE); in = fopen(FIFO_FILE, "r"); thread_cancelability(oldstate); if (in) { len = fread(message, 1, sizeof(message) - 1, in); fclose(in); if (len > 0) { process(this, message); } else { DBG1(DBG_DMN, "reading from UCI fifo failed: %s", strerror(errno)); } } else { DBG1(DBG_DMN, "opening UCI fifo failed: %s", strerror(errno)); } return JOB_REQUEUE_FAIR; } /** * Implementation of uci_control_t.destroy */ static void destroy(private_uci_control_t *this) { this->job->cancel(this->job); unlink(FIFO_FILE); free(this); } /** * Described in header. */ uci_control_t *uci_control_create() { private_uci_control_t *this = malloc_thing(private_uci_control_t); this->public.destroy = (void(*)(uci_control_t*))destroy; unlink(FIFO_FILE); if (mkfifo(FIFO_FILE, S_IRUSR|S_IWUSR) != 0) { DBG1(DBG_CFG, "creating UCI control fifo '%s' failed: %s", FIFO_FILE, strerror(errno)); } else { this->job = callback_job_create((callback_job_cb_t)receive, this, NULL, NULL); charon->processor->queue_job(charon->processor, (job_t*)this->job); } return &this->public; }