Annotation of embedaddon/strongswan/src/libcharon/sa/shunt_manager.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2015-2017 Tobias Brunner
! 3: * Copyright (C) 2011-2016 Andreas Steffen
! 4: * HSR Hochschule fuer Technik Rapperswil
! 5: *
! 6: * This program is free software; you can redistribute it and/or modify it
! 7: * under the terms of the GNU General Public License as published by the
! 8: * Free Software Foundation; either version 2 of the License, or (at your
! 9: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 10: *
! 11: * This program is distributed in the hope that it will be useful, but
! 12: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 13: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 14: * for more details.
! 15: */
! 16:
! 17: #include "shunt_manager.h"
! 18:
! 19: #include <daemon.h>
! 20: #include <threading/rwlock.h>
! 21: #include <threading/rwlock_condvar.h>
! 22: #include <collections/linked_list.h>
! 23:
! 24: #define INSTALL_DISABLED ((u_int)~0)
! 25:
! 26: typedef struct private_shunt_manager_t private_shunt_manager_t;
! 27:
! 28: /**
! 29: * Private data of an shunt_manager_t object.
! 30: */
! 31: struct private_shunt_manager_t {
! 32:
! 33: /**
! 34: * Public shunt_manager_t interface.
! 35: */
! 36: shunt_manager_t public;
! 37:
! 38: /**
! 39: * Installed shunts, as entry_t
! 40: */
! 41: linked_list_t *shunts;
! 42:
! 43: /**
! 44: * Lock to safely access the list of shunts
! 45: */
! 46: rwlock_t *lock;
! 47:
! 48: /**
! 49: * Number of threads currently installing shunts, or INSTALL_DISABLED
! 50: */
! 51: u_int installing;
! 52:
! 53: /**
! 54: * Condvar to signal shunt installation
! 55: */
! 56: rwlock_condvar_t *condvar;
! 57: };
! 58:
! 59: /**
! 60: * Config entry for a shunt
! 61: */
! 62: typedef struct {
! 63: /**
! 64: * Configured namespace
! 65: */
! 66: char *ns;
! 67:
! 68: /**
! 69: * Child config
! 70: */
! 71: child_cfg_t *cfg;
! 72:
! 73: } entry_t;
! 74:
! 75: /**
! 76: * Destroy a config entry
! 77: */
! 78: static void entry_destroy(entry_t *this)
! 79: {
! 80: this->cfg->destroy(this->cfg);
! 81: free(this->ns);
! 82: free(this);
! 83: }
! 84:
! 85: /**
! 86: * Install in and out shunt policies in the kernel
! 87: */
! 88: static bool install_shunt_policy(child_cfg_t *child)
! 89: {
! 90: enumerator_t *e_my_ts, *e_other_ts;
! 91: linked_list_t *my_ts_list, *other_ts_list, *hosts;
! 92: traffic_selector_t *my_ts, *other_ts;
! 93: host_t *host_any, *host_any6;
! 94: policy_type_t policy_type;
! 95: policy_priority_t policy_prio;
! 96: status_t status = SUCCESS;
! 97: uint32_t manual_prio;
! 98: char *interface;
! 99: bool fwd_out;
! 100: ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
! 101:
! 102: switch (child->get_mode(child))
! 103: {
! 104: case MODE_PASS:
! 105: policy_type = POLICY_PASS;
! 106: policy_prio = POLICY_PRIORITY_PASS;
! 107: break;
! 108: case MODE_DROP:
! 109: policy_type = POLICY_DROP;
! 110: policy_prio = POLICY_PRIORITY_FALLBACK;
! 111: break;
! 112: default:
! 113: return FALSE;
! 114: }
! 115:
! 116: host_any = host_create_any(AF_INET);
! 117: host_any6 = host_create_any(AF_INET6);
! 118:
! 119: hosts = linked_list_create_with_items(host_any, host_any6, NULL);
! 120: my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts,
! 121: FALSE);
! 122: other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts,
! 123: FALSE);
! 124: hosts->destroy(hosts);
! 125:
! 126: manual_prio = child->get_manual_prio(child);
! 127: interface = child->get_interface(child);
! 128: fwd_out = child->has_option(child, OPT_FWD_OUT_POLICIES);
! 129:
! 130: /* enumerate pairs of traffic selectors */
! 131: e_my_ts = my_ts_list->create_enumerator(my_ts_list);
! 132: while (e_my_ts->enumerate(e_my_ts, &my_ts))
! 133: {
! 134: e_other_ts = other_ts_list->create_enumerator(other_ts_list);
! 135: while (e_other_ts->enumerate(e_other_ts, &other_ts))
! 136: {
! 137: if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
! 138: {
! 139: continue;
! 140: }
! 141: if (my_ts->get_protocol(my_ts) &&
! 142: other_ts->get_protocol(other_ts) &&
! 143: my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
! 144: {
! 145: continue;
! 146: }
! 147: /* install out policy */
! 148: kernel_ipsec_policy_id_t id = {
! 149: .dir = POLICY_OUT,
! 150: .src_ts = my_ts,
! 151: .dst_ts = other_ts,
! 152: .mark = child->get_mark(child, FALSE),
! 153: .interface = interface,
! 154: };
! 155: kernel_ipsec_manage_policy_t policy = {
! 156: .type = policy_type,
! 157: .prio = policy_prio,
! 158: .manual_prio = manual_prio,
! 159: .src = host_any,
! 160: .dst = host_any,
! 161: .sa = &sa,
! 162: };
! 163: status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
! 164: if (fwd_out)
! 165: { /* install "outbound" forward policy */
! 166: id.dir = POLICY_FWD;
! 167: status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
! 168: }
! 169: /* install in policy */
! 170: id = (kernel_ipsec_policy_id_t){
! 171: .dir = POLICY_IN,
! 172: .src_ts = other_ts,
! 173: .dst_ts = my_ts,
! 174: .mark = child->get_mark(child, TRUE),
! 175: .interface = interface,
! 176: };
! 177: status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
! 178: /* install "inbound" forward policy */
! 179: id.dir = POLICY_FWD;
! 180: status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
! 181: }
! 182: e_other_ts->destroy(e_other_ts);
! 183: }
! 184: e_my_ts->destroy(e_my_ts);
! 185:
! 186: my_ts_list->destroy_offset(my_ts_list,
! 187: offsetof(traffic_selector_t, destroy));
! 188: other_ts_list->destroy_offset(other_ts_list,
! 189: offsetof(traffic_selector_t, destroy));
! 190: host_any6->destroy(host_any6);
! 191: host_any->destroy(host_any);
! 192:
! 193: return status == SUCCESS;
! 194: }
! 195:
! 196: METHOD(shunt_manager_t, install, bool,
! 197: private_shunt_manager_t *this, char *ns, child_cfg_t *cfg)
! 198: {
! 199: enumerator_t *enumerator;
! 200: entry_t *entry;
! 201: bool found = FALSE, success;
! 202:
! 203: if (!ns)
! 204: {
! 205: DBG1(DBG_CFG, "missing namespace for shunt policy '%s'",
! 206: cfg->get_name(cfg));
! 207: return FALSE;
! 208: }
! 209:
! 210: /* check if not already installed */
! 211: this->lock->write_lock(this->lock);
! 212: if (this->installing == INSTALL_DISABLED)
! 213: { /* flush() has been called */
! 214: this->lock->unlock(this->lock);
! 215: return FALSE;
! 216: }
! 217: enumerator = this->shunts->create_enumerator(this->shunts);
! 218: while (enumerator->enumerate(enumerator, &entry))
! 219: {
! 220: if (streq(ns, entry->ns) &&
! 221: streq(cfg->get_name(cfg), entry->cfg->get_name(entry->cfg)))
! 222: {
! 223: found = TRUE;
! 224: break;
! 225: }
! 226: }
! 227: enumerator->destroy(enumerator);
! 228: if (found)
! 229: {
! 230: DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
! 231: ipsec_mode_names, cfg->get_mode(cfg), cfg->get_name(cfg));
! 232: this->lock->unlock(this->lock);
! 233: return TRUE;
! 234: }
! 235: INIT(entry,
! 236: .ns = strdup(ns),
! 237: .cfg = cfg->get_ref(cfg),
! 238: );
! 239: this->shunts->insert_last(this->shunts, entry);
! 240: this->installing++;
! 241: this->lock->unlock(this->lock);
! 242:
! 243: success = install_shunt_policy(cfg);
! 244:
! 245: this->lock->write_lock(this->lock);
! 246: if (!success)
! 247: {
! 248: this->shunts->remove(this->shunts, entry, NULL);
! 249: entry_destroy(entry);
! 250: }
! 251: this->installing--;
! 252: this->condvar->signal(this->condvar);
! 253: this->lock->unlock(this->lock);
! 254: return success;
! 255: }
! 256:
! 257: /**
! 258: * Uninstall in and out shunt policies in the kernel
! 259: */
! 260: static void uninstall_shunt_policy(child_cfg_t *child)
! 261: {
! 262: enumerator_t *e_my_ts, *e_other_ts;
! 263: linked_list_t *my_ts_list, *other_ts_list, *hosts;
! 264: traffic_selector_t *my_ts, *other_ts;
! 265: host_t *host_any, *host_any6;
! 266: policy_type_t policy_type;
! 267: policy_priority_t policy_prio;
! 268: status_t status = SUCCESS;
! 269: uint32_t manual_prio;
! 270: char *interface;
! 271: bool fwd_out;
! 272: ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
! 273:
! 274: switch (child->get_mode(child))
! 275: {
! 276: case MODE_PASS:
! 277: policy_type = POLICY_PASS;
! 278: policy_prio = POLICY_PRIORITY_PASS;
! 279: break;
! 280: case MODE_DROP:
! 281: policy_type = POLICY_DROP;
! 282: policy_prio = POLICY_PRIORITY_FALLBACK;
! 283: break;
! 284: default:
! 285: return;
! 286: }
! 287:
! 288: host_any = host_create_any(AF_INET);
! 289: host_any6 = host_create_any(AF_INET6);
! 290:
! 291: hosts = linked_list_create_with_items(host_any, host_any6, NULL);
! 292: my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts,
! 293: FALSE);
! 294: other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts,
! 295: FALSE);
! 296: hosts->destroy(hosts);
! 297:
! 298: manual_prio = child->get_manual_prio(child);
! 299: interface = child->get_interface(child);
! 300: fwd_out = child->has_option(child, OPT_FWD_OUT_POLICIES);
! 301:
! 302: /* enumerate pairs of traffic selectors */
! 303: e_my_ts = my_ts_list->create_enumerator(my_ts_list);
! 304: while (e_my_ts->enumerate(e_my_ts, &my_ts))
! 305: {
! 306: e_other_ts = other_ts_list->create_enumerator(other_ts_list);
! 307: while (e_other_ts->enumerate(e_other_ts, &other_ts))
! 308: {
! 309: if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
! 310: {
! 311: continue;
! 312: }
! 313: if (my_ts->get_protocol(my_ts) &&
! 314: other_ts->get_protocol(other_ts) &&
! 315: my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
! 316: {
! 317: continue;
! 318: }
! 319: /* uninstall out policy */
! 320: kernel_ipsec_policy_id_t id = {
! 321: .dir = POLICY_OUT,
! 322: .src_ts = my_ts,
! 323: .dst_ts = other_ts,
! 324: .mark = child->get_mark(child, FALSE),
! 325: .interface = interface,
! 326: };
! 327: kernel_ipsec_manage_policy_t policy = {
! 328: .type = policy_type,
! 329: .prio = policy_prio,
! 330: .manual_prio = manual_prio,
! 331: .src = host_any,
! 332: .dst = host_any,
! 333: .sa = &sa,
! 334: };
! 335: status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
! 336: if (fwd_out)
! 337: {
! 338: /* uninstall "outbound" forward policy */
! 339: id.dir = POLICY_FWD;
! 340: status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
! 341: }
! 342: /* uninstall in policy */
! 343: id = (kernel_ipsec_policy_id_t){
! 344: .dir = POLICY_IN,
! 345: .src_ts = other_ts,
! 346: .dst_ts = my_ts,
! 347: .mark = child->get_mark(child, TRUE),
! 348: .interface = interface,
! 349: };
! 350: status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
! 351: /* uninstall "inbound" forward policy */
! 352: id.dir = POLICY_FWD;
! 353: status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
! 354: }
! 355: e_other_ts->destroy(e_other_ts);
! 356: }
! 357: e_my_ts->destroy(e_my_ts);
! 358:
! 359: my_ts_list->destroy_offset(my_ts_list,
! 360: offsetof(traffic_selector_t, destroy));
! 361: other_ts_list->destroy_offset(other_ts_list,
! 362: offsetof(traffic_selector_t, destroy));
! 363: host_any6->destroy(host_any6);
! 364: host_any->destroy(host_any);
! 365:
! 366: if (status != SUCCESS)
! 367: {
! 368: DBG1(DBG_CFG, "uninstalling shunt %N 'policy %s' failed",
! 369: ipsec_mode_names, child->get_mode(child), child->get_name(child));
! 370: }
! 371: }
! 372:
! 373: METHOD(shunt_manager_t, uninstall, bool,
! 374: private_shunt_manager_t *this, char *ns, char *name)
! 375: {
! 376: enumerator_t *enumerator;
! 377: entry_t *entry, *found = NULL;
! 378:
! 379: this->lock->write_lock(this->lock);
! 380: enumerator = this->shunts->create_enumerator(this->shunts);
! 381: while (enumerator->enumerate(enumerator, &entry))
! 382: {
! 383: if ((!ns || streq(ns, entry->ns)) &&
! 384: streq(name, entry->cfg->get_name(entry->cfg)))
! 385: {
! 386: this->shunts->remove_at(this->shunts, enumerator);
! 387: found = entry;
! 388: break;
! 389: }
! 390: }
! 391: enumerator->destroy(enumerator);
! 392: this->lock->unlock(this->lock);
! 393:
! 394: if (!found)
! 395: {
! 396: return FALSE;
! 397: }
! 398: uninstall_shunt_policy(found->cfg);
! 399: entry_destroy(found);
! 400: return TRUE;
! 401: }
! 402:
! 403: CALLBACK(filter_entries, bool,
! 404: void *unused, enumerator_t *orig, va_list args)
! 405: {
! 406: entry_t *entry;
! 407: child_cfg_t **cfg;
! 408: char **ns;
! 409:
! 410: VA_ARGS_VGET(args, ns, cfg);
! 411:
! 412: if (orig->enumerate(orig, &entry))
! 413: {
! 414: if (ns)
! 415: {
! 416: *ns = entry->ns;
! 417: }
! 418: *cfg = entry->cfg;
! 419: return TRUE;
! 420: }
! 421: return FALSE;
! 422: }
! 423:
! 424: METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
! 425: private_shunt_manager_t *this)
! 426: {
! 427: this->lock->read_lock(this->lock);
! 428: return enumerator_create_filter(
! 429: this->shunts->create_enumerator(this->shunts),
! 430: filter_entries, this->lock,
! 431: (void*)this->lock->unlock);
! 432: }
! 433:
! 434: METHOD(shunt_manager_t, flush, void,
! 435: private_shunt_manager_t *this)
! 436: {
! 437: entry_t *entry;
! 438:
! 439: this->lock->write_lock(this->lock);
! 440: while (this->installing)
! 441: {
! 442: this->condvar->wait(this->condvar, this->lock);
! 443: }
! 444: while (this->shunts->remove_last(this->shunts, (void**)&entry) == SUCCESS)
! 445: {
! 446: uninstall_shunt_policy(entry->cfg);
! 447: entry_destroy(entry);
! 448: }
! 449: this->installing = INSTALL_DISABLED;
! 450: this->lock->unlock(this->lock);
! 451: }
! 452:
! 453: METHOD(shunt_manager_t, destroy, void,
! 454: private_shunt_manager_t *this)
! 455: {
! 456: this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy));
! 457: this->lock->destroy(this->lock);
! 458: this->condvar->destroy(this->condvar);
! 459: free(this);
! 460: }
! 461:
! 462: /**
! 463: * See header
! 464: */
! 465: shunt_manager_t *shunt_manager_create()
! 466: {
! 467: private_shunt_manager_t *this;
! 468:
! 469: INIT(this,
! 470: .public = {
! 471: .install = _install,
! 472: .uninstall = _uninstall,
! 473: .create_enumerator = _create_enumerator,
! 474: .flush = _flush,
! 475: .destroy = _destroy,
! 476: },
! 477: .shunts = linked_list_create(),
! 478: .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
! 479: .condvar = rwlock_condvar_create(),
! 480: );
! 481:
! 482: return &this->public;
! 483: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>