Annotation of embedaddon/strongswan/src/libcharon/control/controller.c, revision 1.1.1.2
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);
1.1.1.2 ! misho 420: peer_cfg->destroy(peer_cfg);
1.1 misho 421: if (!ike_sa)
422: {
423: DESTROY_IF(listener->child_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:
433: if (listener->options.limits && ike_sa->get_state(ike_sa) == IKE_CREATED)
434: { /* only check if we are not reusing an IKE_SA */
435: u_int half_open, limit_half_open, limit_job_load;
436:
437: half_open = charon->ike_sa_manager->get_half_open_count(
438: charon->ike_sa_manager, NULL, FALSE);
439: limit_half_open = lib->settings->get_int(lib->settings,
440: "%s.init_limit_half_open", 0, lib->ns);
441: limit_job_load = lib->settings->get_int(lib->settings,
442: "%s.init_limit_job_load", 0, lib->ns);
443: if (limit_half_open && half_open >= limit_half_open)
444: {
445: DBG1(DBG_IKE, "abort IKE_SA initiation, half open IKE_SA count of "
446: "%d exceeds limit of %d", half_open, limit_half_open);
447: charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
448: ike_sa);
449: DESTROY_IF(listener->child_cfg);
450: listener->status = INVALID_STATE;
451: listener_done(listener);
452: return JOB_REQUEUE_NONE;
453: }
454: if (limit_job_load)
455: {
456: u_int jobs = 0, i;
457:
458: for (i = 0; i < JOB_PRIO_MAX; i++)
459: {
460: jobs += lib->processor->get_job_load(lib->processor, i);
461: }
462: if (jobs > limit_job_load)
463: {
464: DBG1(DBG_IKE, "abort IKE_SA initiation, job load of %d exceeds "
465: "limit of %d", jobs, limit_job_load);
466: charon->ike_sa_manager->checkin_and_destroy(
467: charon->ike_sa_manager, ike_sa);
468: DESTROY_IF(listener->child_cfg);
469: listener->status = INVALID_STATE;
470: listener_done(listener);
471: return JOB_REQUEUE_NONE;
472: }
473: }
474: }
475:
476: if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
477: {
478: if (!listener->logger.callback)
479: {
480: listener->status = SUCCESS;
481: }
482: charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
483: }
484: else
485: {
486: listener->status = FAILED;
487: charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
488: ike_sa);
489: }
490: return JOB_REQUEUE_NONE;
491: }
492:
493: METHOD(controller_t, initiate, status_t,
494: private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
495: controller_cb_t callback, void *param, u_int timeout, bool limits)
496: {
497: interface_job_t *job;
498: status_t status;
499:
500: INIT(job,
501: .listener = {
502: .public = {
503: .ike_state_change = _ike_state_change,
504: .child_state_change = _child_state_change,
505: },
506: .logger = {
507: .public = {
508: .log = _listener_log,
509: .get_level = _listener_get_level,
510: },
511: .callback = callback,
512: .param = param,
513: },
514: .status = FAILED,
515: .child_cfg = child_cfg,
516: .peer_cfg = peer_cfg,
517: .lock = spinlock_create(),
518: .options.limits = limits,
519: },
520: .public = {
521: .execute = _initiate_execute,
522: .get_priority = _get_priority_medium,
523: .destroy = _destroy_job,
524: },
525: .refcount = 1,
526: );
527: job->listener.logger.listener = &job->listener;
528: thread_cleanup_push((void*)destroy_job, job);
529:
530: if (callback == NULL)
531: {
532: initiate_execute(job);
533: }
534: else
535: {
536: if (wait_for_listener(job, timeout))
537: {
538: job->listener.status = OUT_OF_RES;
539: }
540: }
541: status = job->listener.status;
542: thread_cleanup_pop(TRUE);
543: return status;
544: }
545:
546: METHOD(job_t, terminate_ike_execute, job_requeue_t,
547: interface_job_t *job)
548: {
549: interface_listener_t *listener = &job->listener;
550: uint32_t unique_id = listener->id;
551: ike_sa_t *ike_sa;
552:
553: ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
554: unique_id);
555: if (!ike_sa)
556: {
557: DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
558: listener->status = NOT_FOUND;
559: /* release listener */
560: listener_done(listener);
561: return JOB_REQUEUE_NONE;
562: }
563: listener->lock->lock(listener->lock);
564: listener->ike_sa = ike_sa;
565: listener->lock->unlock(listener->lock);
566:
1.1.1.2 ! misho 567: if (!listener->logger.callback)
! 568: { /* if we don't wait for the result, either outcome below is a success */
! 569: listener->status = SUCCESS;
! 570: }
! 571:
1.1 misho 572: if (ike_sa->delete(ike_sa, listener->options.force) != DESTROY_ME)
573: { /* delete queued */
574: charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
575: }
576: else
577: {
578: charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
579: ike_sa);
580: }
581: return JOB_REQUEUE_NONE;
582: }
583:
584: METHOD(controller_t, terminate_ike, status_t,
585: controller_t *this, uint32_t unique_id, bool force,
586: controller_cb_t callback, void *param, u_int timeout)
587: {
588: interface_job_t *job;
589: status_t status;
590:
591: INIT(job,
592: .listener = {
593: .public = {
594: .ike_state_change = _ike_state_change_terminate,
595: },
596: .logger = {
597: .public = {
598: .log = _listener_log,
599: .get_level = _listener_get_level,
600: },
601: .callback = callback,
602: .param = param,
603: },
604: .status = FAILED,
605: .id = unique_id,
606: .lock = spinlock_create(),
607: },
608: .public = {
609: .execute = _terminate_ike_execute,
610: .get_priority = _get_priority_medium,
611: .destroy = _destroy_job,
612: },
613: .refcount = 1,
614: );
615: job->listener.logger.listener = &job->listener;
616: thread_cleanup_push((void*)destroy_job, job);
617:
618: if (callback == NULL)
619: {
620: job->listener.options.force = force;
621: terminate_ike_execute(job);
622: }
623: else
624: {
625: if (!timeout)
626: {
627: job->listener.options.force = force;
628: }
629: if (wait_for_listener(job, timeout))
630: {
631: job->listener.status = OUT_OF_RES;
632:
633: if (force)
634: { /* force termination once timeout is reached */
635: job->listener.options.force = TRUE;
636: terminate_ike_execute(job);
637: }
638: }
639: }
640: status = job->listener.status;
641: thread_cleanup_pop(TRUE);
642: return status;
643: }
644:
645: METHOD(job_t, terminate_child_execute, job_requeue_t,
646: interface_job_t *job)
647: {
648: interface_listener_t *listener = &job->listener;
649: uint32_t id = listener->id;
650: child_sa_t *child_sa;
651: ike_sa_t *ike_sa;
652:
653: ike_sa = charon->child_sa_manager->checkout_by_id(charon->child_sa_manager,
654: id, &child_sa);
655: if (!ike_sa)
656: {
657: DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found", id);
658: listener->status = NOT_FOUND;
659: /* release listener */
660: listener_done(listener);
661: return JOB_REQUEUE_NONE;
662: }
663: listener->lock->lock(listener->lock);
664: listener->ike_sa = ike_sa;
665: listener->lock->unlock(listener->lock);
666:
667: if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
668: child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
669: {
670: if (!listener->logger.callback)
671: {
672: listener->status = SUCCESS;
673: }
674: charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
675: }
676: else
677: {
678: listener->status = FAILED;
679: charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
680: ike_sa);
681: }
682: return JOB_REQUEUE_NONE;
683: }
684:
685: METHOD(controller_t, terminate_child, status_t,
686: controller_t *this, uint32_t unique_id,
687: controller_cb_t callback, void *param, u_int timeout)
688: {
689: interface_job_t *job;
690: status_t status;
691:
692: INIT(job,
693: .listener = {
694: .public = {
695: .ike_state_change = _ike_state_change_terminate,
696: .child_state_change = _child_state_change_terminate,
697: },
698: .logger = {
699: .public = {
700: .log = _listener_log,
701: .get_level = _listener_get_level,
702: },
703: .callback = callback,
704: .param = param,
705: },
706: .status = FAILED,
707: .id = unique_id,
708: .lock = spinlock_create(),
709: },
710: .public = {
711: .execute = _terminate_child_execute,
712: .get_priority = _get_priority_medium,
713: .destroy = _destroy_job,
714: },
715: .refcount = 1,
716: );
717: job->listener.logger.listener = &job->listener;
718: thread_cleanup_push((void*)destroy_job, job);
719:
720: if (callback == NULL)
721: {
722: terminate_child_execute(job);
723: }
724: else
725: {
726: if (wait_for_listener(job, timeout))
727: {
728: job->listener.status = OUT_OF_RES;
729: }
730: }
731: status = job->listener.status;
732: thread_cleanup_pop(TRUE);
733: return status;
734: }
735:
736: /**
737: * See header
738: */
739: bool controller_cb_empty(void *param, debug_t group, level_t level,
740: ike_sa_t *ike_sa, const char *message)
741: {
742: return TRUE;
743: }
744:
745: METHOD(controller_t, destroy, void,
746: private_controller_t *this)
747: {
748: free(this);
749: }
750:
751: /*
752: * Described in header-file
753: */
754: controller_t *controller_create(void)
755: {
756: private_controller_t *this;
757:
758: INIT(this,
759: .public = {
760: .create_ike_sa_enumerator = _create_ike_sa_enumerator,
761: .initiate = _initiate,
762: .terminate_ike = _terminate_ike,
763: .terminate_child = _terminate_child,
764: .destroy = _destroy,
765: },
766: );
767:
768: return &this->public;
769: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>