Annotation of embedaddon/strongswan/src/libcharon/sa/ikev2/tasks/child_delete.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2009-2016 Tobias Brunner
! 3: * Copyright (C) 2006-2007 Martin Willi
! 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 "child_delete.h"
! 18:
! 19: #include <daemon.h>
! 20: #include <encoding/payloads/delete_payload.h>
! 21: #include <processing/jobs/delete_child_sa_job.h>
! 22: #include <sa/ikev2/tasks/child_create.h>
! 23: #include <sa/ikev2/tasks/child_rekey.h>
! 24:
! 25: #ifndef DELETE_REKEYED_DELAY
! 26: #define DELETE_REKEYED_DELAY 5
! 27: #endif
! 28:
! 29: typedef struct private_child_delete_t private_child_delete_t;
! 30:
! 31: /**
! 32: * Private members of a child_delete_t task.
! 33: */
! 34: struct private_child_delete_t {
! 35:
! 36: /**
! 37: * Public methods and task_t interface.
! 38: */
! 39: child_delete_t public;
! 40:
! 41: /**
! 42: * Assigned IKE_SA.
! 43: */
! 44: ike_sa_t *ike_sa;
! 45:
! 46: /**
! 47: * Whether we are the initiator of the exchange
! 48: */
! 49: bool initiator;
! 50:
! 51: /**
! 52: * Protocol of CHILD_SA to delete (as initiator)
! 53: */
! 54: protocol_id_t protocol;
! 55:
! 56: /**
! 57: * Inbound SPI of CHILD_SA to delete (as initiator)
! 58: */
! 59: uint32_t spi;
! 60:
! 61: /**
! 62: * CHILD_SA already expired (as initiator)
! 63: */
! 64: bool expired;
! 65:
! 66: /**
! 67: * CHILD_SAs which get deleted, entry_t*
! 68: */
! 69: linked_list_t *child_sas;
! 70: };
! 71:
! 72: /**
! 73: * Information about a deleted CHILD_SA
! 74: */
! 75: typedef struct {
! 76: /** Deleted CHILD_SA */
! 77: child_sa_t *child_sa;
! 78: /** Whether the CHILD_SA was rekeyed */
! 79: bool rekeyed;
! 80: /** Whether to enforce any delete action policy */
! 81: bool check_delete_action;
! 82: } entry_t;
! 83:
! 84: CALLBACK(match_child, bool,
! 85: entry_t *entry, va_list args)
! 86: {
! 87: child_sa_t *child_sa;
! 88:
! 89: VA_ARGS_VGET(args, child_sa);
! 90: return entry->child_sa == child_sa;
! 91: }
! 92:
! 93: /**
! 94: * build the delete payloads from the listed child_sas
! 95: */
! 96: static void build_payloads(private_child_delete_t *this, message_t *message)
! 97: {
! 98: delete_payload_t *ah = NULL, *esp = NULL;
! 99: enumerator_t *enumerator;
! 100: entry_t *entry;
! 101: protocol_id_t protocol;
! 102: uint32_t spi;
! 103:
! 104: enumerator = this->child_sas->create_enumerator(this->child_sas);
! 105: while (enumerator->enumerate(enumerator, (void**)&entry))
! 106: {
! 107: protocol = entry->child_sa->get_protocol(entry->child_sa);
! 108: spi = entry->child_sa->get_spi(entry->child_sa, TRUE);
! 109:
! 110: switch (protocol)
! 111: {
! 112: case PROTO_ESP:
! 113: if (!esp)
! 114: {
! 115: esp = delete_payload_create(PLV2_DELETE, PROTO_ESP);
! 116: message->add_payload(message, (payload_t*)esp);
! 117: }
! 118: esp->add_spi(esp, spi);
! 119: DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
! 120: protocol_id_names, protocol, ntohl(spi));
! 121: break;
! 122: case PROTO_AH:
! 123: if (ah == NULL)
! 124: {
! 125: ah = delete_payload_create(PLV2_DELETE, PROTO_AH);
! 126: message->add_payload(message, (payload_t*)ah);
! 127: }
! 128: ah->add_spi(ah, spi);
! 129: DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
! 130: protocol_id_names, protocol, ntohl(spi));
! 131: break;
! 132: default:
! 133: break;
! 134: }
! 135: entry->child_sa->set_state(entry->child_sa, CHILD_DELETING);
! 136: }
! 137: enumerator->destroy(enumerator);
! 138: }
! 139:
! 140: /**
! 141: * Check if the given CHILD_SA is the redundant SA created in a rekey collision.
! 142: */
! 143: static bool is_redundant(private_child_delete_t *this, child_sa_t *child)
! 144: {
! 145: enumerator_t *tasks;
! 146: task_t *task;
! 147:
! 148: tasks = this->ike_sa->create_task_enumerator(this->ike_sa,
! 149: TASK_QUEUE_ACTIVE);
! 150: while (tasks->enumerate(tasks, &task))
! 151: {
! 152: if (task->get_type(task) == TASK_CHILD_REKEY)
! 153: {
! 154: child_rekey_t *rekey = (child_rekey_t*)task;
! 155:
! 156: if (rekey->is_redundant(rekey, child))
! 157: {
! 158: tasks->destroy(tasks);
! 159: return TRUE;
! 160: }
! 161: }
! 162: }
! 163: tasks->destroy(tasks);
! 164: return FALSE;
! 165: }
! 166:
! 167: /**
! 168: * Install the outbound CHILD_SA with the given SPI
! 169: */
! 170: static void install_outbound(private_child_delete_t *this,
! 171: protocol_id_t protocol, uint32_t spi)
! 172: {
! 173: child_sa_t *child_sa;
! 174: linked_list_t *my_ts, *other_ts;
! 175: status_t status;
! 176:
! 177: if (!spi)
! 178: {
! 179: return;
! 180: }
! 181:
! 182: child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
! 183: spi, FALSE);
! 184: if (!child_sa)
! 185: {
! 186: DBG1(DBG_IKE, "CHILD_SA not found after rekeying");
! 187: return;
! 188: }
! 189: if (this->initiator && is_redundant(this, child_sa))
! 190: { /* if we won the rekey collision we don't want to install the
! 191: * redundant SA created by the peer */
! 192: return;
! 193: }
! 194:
! 195: status = child_sa->install_outbound(child_sa);
! 196: if (status != SUCCESS)
! 197: {
! 198: DBG1(DBG_IKE, "unable to install outbound IPsec SA (SAD) in kernel");
! 199: charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_SA_FAILED,
! 200: child_sa);
! 201: /* FIXME: delete the new child_sa? */
! 202: return;
! 203: }
! 204:
! 205: my_ts = linked_list_create_from_enumerator(
! 206: child_sa->create_ts_enumerator(child_sa, TRUE));
! 207: other_ts = linked_list_create_from_enumerator(
! 208: child_sa->create_ts_enumerator(child_sa, FALSE));
! 209:
! 210: DBG0(DBG_IKE, "outbound CHILD_SA %s{%d} established "
! 211: "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
! 212: child_sa->get_name(child_sa),
! 213: child_sa->get_unique_id(child_sa),
! 214: ntohl(child_sa->get_spi(child_sa, TRUE)),
! 215: ntohl(child_sa->get_spi(child_sa, FALSE)),
! 216: my_ts, other_ts);
! 217:
! 218: my_ts->destroy(my_ts);
! 219: other_ts->destroy(other_ts);
! 220: }
! 221:
! 222: /**
! 223: * read in payloads and find the children to delete
! 224: */
! 225: static void process_payloads(private_child_delete_t *this, message_t *message)
! 226: {
! 227: enumerator_t *payloads, *spis;
! 228: payload_t *payload;
! 229: delete_payload_t *delete_payload;
! 230: uint32_t spi;
! 231: protocol_id_t protocol;
! 232: child_sa_t *child_sa;
! 233: entry_t *entry;
! 234:
! 235: payloads = message->create_payload_enumerator(message);
! 236: while (payloads->enumerate(payloads, &payload))
! 237: {
! 238: if (payload->get_type(payload) == PLV2_DELETE)
! 239: {
! 240: delete_payload = (delete_payload_t*)payload;
! 241: protocol = delete_payload->get_protocol_id(delete_payload);
! 242: if (protocol != PROTO_ESP && protocol != PROTO_AH)
! 243: {
! 244: continue;
! 245: }
! 246: spis = delete_payload->create_spi_enumerator(delete_payload);
! 247: while (spis->enumerate(spis, &spi))
! 248: {
! 249: child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
! 250: spi, FALSE);
! 251: if (!child_sa)
! 252: {
! 253: DBG1(DBG_IKE, "received DELETE for unknown %N CHILD_SA with"
! 254: " SPI %.8x", protocol_id_names, protocol, ntohl(spi));
! 255: continue;
! 256: }
! 257: DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x",
! 258: protocol_id_names, protocol, ntohl(spi));
! 259:
! 260: if (this->child_sas->find_first(this->child_sas, match_child,
! 261: NULL, child_sa))
! 262: {
! 263: continue;
! 264: }
! 265: INIT(entry,
! 266: .child_sa = child_sa
! 267: );
! 268: switch (child_sa->get_state(child_sa))
! 269: {
! 270: case CHILD_REKEYED:
! 271: entry->rekeyed = TRUE;
! 272: break;
! 273: case CHILD_DELETED:
! 274: /* already deleted but not yet destroyed, ignore */
! 275: case CHILD_DELETING:
! 276: /* we don't send back a delete if we already initiated
! 277: * a delete ourself */
! 278: if (!this->initiator)
! 279: {
! 280: free(entry);
! 281: continue;
! 282: }
! 283: break;
! 284: case CHILD_REKEYING:
! 285: /* we reply as usual, rekeying will fail */
! 286: case CHILD_INSTALLED:
! 287: if (!this->initiator)
! 288: {
! 289: if (is_redundant(this, child_sa))
! 290: {
! 291: entry->rekeyed = TRUE;
! 292: }
! 293: else
! 294: {
! 295: entry->check_delete_action = TRUE;
! 296: }
! 297: }
! 298: break;
! 299: default:
! 300: break;
! 301: }
! 302: this->child_sas->insert_last(this->child_sas, entry);
! 303: }
! 304: spis->destroy(spis);
! 305: }
! 306: }
! 307: payloads->destroy(payloads);
! 308: }
! 309:
! 310: /**
! 311: * destroy the children listed in this->child_sas, reestablish by policy
! 312: */
! 313: static status_t destroy_and_reestablish(private_child_delete_t *this)
! 314: {
! 315: enumerator_t *enumerator;
! 316: entry_t *entry;
! 317: child_sa_t *child_sa;
! 318: child_cfg_t *child_cfg;
! 319: protocol_id_t protocol;
! 320: uint32_t spi, reqid;
! 321: action_t action;
! 322: status_t status = SUCCESS;
! 323: time_t now, expire;
! 324: u_int delay;
! 325:
! 326: now = time_monotonic(NULL);
! 327: delay = lib->settings->get_int(lib->settings, "%s.delete_rekeyed_delay",
! 328: DELETE_REKEYED_DELAY, lib->ns);
! 329:
! 330: enumerator = this->child_sas->create_enumerator(this->child_sas);
! 331: while (enumerator->enumerate(enumerator, (void**)&entry))
! 332: {
! 333: child_sa = entry->child_sa;
! 334: child_sa->set_state(child_sa, CHILD_DELETED);
! 335: /* signal child down event if we weren't rekeying */
! 336: protocol = child_sa->get_protocol(child_sa);
! 337: if (!entry->rekeyed)
! 338: {
! 339: charon->bus->child_updown(charon->bus, child_sa, FALSE);
! 340: }
! 341: else
! 342: {
! 343: install_outbound(this, protocol, child_sa->get_rekey_spi(child_sa));
! 344: /* for rekeyed CHILD_SAs we uninstall the outbound SA but don't
! 345: * immediately destroy it, by default, so we can process delayed
! 346: * packets */
! 347: child_sa->remove_outbound(child_sa);
! 348: expire = child_sa->get_lifetime(child_sa, TRUE);
! 349: if (delay && (!expire || ((now + delay) < expire)))
! 350: {
! 351: lib->scheduler->schedule_job(lib->scheduler,
! 352: (job_t*)delete_child_sa_job_create_id(
! 353: child_sa->get_unique_id(child_sa)), delay);
! 354: continue;
! 355: }
! 356: else if (now < expire)
! 357: { /* let it expire naturally */
! 358: continue;
! 359: }
! 360: /* no delay and no lifetime, destroy it immediately */
! 361: }
! 362: spi = child_sa->get_spi(child_sa, TRUE);
! 363: reqid = child_sa->get_reqid(child_sa);
! 364: child_cfg = child_sa->get_config(child_sa);
! 365: child_cfg->get_ref(child_cfg);
! 366: action = child_sa->get_close_action(child_sa);
! 367:
! 368: this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
! 369:
! 370: if (entry->check_delete_action)
! 371: { /* enforce child_cfg policy if deleted passively */
! 372: switch (action)
! 373: {
! 374: case ACTION_RESTART:
! 375: child_cfg->get_ref(child_cfg);
! 376: status = this->ike_sa->initiate(this->ike_sa, child_cfg,
! 377: reqid, NULL, NULL);
! 378: break;
! 379: case ACTION_ROUTE:
! 380: charon->traps->install(charon->traps,
! 381: this->ike_sa->get_peer_cfg(this->ike_sa),
! 382: child_cfg);
! 383: break;
! 384: default:
! 385: break;
! 386: }
! 387: }
! 388: child_cfg->destroy(child_cfg);
! 389: if (status != SUCCESS)
! 390: {
! 391: break;
! 392: }
! 393: }
! 394: enumerator->destroy(enumerator);
! 395: return status;
! 396: }
! 397:
! 398: /**
! 399: * send closing signals for all CHILD_SAs over the bus
! 400: */
! 401: static void log_children(private_child_delete_t *this)
! 402: {
! 403: linked_list_t *my_ts, *other_ts;
! 404: enumerator_t *enumerator;
! 405: entry_t *entry;
! 406: child_sa_t *child_sa;
! 407: uint64_t bytes_in, bytes_out;
! 408:
! 409: enumerator = this->child_sas->create_enumerator(this->child_sas);
! 410: while (enumerator->enumerate(enumerator, (void**)&entry))
! 411: {
! 412: child_sa = entry->child_sa;
! 413: my_ts = linked_list_create_from_enumerator(
! 414: child_sa->create_ts_enumerator(child_sa, TRUE));
! 415: other_ts = linked_list_create_from_enumerator(
! 416: child_sa->create_ts_enumerator(child_sa, FALSE));
! 417: if (this->expired)
! 418: {
! 419: DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} "
! 420: "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
! 421: child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
! 422: ntohl(child_sa->get_spi(child_sa, TRUE)),
! 423: ntohl(child_sa->get_spi(child_sa, FALSE)), my_ts, other_ts);
! 424: }
! 425: else
! 426: {
! 427: child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, NULL);
! 428: child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL);
! 429:
! 430: DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs %.8x_i "
! 431: "(%llu bytes) %.8x_o (%llu bytes) and TS %#R === %#R",
! 432: child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
! 433: ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
! 434: ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
! 435: my_ts, other_ts);
! 436: }
! 437: my_ts->destroy(my_ts);
! 438: other_ts->destroy(other_ts);
! 439: }
! 440: enumerator->destroy(enumerator);
! 441: }
! 442:
! 443: METHOD(task_t, build_i, status_t,
! 444: private_child_delete_t *this, message_t *message)
! 445: {
! 446: child_sa_t *child_sa;
! 447: entry_t *entry;
! 448:
! 449: child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
! 450: this->spi, TRUE);
! 451: if (!child_sa)
! 452: { /* check if it is an outbound sa */
! 453: child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
! 454: this->spi, FALSE);
! 455: if (!child_sa)
! 456: { /* child does not exist anymore */
! 457: return SUCCESS;
! 458: }
! 459: /* we work only with the inbound SPI */
! 460: this->spi = child_sa->get_spi(child_sa, TRUE);
! 461: }
! 462:
! 463: if (this->expired && child_sa->get_state(child_sa) == CHILD_REKEYED)
! 464: { /* the peer was expected to delete this SA, but if we send a DELETE
! 465: * we might cause a collision there if the CREATE_CHILD_SA response
! 466: * is delayed (the peer wouldn't know if we deleted this SA due to an
! 467: * expire or because of a forced delete by the user and might then
! 468: * ignore the CREATE_CHILD_SA response once it arrives) */
! 469: child_sa->set_state(child_sa, CHILD_DELETED);
! 470: install_outbound(this, this->protocol,
! 471: child_sa->get_rekey_spi(child_sa));
! 472: }
! 473:
! 474: if (child_sa->get_state(child_sa) == CHILD_DELETED)
! 475: { /* DELETEs for this CHILD_SA were already exchanged, but it was not yet
! 476: * destroyed to allow delayed packets to get processed */
! 477: this->ike_sa->destroy_child_sa(this->ike_sa, this->protocol, this->spi);
! 478: message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
! 479: return SUCCESS;
! 480: }
! 481:
! 482: INIT(entry,
! 483: .child_sa = child_sa,
! 484: .rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYED,
! 485: );
! 486: this->child_sas->insert_last(this->child_sas, entry);
! 487: log_children(this);
! 488: build_payloads(this, message);
! 489:
! 490: if (!entry->rekeyed && this->expired)
! 491: {
! 492: child_cfg_t *child_cfg;
! 493:
! 494: DBG1(DBG_IKE, "scheduling CHILD_SA recreate after hard expire");
! 495: child_cfg = child_sa->get_config(child_sa);
! 496: this->ike_sa->queue_task(this->ike_sa, (task_t*)
! 497: child_create_create(this->ike_sa, child_cfg->get_ref(child_cfg),
! 498: FALSE, NULL, NULL));
! 499: }
! 500: return NEED_MORE;
! 501: }
! 502:
! 503: METHOD(task_t, process_i, status_t,
! 504: private_child_delete_t *this, message_t *message)
! 505: {
! 506: process_payloads(this, message);
! 507: DBG1(DBG_IKE, "CHILD_SA closed");
! 508: return destroy_and_reestablish(this);
! 509: }
! 510:
! 511: METHOD(task_t, process_r, status_t,
! 512: private_child_delete_t *this, message_t *message)
! 513: {
! 514: process_payloads(this, message);
! 515: log_children(this);
! 516: return NEED_MORE;
! 517: }
! 518:
! 519: METHOD(task_t, build_r, status_t,
! 520: private_child_delete_t *this, message_t *message)
! 521: {
! 522: build_payloads(this, message);
! 523: DBG1(DBG_IKE, "CHILD_SA closed");
! 524: return destroy_and_reestablish(this);
! 525: }
! 526:
! 527: METHOD(task_t, get_type, task_type_t,
! 528: private_child_delete_t *this)
! 529: {
! 530: return TASK_CHILD_DELETE;
! 531: }
! 532:
! 533: METHOD(child_delete_t , get_child, child_sa_t*,
! 534: private_child_delete_t *this)
! 535: {
! 536: child_sa_t *child_sa = NULL;
! 537: entry_t *entry;
! 538:
! 539: if (this->child_sas->get_first(this->child_sas, (void**)&entry) == SUCCESS)
! 540: {
! 541: child_sa = entry->child_sa;
! 542: }
! 543: return child_sa;
! 544: }
! 545:
! 546: METHOD(task_t, migrate, void,
! 547: private_child_delete_t *this, ike_sa_t *ike_sa)
! 548: {
! 549: this->ike_sa = ike_sa;
! 550:
! 551: this->child_sas->destroy_function(this->child_sas, free);
! 552: this->child_sas = linked_list_create();
! 553: }
! 554:
! 555: METHOD(task_t, destroy, void,
! 556: private_child_delete_t *this)
! 557: {
! 558: this->child_sas->destroy_function(this->child_sas, free);
! 559: free(this);
! 560: }
! 561:
! 562: /*
! 563: * Described in header.
! 564: */
! 565: child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
! 566: uint32_t spi, bool expired)
! 567: {
! 568: private_child_delete_t *this;
! 569:
! 570: INIT(this,
! 571: .public = {
! 572: .task = {
! 573: .get_type = _get_type,
! 574: .migrate = _migrate,
! 575: .destroy = _destroy,
! 576: },
! 577: .get_child = _get_child,
! 578: },
! 579: .ike_sa = ike_sa,
! 580: .child_sas = linked_list_create(),
! 581: .protocol = protocol,
! 582: .spi = spi,
! 583: .expired = expired,
! 584: );
! 585:
! 586: if (protocol != PROTO_NONE)
! 587: {
! 588: this->public.task.build = _build_i;
! 589: this->public.task.process = _process_i;
! 590: this->initiator = TRUE;
! 591: }
! 592: else
! 593: {
! 594: this->public.task.build = _build_r;
! 595: this->public.task.process = _process_r;
! 596: this->initiator = FALSE;
! 597: }
! 598: return &this->public;
! 599: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>