Annotation of embedaddon/strongswan/src/libcharon/control/controller.c, revision 1.1.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>