74bd0ec9a9cadfbc17181c9886513c7182d3d8d7
[strongswan.git] / src / libcharon / bus / bus.c
1 /*
2 * Copyright (C) 2006 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "bus.h"
17
18 #include <stdint.h>
19
20 #include <threading/thread.h>
21 #include <threading/thread_value.h>
22 #include <threading/condvar.h>
23 #include <threading/mutex.h>
24
25 typedef struct private_bus_t private_bus_t;
26
27 /**
28 * Private data of a bus_t object.
29 */
30 struct private_bus_t {
31 /**
32 * Public part of a bus_t object.
33 */
34 bus_t public;
35
36 /**
37 * List of registered listeners as entry_t's
38 */
39 linked_list_t *listeners;
40
41 /**
42 * mutex to synchronize active listeners, recursively
43 */
44 mutex_t *mutex;
45
46 /**
47 * Thread local storage the threads IKE_SA
48 */
49 thread_value_t *thread_sa;
50 };
51
52 typedef struct entry_t entry_t;
53
54 /**
55 * a listener entry, either active or passive
56 */
57 struct entry_t {
58
59 /**
60 * registered listener interface
61 */
62 listener_t *listener;
63
64 /**
65 * is this a active listen() call with a blocking thread
66 */
67 bool blocker;
68
69 /**
70 * are we currently calling this listener
71 */
72 int calling;
73
74 /**
75 * condvar where active listeners wait
76 */
77 condvar_t *condvar;
78 };
79
80 /**
81 * create a listener entry
82 */
83 static entry_t *entry_create(listener_t *listener, bool blocker)
84 {
85 entry_t *this = malloc_thing(entry_t);
86
87 this->listener = listener;
88 this->blocker = blocker;
89 this->calling = 0;
90 this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
91
92 return this;
93 }
94
95 /**
96 * destroy an entry_t
97 */
98 static void entry_destroy(entry_t *entry)
99 {
100 entry->condvar->destroy(entry->condvar);
101 free(entry);
102 }
103
104 METHOD(bus_t, add_listener, void,
105 private_bus_t *this, listener_t *listener)
106 {
107 this->mutex->lock(this->mutex);
108 this->listeners->insert_last(this->listeners, entry_create(listener, FALSE));
109 this->mutex->unlock(this->mutex);
110 }
111
112 METHOD(bus_t, remove_listener, void,
113 private_bus_t *this, listener_t *listener)
114 {
115 enumerator_t *enumerator;
116 entry_t *entry;
117
118 this->mutex->lock(this->mutex);
119 enumerator = this->listeners->create_enumerator(this->listeners);
120 while (enumerator->enumerate(enumerator, &entry))
121 {
122 if (entry->listener == listener)
123 {
124 this->listeners->remove_at(this->listeners, enumerator);
125 entry_destroy(entry);
126 break;
127 }
128 }
129 enumerator->destroy(enumerator);
130 this->mutex->unlock(this->mutex);
131 }
132
133 typedef struct cleanup_data_t cleanup_data_t;
134
135 /**
136 * data to remove a listener using thread_cleanup_t handler
137 */
138 struct cleanup_data_t {
139 /** bus instance */
140 private_bus_t *this;
141 /** listener entry */
142 entry_t *entry;
143 };
144
145 /**
146 * thread_cleanup_t handler to remove a listener
147 */
148 static void listener_cleanup(cleanup_data_t *data)
149 {
150 data->this->listeners->remove(data->this->listeners, data->entry, NULL);
151 entry_destroy(data->entry);
152 }
153
154 METHOD(bus_t, listen_, bool,
155 private_bus_t *this, listener_t *listener, job_t *job, u_int timeout)
156 {
157 bool old, timed_out = FALSE;
158 cleanup_data_t data;
159 timeval_t tv, add;
160
161 if (timeout)
162 {
163 add.tv_sec = timeout / 1000;
164 add.tv_usec = (timeout - (add.tv_sec * 1000)) * 1000;
165 time_monotonic(&tv);
166 timeradd(&tv, &add, &tv);
167 }
168
169 data.this = this;
170 data.entry = entry_create(listener, TRUE);
171
172 this->mutex->lock(this->mutex);
173 this->listeners->insert_last(this->listeners, data.entry);
174 lib->processor->queue_job(lib->processor, job);
175 thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex);
176 thread_cleanup_push((thread_cleanup_t)listener_cleanup, &data);
177 old = thread_cancelability(TRUE);
178 while (data.entry->blocker)
179 {
180 if (timeout)
181 {
182 if (data.entry->condvar->timed_wait_abs(data.entry->condvar,
183 this->mutex, tv))
184 {
185 timed_out = TRUE;
186 break;
187 }
188 }
189 else
190 {
191 data.entry->condvar->wait(data.entry->condvar, this->mutex);
192 }
193 }
194 thread_cancelability(old);
195 thread_cleanup_pop(FALSE);
196 /* unlock mutex */
197 thread_cleanup_pop(TRUE);
198 entry_destroy(data.entry);
199 return timed_out;
200 }
201
202 METHOD(bus_t, set_sa, void,
203 private_bus_t *this, ike_sa_t *ike_sa)
204 {
205 this->thread_sa->set(this->thread_sa, ike_sa);
206 }
207
208 METHOD(bus_t, get_sa, ike_sa_t*,
209 private_bus_t *this)
210 {
211 return this->thread_sa->get(this->thread_sa);
212 }
213
214 /**
215 * data associated to a signal, passed to callback
216 */
217 typedef struct {
218 /** associated IKE_SA */
219 ike_sa_t *ike_sa;
220 /** invoking thread */
221 long thread;
222 /** debug group */
223 debug_t group;
224 /** debug level */
225 level_t level;
226 /** format string */
227 char *format;
228 /** argument list */
229 va_list args;
230 } log_data_t;
231
232 /**
233 * listener->log() invocation as a list remove callback
234 */
235 static bool log_cb(entry_t *entry, log_data_t *data)
236 {
237 va_list args;
238
239 if (entry->calling || !entry->listener->log)
240 { /* avoid recursive calls */
241 return FALSE;
242 }
243 entry->calling++;
244 va_copy(args, data->args);
245 if (!entry->listener->log(entry->listener, data->group, data->level,
246 data->thread, data->ike_sa, data->format, args))
247 {
248 if (entry->blocker)
249 {
250 entry->blocker = FALSE;
251 entry->condvar->signal(entry->condvar);
252 entry->calling--;
253 }
254 else
255 {
256 entry_destroy(entry);
257 }
258 va_end(args);
259 return TRUE;
260 }
261 va_end(args);
262 entry->calling--;
263 return FALSE;
264 }
265
266 METHOD(bus_t, vlog, void,
267 private_bus_t *this, debug_t group, level_t level,
268 char* format, va_list args)
269 {
270 log_data_t data;
271
272 data.ike_sa = this->thread_sa->get(this->thread_sa);
273 data.thread = thread_current_id();
274 data.group = group;
275 data.level = level;
276 data.format = format;
277 va_copy(data.args, args);
278
279 this->mutex->lock(this->mutex);
280 /* We use the remove() method to invoke all listeners. This is cheap and
281 * does not require an allocation for this performance critical function. */
282 this->listeners->remove(this->listeners, &data, (void*)log_cb);
283 this->mutex->unlock(this->mutex);
284
285 va_end(data.args);
286 }
287
288 METHOD(bus_t, log_, void,
289 private_bus_t *this, debug_t group, level_t level, char* format, ...)
290 {
291 va_list args;
292
293 va_start(args, format);
294 vlog(this, group, level, format, args);
295 va_end(args);
296 }
297
298 /**
299 * unregister a listener
300 */
301 static void unregister_listener(private_bus_t *this, entry_t *entry,
302 enumerator_t *enumerator)
303 {
304 if (entry->blocker)
305 {
306 entry->blocker = FALSE;
307 entry->condvar->signal(entry->condvar);
308 }
309 else
310 {
311 entry_destroy(entry);
312 }
313 this->listeners->remove_at(this->listeners, enumerator);
314 }
315
316 METHOD(bus_t, alert, void,
317 private_bus_t *this, alert_t alert, ...)
318 {
319 enumerator_t *enumerator;
320 ike_sa_t *ike_sa;
321 entry_t *entry;
322 va_list args;
323 bool keep;
324
325 ike_sa = this->thread_sa->get(this->thread_sa);
326
327 this->mutex->lock(this->mutex);
328 enumerator = this->listeners->create_enumerator(this->listeners);
329 while (enumerator->enumerate(enumerator, &entry))
330 {
331 if (entry->calling || !entry->listener->alert)
332 {
333 continue;
334 }
335 entry->calling++;
336 va_start(args, alert);
337 keep = entry->listener->alert(entry->listener, ike_sa, alert, args);
338 va_end(args);
339 entry->calling--;
340 if (!keep)
341 {
342 unregister_listener(this, entry, enumerator);
343 }
344 }
345 enumerator->destroy(enumerator);
346 this->mutex->unlock(this->mutex);
347 }
348
349 METHOD(bus_t, ike_state_change, void,
350 private_bus_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
351 {
352 enumerator_t *enumerator;
353 entry_t *entry;
354 bool keep;
355
356 this->mutex->lock(this->mutex);
357 enumerator = this->listeners->create_enumerator(this->listeners);
358 while (enumerator->enumerate(enumerator, &entry))
359 {
360 if (entry->calling || !entry->listener->ike_state_change)
361 {
362 continue;
363 }
364 entry->calling++;
365 keep = entry->listener->ike_state_change(entry->listener, ike_sa, state);
366 entry->calling--;
367 if (!keep)
368 {
369 unregister_listener(this, entry, enumerator);
370 }
371 }
372 enumerator->destroy(enumerator);
373 this->mutex->unlock(this->mutex);
374 }
375
376 METHOD(bus_t, child_state_change, void,
377 private_bus_t *this, child_sa_t *child_sa, child_sa_state_t state)
378 {
379 enumerator_t *enumerator;
380 ike_sa_t *ike_sa;
381 entry_t *entry;
382 bool keep;
383
384 ike_sa = this->thread_sa->get(this->thread_sa);
385
386 this->mutex->lock(this->mutex);
387 enumerator = this->listeners->create_enumerator(this->listeners);
388 while (enumerator->enumerate(enumerator, &entry))
389 {
390 if (entry->calling || !entry->listener->child_state_change)
391 {
392 continue;
393 }
394 entry->calling++;
395 keep = entry->listener->child_state_change(entry->listener, ike_sa,
396 child_sa, state);
397 entry->calling--;
398 if (!keep)
399 {
400 unregister_listener(this, entry, enumerator);
401 }
402 }
403 enumerator->destroy(enumerator);
404 this->mutex->unlock(this->mutex);
405 }
406
407 METHOD(bus_t, message, void,
408 private_bus_t *this, message_t *message, bool incoming)
409 {
410 enumerator_t *enumerator;
411 ike_sa_t *ike_sa;
412 entry_t *entry;
413 bool keep;
414
415 ike_sa = this->thread_sa->get(this->thread_sa);
416
417 this->mutex->lock(this->mutex);
418 enumerator = this->listeners->create_enumerator(this->listeners);
419 while (enumerator->enumerate(enumerator, &entry))
420 {
421 if (entry->calling || !entry->listener->message)
422 {
423 continue;
424 }
425 entry->calling++;
426 keep = entry->listener->message(entry->listener, ike_sa,
427 message, incoming);
428 entry->calling--;
429 if (!keep)
430 {
431 unregister_listener(this, entry, enumerator);
432 }
433 }
434 enumerator->destroy(enumerator);
435 this->mutex->unlock(this->mutex);
436 }
437
438 METHOD(bus_t, ike_keys, void,
439 private_bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh,
440 chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey)
441 {
442 enumerator_t *enumerator;
443 entry_t *entry;
444 bool keep;
445
446 this->mutex->lock(this->mutex);
447 enumerator = this->listeners->create_enumerator(this->listeners);
448 while (enumerator->enumerate(enumerator, &entry))
449 {
450 if (entry->calling || !entry->listener->ike_keys)
451 {
452 continue;
453 }
454 entry->calling++;
455 keep = entry->listener->ike_keys(entry->listener, ike_sa, dh,
456 nonce_i, nonce_r, rekey);
457 entry->calling--;
458 if (!keep)
459 {
460 unregister_listener(this, entry, enumerator);
461 }
462 }
463 enumerator->destroy(enumerator);
464 this->mutex->unlock(this->mutex);
465 }
466
467 METHOD(bus_t, child_keys, void,
468 private_bus_t *this, child_sa_t *child_sa, bool initiator,
469 diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r)
470 {
471 enumerator_t *enumerator;
472 ike_sa_t *ike_sa;
473 entry_t *entry;
474 bool keep;
475
476 ike_sa = this->thread_sa->get(this->thread_sa);
477
478 this->mutex->lock(this->mutex);
479 enumerator = this->listeners->create_enumerator(this->listeners);
480 while (enumerator->enumerate(enumerator, &entry))
481 {
482 if (entry->calling || !entry->listener->child_keys)
483 {
484 continue;
485 }
486 entry->calling++;
487 keep = entry->listener->child_keys(entry->listener, ike_sa, child_sa,
488 initiator, dh, nonce_i, nonce_r);
489 entry->calling--;
490 if (!keep)
491 {
492 unregister_listener(this, entry, enumerator);
493 }
494 }
495 enumerator->destroy(enumerator);
496 this->mutex->unlock(this->mutex);
497 }
498
499 METHOD(bus_t, child_updown, void,
500 private_bus_t *this, child_sa_t *child_sa, bool up)
501 {
502 enumerator_t *enumerator;
503 ike_sa_t *ike_sa;
504 entry_t *entry;
505 bool keep;
506
507 ike_sa = this->thread_sa->get(this->thread_sa);
508
509 this->mutex->lock(this->mutex);
510 enumerator = this->listeners->create_enumerator(this->listeners);
511 while (enumerator->enumerate(enumerator, &entry))
512 {
513 if (entry->calling || !entry->listener->child_updown)
514 {
515 continue;
516 }
517 entry->calling++;
518 keep = entry->listener->child_updown(entry->listener,
519 ike_sa, child_sa, up);
520 entry->calling--;
521 if (!keep)
522 {
523 unregister_listener(this, entry, enumerator);
524 }
525 }
526 enumerator->destroy(enumerator);
527 this->mutex->unlock(this->mutex);
528 }
529
530 METHOD(bus_t, child_rekey, void,
531 private_bus_t *this, child_sa_t *old, child_sa_t *new)
532 {
533 enumerator_t *enumerator;
534 ike_sa_t *ike_sa;
535 entry_t *entry;
536 bool keep;
537
538 ike_sa = this->thread_sa->get(this->thread_sa);
539
540 this->mutex->lock(this->mutex);
541 enumerator = this->listeners->create_enumerator(this->listeners);
542 while (enumerator->enumerate(enumerator, &entry))
543 {
544 if (entry->calling || !entry->listener->child_rekey)
545 {
546 continue;
547 }
548 entry->calling++;
549 keep = entry->listener->child_rekey(entry->listener, ike_sa, old, new);
550 entry->calling--;
551 if (!keep)
552 {
553 unregister_listener(this, entry, enumerator);
554 }
555 }
556 enumerator->destroy(enumerator);
557 this->mutex->unlock(this->mutex);
558 }
559
560 METHOD(bus_t, ike_updown, void,
561 private_bus_t *this, ike_sa_t *ike_sa, bool up)
562 {
563 enumerator_t *enumerator;
564 entry_t *entry;
565 bool keep;
566
567 this->mutex->lock(this->mutex);
568 enumerator = this->listeners->create_enumerator(this->listeners);
569 while (enumerator->enumerate(enumerator, &entry))
570 {
571 if (entry->calling || !entry->listener->ike_updown)
572 {
573 continue;
574 }
575 entry->calling++;
576 keep = entry->listener->ike_updown(entry->listener, ike_sa, up);
577 entry->calling--;
578 if (!keep)
579 {
580 unregister_listener(this, entry, enumerator);
581 }
582 }
583 enumerator->destroy(enumerator);
584 this->mutex->unlock(this->mutex);
585
586 /* a down event for IKE_SA implicitly downs all CHILD_SAs */
587 if (!up)
588 {
589 enumerator_t *enumerator;
590 child_sa_t *child_sa;
591
592 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
593 while (enumerator->enumerate(enumerator, (void**)&child_sa))
594 {
595 child_updown(this, child_sa, FALSE);
596 }
597 enumerator->destroy(enumerator);
598 }
599 }
600
601 METHOD(bus_t, ike_rekey, void,
602 private_bus_t *this, ike_sa_t *old, ike_sa_t *new)
603 {
604 enumerator_t *enumerator;
605 entry_t *entry;
606 bool keep;
607
608 this->mutex->lock(this->mutex);
609 enumerator = this->listeners->create_enumerator(this->listeners);
610 while (enumerator->enumerate(enumerator, &entry))
611 {
612 if (entry->calling || !entry->listener->ike_rekey)
613 {
614 continue;
615 }
616 entry->calling++;
617 keep = entry->listener->ike_rekey(entry->listener, old, new);
618 entry->calling--;
619 if (!keep)
620 {
621 unregister_listener(this, entry, enumerator);
622 }
623 }
624 enumerator->destroy(enumerator);
625 this->mutex->unlock(this->mutex);
626 }
627
628 METHOD(bus_t, authorize, bool,
629 private_bus_t *this, bool final)
630 {
631 enumerator_t *enumerator;
632 ike_sa_t *ike_sa;
633 entry_t *entry;
634 bool keep, success = TRUE;
635
636 ike_sa = this->thread_sa->get(this->thread_sa);
637
638 this->mutex->lock(this->mutex);
639 enumerator = this->listeners->create_enumerator(this->listeners);
640 while (enumerator->enumerate(enumerator, &entry))
641 {
642 if (entry->calling || !entry->listener->authorize)
643 {
644 continue;
645 }
646 entry->calling++;
647 keep = entry->listener->authorize(entry->listener, ike_sa,
648 final, &success);
649 entry->calling--;
650 if (!keep)
651 {
652 unregister_listener(this, entry, enumerator);
653 }
654 if (!success)
655 {
656 break;
657 }
658 }
659 enumerator->destroy(enumerator);
660 this->mutex->unlock(this->mutex);
661 return success;
662 }
663
664 METHOD(bus_t, narrow, void,
665 private_bus_t *this, child_sa_t *child_sa, narrow_hook_t type,
666 linked_list_t *local, linked_list_t *remote)
667 {
668 enumerator_t *enumerator;
669 ike_sa_t *ike_sa;
670 entry_t *entry;
671 bool keep;
672
673 ike_sa = this->thread_sa->get(this->thread_sa);
674
675 this->mutex->lock(this->mutex);
676 enumerator = this->listeners->create_enumerator(this->listeners);
677 while (enumerator->enumerate(enumerator, &entry))
678 {
679 if (entry->calling || !entry->listener->narrow)
680 {
681 continue;
682 }
683 entry->calling++;
684 keep = entry->listener->narrow(entry->listener, ike_sa, child_sa,
685 type, local, remote);
686 entry->calling--;
687 if (!keep)
688 {
689 unregister_listener(this, entry, enumerator);
690 }
691 }
692 enumerator->destroy(enumerator);
693 this->mutex->unlock(this->mutex);
694 }
695
696 METHOD(bus_t, destroy, void,
697 private_bus_t *this)
698 {
699 this->thread_sa->destroy(this->thread_sa);
700 this->mutex->destroy(this->mutex);
701 this->listeners->destroy_function(this->listeners, (void*)entry_destroy);
702 free(this);
703 }
704
705 /*
706 * Described in header.
707 */
708 bus_t *bus_create()
709 {
710 private_bus_t *this;
711
712 INIT(this,
713 .public = {
714 .add_listener = _add_listener,
715 .remove_listener = _remove_listener,
716 .listen = _listen_,
717 .set_sa = _set_sa,
718 .get_sa = _get_sa,
719 .log = _log_,
720 .vlog = _vlog,
721 .alert = _alert,
722 .ike_state_change = _ike_state_change,
723 .child_state_change = _child_state_change,
724 .message = _message,
725 .ike_keys = _ike_keys,
726 .child_keys = _child_keys,
727 .ike_updown = _ike_updown,
728 .ike_rekey = _ike_rekey,
729 .child_updown = _child_updown,
730 .child_rekey = _child_rekey,
731 .authorize = _authorize,
732 .narrow = _narrow,
733 .destroy = _destroy,
734 },
735 .listeners = linked_list_create(),
736 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
737 .thread_sa = thread_value_create(NULL),
738 );
739
740 return &this->public;
741 }
742