Annotation of embedaddon/strongswan/src/libcharon/control/controller.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2011-2019 Tobias Brunner
! 3: * Copyright (C) 2007-2011 Martin Willi
! 4: * Copyright (C) 2011 revosec AG
! 5: * HSR Hochschule fuer Technik Rapperswil
! 6: *
! 7: * This program is free software; you can redistribute it and/or modify it
! 8: * under the terms of the GNU General Public License as published by the
! 9: * Free Software Foundation; either version 2 of the License, or (at your
! 10: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 11: *
! 12: * This program is distributed in the hope that it will be useful, but
! 13: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 14: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 15: * for more details.
! 16: */
! 17:
! 18: #include "controller.h"
! 19:
! 20: #include <sys/types.h>
! 21: #include <dirent.h>
! 22: #include <sys/stat.h>
! 23:
! 24: #include <daemon.h>
! 25: #include <library.h>
! 26: #include <threading/thread.h>
! 27: #include <threading/spinlock.h>
! 28: #include <threading/semaphore.h>
! 29:
! 30: typedef struct private_controller_t private_controller_t;
! 31: typedef struct interface_listener_t interface_listener_t;
! 32: typedef struct interface_logger_t interface_logger_t;
! 33:
! 34: /**
! 35: * Private data of an stroke_t object.
! 36: */
! 37: struct private_controller_t {
! 38:
! 39: /**
! 40: * Public part of stroke_t object.
! 41: */
! 42: controller_t public;
! 43: };
! 44:
! 45: /**
! 46: * helper struct for the logger interface
! 47: */
! 48: struct interface_logger_t {
! 49: /**
! 50: * public logger interface
! 51: */
! 52: logger_t public;
! 53:
! 54: /**
! 55: * reference to the listener
! 56: */
! 57: interface_listener_t *listener;
! 58:
! 59: /**
! 60: * interface callback (listener gets redirected to here)
! 61: */
! 62: controller_cb_t callback;
! 63:
! 64: /**
! 65: * user parameter to pass to callback
! 66: */
! 67: void *param;
! 68: };
! 69:
! 70: /**
! 71: * helper struct to map listener callbacks to interface callbacks
! 72: */
! 73: struct interface_listener_t {
! 74:
! 75: /**
! 76: * public bus listener interface
! 77: */
! 78: listener_t public;
! 79:
! 80: /**
! 81: * logger interface
! 82: */
! 83: interface_logger_t logger;
! 84:
! 85: /**
! 86: * status of the operation, return to method callers
! 87: */
! 88: status_t status;
! 89:
! 90: /**
! 91: * child configuration, used for initiate
! 92: */
! 93: child_cfg_t *child_cfg;
! 94:
! 95: /**
! 96: * peer configuration, used for initiate
! 97: */
! 98: peer_cfg_t *peer_cfg;
! 99:
! 100: /**
! 101: * IKE_SA to handle
! 102: */
! 103: ike_sa_t *ike_sa;
! 104:
! 105: /**
! 106: * unique ID, used for various methods
! 107: */
! 108: uint32_t id;
! 109:
! 110: /**
! 111: * semaphore to implement wait_for_listener()
! 112: */
! 113: semaphore_t *done;
! 114:
! 115: /**
! 116: * spinlock to update the IKE_SA handle properly
! 117: */
! 118: spinlock_t *lock;
! 119:
! 120: union {
! 121: /**
! 122: * whether to check limits during initiation
! 123: */
! 124: bool limits;
! 125:
! 126: /**
! 127: * whether to force termination
! 128: */
! 129: bool force;
! 130: } options;
! 131: };
! 132:
! 133:
! 134: typedef struct interface_job_t interface_job_t;
! 135:
! 136: /**
! 137: * job for asynchronous listen operations
! 138: */
! 139: struct interface_job_t {
! 140:
! 141: /**
! 142: * job interface
! 143: */
! 144: job_t public;
! 145:
! 146: /**
! 147: * associated listener
! 148: */
! 149: interface_listener_t listener;
! 150:
! 151: /**
! 152: * the job is reference counted as the thread executing a job as well as
! 153: * the thread waiting in wait_for_listener() require it but either of them
! 154: * could be done first
! 155: */
! 156: refcount_t refcount;
! 157: };
! 158:
! 159: /**
! 160: * This function wakes a thread that is waiting in wait_for_listener(),
! 161: * either from a listener or from a job.
! 162: */
! 163: static inline bool listener_done(interface_listener_t *listener)
! 164: {
! 165: if (listener->done)
! 166: {
! 167: listener->done->post(listener->done);
! 168: }
! 169: return FALSE;
! 170: }
! 171:
! 172: /**
! 173: * thread_cleanup_t handler to unregister a listener.
! 174: */
! 175: static void listener_unregister(interface_listener_t *listener)
! 176: {
! 177: charon->bus->remove_listener(charon->bus, &listener->public);
! 178: charon->bus->remove_logger(charon->bus, &listener->logger.public);
! 179: }
! 180:
! 181: /**
! 182: * Registers the listener, executes the job and then waits synchronously until
! 183: * the listener is done or the timeout occurred.
! 184: *
! 185: * @note Use 'return listener_done(listener)' to properly unregister a listener
! 186: *
! 187: * @param listener listener to register
! 188: * @param job job to execute asynchronously when registered, or NULL
! 189: * @param timeout max timeout in ms to listen for events, 0 to disable
! 190: * @return TRUE if timed out
! 191: */
! 192: static bool wait_for_listener(interface_job_t *job, u_int timeout)
! 193: {
! 194: interface_listener_t *listener = &job->listener;
! 195: bool old, timed_out = FALSE;
! 196:
! 197: /* avoid that the job is destroyed too early */
! 198: ref_get(&job->refcount);
! 199:
! 200: listener->done = semaphore_create(0);
! 201:
! 202: charon->bus->add_logger(charon->bus, &listener->logger.public);
! 203: charon->bus->add_listener(charon->bus, &listener->public);
! 204: lib->processor->queue_job(lib->processor, &job->public);
! 205:
! 206: thread_cleanup_push((thread_cleanup_t)listener_unregister, listener);
! 207: old = thread_cancelability(TRUE);
! 208: if (timeout)
! 209: {
! 210: timed_out = listener->done->timed_wait(listener->done, timeout);
! 211: }
! 212: else
! 213: {
! 214: listener->done->wait(listener->done);
! 215: }
! 216: thread_cancelability(old);
! 217: thread_cleanup_pop(TRUE);
! 218: return timed_out;
! 219: }
! 220:
! 221: METHOD(logger_t, listener_log, void,
! 222: interface_logger_t *this, debug_t group, level_t level, int thread,
! 223: ike_sa_t *ike_sa, const char *message)
! 224: {
! 225: ike_sa_t *target;
! 226:
! 227: this->listener->lock->lock(this->listener->lock);
! 228: target = this->listener->ike_sa;
! 229: this->listener->lock->unlock(this->listener->lock);
! 230:
! 231: if (target == ike_sa)
! 232: {
! 233: if (!this->callback(this->param, group, level, ike_sa, message))
! 234: {
! 235: this->listener->status = NEED_MORE;
! 236: listener_done(this->listener);
! 237: }
! 238: }
! 239: }
! 240:
! 241: METHOD(logger_t, listener_get_level, level_t,
! 242: interface_logger_t *this, debug_t group)
! 243: {
! 244: /* in order to allow callback listeners to decide what they want to log
! 245: * we request any log message, but only if we actually want logging */
! 246: return this->callback == controller_cb_empty ? LEVEL_SILENT : LEVEL_PRIVATE;
! 247: }
! 248:
! 249: METHOD(job_t, get_priority_medium, job_priority_t,
! 250: job_t *this)
! 251: {
! 252: return JOB_PRIO_MEDIUM;
! 253: }
! 254:
! 255: METHOD(listener_t, ike_state_change, bool,
! 256: interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
! 257: {
! 258: ike_sa_t *target;
! 259:
! 260: this->lock->lock(this->lock);
! 261: target = this->ike_sa;
! 262: this->lock->unlock(this->lock);
! 263:
! 264: if (target == ike_sa)
! 265: {
! 266: switch (state)
! 267: {
! 268: case IKE_ESTABLISHED:
! 269: {
! 270: #ifdef ME
! 271: peer_cfg_t *peer_cfg = ike_sa->get_peer_cfg(ike_sa);
! 272: #endif /* ME */
! 273: /* we're done if we didn't initiate a CHILD_SA */
! 274: if (!this->child_cfg
! 275: #ifdef ME
! 276: /* the same is always true for mediation connections */
! 277: || peer_cfg->is_mediation(peer_cfg)
! 278: #endif /* ME */
! 279: )
! 280: {
! 281: this->status = SUCCESS;
! 282: return listener_done(this);
! 283: }
! 284: break;
! 285: }
! 286: case IKE_DESTROYING:
! 287: return listener_done(this);
! 288: default:
! 289: break;
! 290: }
! 291: }
! 292: return TRUE;
! 293: }
! 294:
! 295: METHOD(listener_t, ike_state_change_terminate, bool,
! 296: interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
! 297: {
! 298: ike_sa_t *target;
! 299:
! 300: this->lock->lock(this->lock);
! 301: target = this->ike_sa;
! 302: this->lock->unlock(this->lock);
! 303:
! 304: if (target == ike_sa)
! 305: {
! 306: switch (state)
! 307: {
! 308: case IKE_DESTROYING:
! 309: this->status = SUCCESS;
! 310: return listener_done(this);
! 311: default:
! 312: break;
! 313: }
! 314: }
! 315: return TRUE;
! 316: }
! 317:
! 318: METHOD(listener_t, child_state_change, bool,
! 319: interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
! 320: child_sa_state_t state)
! 321: {
! 322: ike_sa_t *target;
! 323:
! 324: this->lock->lock(this->lock);
! 325: target = this->ike_sa;
! 326: this->lock->unlock(this->lock);
! 327:
! 328: if (target == ike_sa)
! 329: {
! 330: switch (state)
! 331: {
! 332: case CHILD_INSTALLED:
! 333: this->status = SUCCESS;
! 334: return listener_done(this);
! 335: case CHILD_DESTROYING:
! 336: switch (child_sa->get_state(child_sa))
! 337: {
! 338: case CHILD_RETRYING:
! 339: /* retrying with a different DH group; survive another
! 340: * initiation round */
! 341: this->status = NEED_MORE;
! 342: return TRUE;
! 343: case CHILD_CREATED:
! 344: if (this->status == NEED_MORE)
! 345: {
! 346: this->status = FAILED;
! 347: return TRUE;
! 348: }
! 349: break;
! 350: default:
! 351: break;
! 352: }
! 353: return listener_done(this);
! 354: default:
! 355: break;
! 356: }
! 357: }
! 358: return TRUE;
! 359: }
! 360:
! 361: METHOD(listener_t, child_state_change_terminate, bool,
! 362: interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
! 363: child_sa_state_t state)
! 364: {
! 365: ike_sa_t *target;
! 366:
! 367: this->lock->lock(this->lock);
! 368: target = this->ike_sa;
! 369: this->lock->unlock(this->lock);
! 370:
! 371: if (target == ike_sa)
! 372: {
! 373: switch (state)
! 374: {
! 375: case CHILD_DESTROYING:
! 376: switch (child_sa->get_state(child_sa))
! 377: {
! 378: case CHILD_DELETED:
! 379: /* proper delete */
! 380: this->status = SUCCESS;
! 381: break;
! 382: default:
! 383: break;
! 384: }
! 385: return listener_done(this);
! 386: default:
! 387: break;
! 388: }
! 389: }
! 390: return TRUE;
! 391: }
! 392:
! 393: METHOD(job_t, destroy_job, void,
! 394: interface_job_t *this)
! 395: {
! 396: if (ref_put(&this->refcount))
! 397: {
! 398: this->listener.lock->destroy(this->listener.lock);
! 399: DESTROY_IF(this->listener.done);
! 400: free(this);
! 401: }
! 402: }
! 403:
! 404: METHOD(controller_t, create_ike_sa_enumerator, enumerator_t*,
! 405: private_controller_t *this, bool wait)
! 406: {
! 407: return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager,
! 408: wait);
! 409: }
! 410:
! 411: METHOD(job_t, initiate_execute, job_requeue_t,
! 412: interface_job_t *job)
! 413: {
! 414: ike_sa_t *ike_sa;
! 415: interface_listener_t *listener = &job->listener;
! 416: peer_cfg_t *peer_cfg = listener->peer_cfg;
! 417:
! 418: ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
! 419: peer_cfg);
! 420: if (!ike_sa)
! 421: {
! 422: DESTROY_IF(listener->child_cfg);
! 423: peer_cfg->destroy(peer_cfg);
! 424: listener->status = FAILED;
! 425: listener_done(listener);
! 426: return JOB_REQUEUE_NONE;
! 427: }
! 428: listener->lock->lock(listener->lock);
! 429: listener->ike_sa = ike_sa;
! 430: listener->lock->unlock(listener->lock);
! 431:
! 432: if (ike_sa->get_peer_cfg(ike_sa) == NULL)
! 433: {
! 434: ike_sa->set_peer_cfg(ike_sa, peer_cfg);
! 435: }
! 436: peer_cfg->destroy(peer_cfg);
! 437:
! 438: if (listener->options.limits && ike_sa->get_state(ike_sa) == IKE_CREATED)
! 439: { /* only check if we are not reusing an IKE_SA */
! 440: u_int half_open, limit_half_open, limit_job_load;
! 441:
! 442: half_open = charon->ike_sa_manager->get_half_open_count(
! 443: charon->ike_sa_manager, NULL, FALSE);
! 444: limit_half_open = lib->settings->get_int(lib->settings,
! 445: "%s.init_limit_half_open", 0, lib->ns);
! 446: limit_job_load = lib->settings->get_int(lib->settings,
! 447: "%s.init_limit_job_load", 0, lib->ns);
! 448: if (limit_half_open && half_open >= limit_half_open)
! 449: {
! 450: DBG1(DBG_IKE, "abort IKE_SA initiation, half open IKE_SA count of "
! 451: "%d exceeds limit of %d", half_open, limit_half_open);
! 452: charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
! 453: ike_sa);
! 454: DESTROY_IF(listener->child_cfg);
! 455: listener->status = INVALID_STATE;
! 456: listener_done(listener);
! 457: return JOB_REQUEUE_NONE;
! 458: }
! 459: if (limit_job_load)
! 460: {
! 461: u_int jobs = 0, i;
! 462:
! 463: for (i = 0; i < JOB_PRIO_MAX; i++)
! 464: {
! 465: jobs += lib->processor->get_job_load(lib->processor, i);
! 466: }
! 467: if (jobs > limit_job_load)
! 468: {
! 469: DBG1(DBG_IKE, "abort IKE_SA initiation, job load of %d exceeds "
! 470: "limit of %d", jobs, limit_job_load);
! 471: charon->ike_sa_manager->checkin_and_destroy(
! 472: charon->ike_sa_manager, ike_sa);
! 473: DESTROY_IF(listener->child_cfg);
! 474: listener->status = INVALID_STATE;
! 475: listener_done(listener);
! 476: return JOB_REQUEUE_NONE;
! 477: }
! 478: }
! 479: }
! 480:
! 481: if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
! 482: {
! 483: if (!listener->logger.callback)
! 484: {
! 485: listener->status = SUCCESS;
! 486: }
! 487: charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
! 488: }
! 489: else
! 490: {
! 491: listener->status = FAILED;
! 492: charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
! 493: ike_sa);
! 494: }
! 495: return JOB_REQUEUE_NONE;
! 496: }
! 497:
! 498: METHOD(controller_t, initiate, status_t,
! 499: private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
! 500: controller_cb_t callback, void *param, u_int timeout, bool limits)
! 501: {
! 502: interface_job_t *job;
! 503: status_t status;
! 504:
! 505: INIT(job,
! 506: .listener = {
! 507: .public = {
! 508: .ike_state_change = _ike_state_change,
! 509: .child_state_change = _child_state_change,
! 510: },
! 511: .logger = {
! 512: .public = {
! 513: .log = _listener_log,
! 514: .get_level = _listener_get_level,
! 515: },
! 516: .callback = callback,
! 517: .param = param,
! 518: },
! 519: .status = FAILED,
! 520: .child_cfg = child_cfg,
! 521: .peer_cfg = peer_cfg,
! 522: .lock = spinlock_create(),
! 523: .options.limits = limits,
! 524: },
! 525: .public = {
! 526: .execute = _initiate_execute,
! 527: .get_priority = _get_priority_medium,
! 528: .destroy = _destroy_job,
! 529: },
! 530: .refcount = 1,
! 531: );
! 532: job->listener.logger.listener = &job->listener;
! 533: thread_cleanup_push((void*)destroy_job, job);
! 534:
! 535: if (callback == NULL)
! 536: {
! 537: initiate_execute(job);
! 538: }
! 539: else
! 540: {
! 541: if (wait_for_listener(job, timeout))
! 542: {
! 543: job->listener.status = OUT_OF_RES;
! 544: }
! 545: }
! 546: status = job->listener.status;
! 547: thread_cleanup_pop(TRUE);
! 548: return status;
! 549: }
! 550:
! 551: METHOD(job_t, terminate_ike_execute, job_requeue_t,
! 552: interface_job_t *job)
! 553: {
! 554: interface_listener_t *listener = &job->listener;
! 555: uint32_t unique_id = listener->id;
! 556: ike_sa_t *ike_sa;
! 557:
! 558: ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
! 559: unique_id);
! 560: if (!ike_sa)
! 561: {
! 562: DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
! 563: listener->status = NOT_FOUND;
! 564: /* release listener */
! 565: listener_done(listener);
! 566: return JOB_REQUEUE_NONE;
! 567: }
! 568: listener->lock->lock(listener->lock);
! 569: listener->ike_sa = ike_sa;
! 570: listener->lock->unlock(listener->lock);
! 571:
! 572: if (ike_sa->delete(ike_sa, listener->options.force) != DESTROY_ME)
! 573: { /* delete queued */
! 574: listener->status = FAILED;
! 575: charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
! 576: }
! 577: else
! 578: {
! 579: if (!listener->logger.callback)
! 580: {
! 581: listener->status = SUCCESS;
! 582: }
! 583: charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
! 584: ike_sa);
! 585: }
! 586: return JOB_REQUEUE_NONE;
! 587: }
! 588:
! 589: METHOD(controller_t, terminate_ike, status_t,
! 590: controller_t *this, uint32_t unique_id, bool force,
! 591: controller_cb_t callback, void *param, u_int timeout)
! 592: {
! 593: interface_job_t *job;
! 594: status_t status;
! 595:
! 596: INIT(job,
! 597: .listener = {
! 598: .public = {
! 599: .ike_state_change = _ike_state_change_terminate,
! 600: },
! 601: .logger = {
! 602: .public = {
! 603: .log = _listener_log,
! 604: .get_level = _listener_get_level,
! 605: },
! 606: .callback = callback,
! 607: .param = param,
! 608: },
! 609: .status = FAILED,
! 610: .id = unique_id,
! 611: .lock = spinlock_create(),
! 612: },
! 613: .public = {
! 614: .execute = _terminate_ike_execute,
! 615: .get_priority = _get_priority_medium,
! 616: .destroy = _destroy_job,
! 617: },
! 618: .refcount = 1,
! 619: );
! 620: job->listener.logger.listener = &job->listener;
! 621: thread_cleanup_push((void*)destroy_job, job);
! 622:
! 623: if (callback == NULL)
! 624: {
! 625: job->listener.options.force = force;
! 626: terminate_ike_execute(job);
! 627: }
! 628: else
! 629: {
! 630: if (!timeout)
! 631: {
! 632: job->listener.options.force = force;
! 633: }
! 634: if (wait_for_listener(job, timeout))
! 635: {
! 636: job->listener.status = OUT_OF_RES;
! 637:
! 638: if (force)
! 639: { /* force termination once timeout is reached */
! 640: job->listener.options.force = TRUE;
! 641: terminate_ike_execute(job);
! 642: }
! 643: }
! 644: }
! 645: status = job->listener.status;
! 646: thread_cleanup_pop(TRUE);
! 647: return status;
! 648: }
! 649:
! 650: METHOD(job_t, terminate_child_execute, job_requeue_t,
! 651: interface_job_t *job)
! 652: {
! 653: interface_listener_t *listener = &job->listener;
! 654: uint32_t id = listener->id;
! 655: child_sa_t *child_sa;
! 656: ike_sa_t *ike_sa;
! 657:
! 658: ike_sa = charon->child_sa_manager->checkout_by_id(charon->child_sa_manager,
! 659: id, &child_sa);
! 660: if (!ike_sa)
! 661: {
! 662: DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found", id);
! 663: listener->status = NOT_FOUND;
! 664: /* release listener */
! 665: listener_done(listener);
! 666: return JOB_REQUEUE_NONE;
! 667: }
! 668: listener->lock->lock(listener->lock);
! 669: listener->ike_sa = ike_sa;
! 670: listener->lock->unlock(listener->lock);
! 671:
! 672: if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
! 673: child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
! 674: {
! 675: if (!listener->logger.callback)
! 676: {
! 677: listener->status = SUCCESS;
! 678: }
! 679: charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
! 680: }
! 681: else
! 682: {
! 683: listener->status = FAILED;
! 684: charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
! 685: ike_sa);
! 686: }
! 687: return JOB_REQUEUE_NONE;
! 688: }
! 689:
! 690: METHOD(controller_t, terminate_child, status_t,
! 691: controller_t *this, uint32_t unique_id,
! 692: controller_cb_t callback, void *param, u_int timeout)
! 693: {
! 694: interface_job_t *job;
! 695: status_t status;
! 696:
! 697: INIT(job,
! 698: .listener = {
! 699: .public = {
! 700: .ike_state_change = _ike_state_change_terminate,
! 701: .child_state_change = _child_state_change_terminate,
! 702: },
! 703: .logger = {
! 704: .public = {
! 705: .log = _listener_log,
! 706: .get_level = _listener_get_level,
! 707: },
! 708: .callback = callback,
! 709: .param = param,
! 710: },
! 711: .status = FAILED,
! 712: .id = unique_id,
! 713: .lock = spinlock_create(),
! 714: },
! 715: .public = {
! 716: .execute = _terminate_child_execute,
! 717: .get_priority = _get_priority_medium,
! 718: .destroy = _destroy_job,
! 719: },
! 720: .refcount = 1,
! 721: );
! 722: job->listener.logger.listener = &job->listener;
! 723: thread_cleanup_push((void*)destroy_job, job);
! 724:
! 725: if (callback == NULL)
! 726: {
! 727: terminate_child_execute(job);
! 728: }
! 729: else
! 730: {
! 731: if (wait_for_listener(job, timeout))
! 732: {
! 733: job->listener.status = OUT_OF_RES;
! 734: }
! 735: }
! 736: status = job->listener.status;
! 737: thread_cleanup_pop(TRUE);
! 738: return status;
! 739: }
! 740:
! 741: /**
! 742: * See header
! 743: */
! 744: bool controller_cb_empty(void *param, debug_t group, level_t level,
! 745: ike_sa_t *ike_sa, const char *message)
! 746: {
! 747: return TRUE;
! 748: }
! 749:
! 750: METHOD(controller_t, destroy, void,
! 751: private_controller_t *this)
! 752: {
! 753: free(this);
! 754: }
! 755:
! 756: /*
! 757: * Described in header-file
! 758: */
! 759: controller_t *controller_create(void)
! 760: {
! 761: private_controller_t *this;
! 762:
! 763: INIT(this,
! 764: .public = {
! 765: .create_ike_sa_enumerator = _create_ike_sa_enumerator,
! 766: .initiate = _initiate,
! 767: .terminate_ike = _terminate_ike,
! 768: .terminate_child = _terminate_child,
! 769: .destroy = _destroy,
! 770: },
! 771: );
! 772:
! 773: return &this->public;
! 774: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>