cfd7e1039d40bc79b6eb7b5a9c9edc69f96380cf
[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 METHOD(bus_t, set_sa, void,
146 private_bus_t *this, ike_sa_t *ike_sa)
147 {
148 this->thread_sa->set(this->thread_sa, ike_sa);
149 }
150
151 METHOD(bus_t, get_sa, ike_sa_t*,
152 private_bus_t *this)
153 {
154 return this->thread_sa->get(this->thread_sa);
155 }
156
157 /**
158 * data associated to a signal, passed to callback
159 */
160 typedef struct {
161 /** associated IKE_SA */
162 ike_sa_t *ike_sa;
163 /** invoking thread */
164 long thread;
165 /** debug group */
166 debug_t group;
167 /** debug level */
168 level_t level;
169 /** format string */
170 char *format;
171 /** argument list */
172 va_list args;
173 } log_data_t;
174
175 /**
176 * listener->log() invocation as a list remove callback
177 */
178 static bool log_cb(entry_t *entry, log_data_t *data)
179 {
180 va_list args;
181
182 if (entry->calling || !entry->listener->log)
183 { /* avoid recursive calls */
184 return FALSE;
185 }
186 entry->calling++;
187 va_copy(args, data->args);
188 if (!entry->listener->log(entry->listener, data->group, data->level,
189 data->thread, data->ike_sa, data->format, args))
190 {
191 if (entry->blocker)
192 {
193 entry->blocker = FALSE;
194 entry->condvar->signal(entry->condvar);
195 entry->calling--;
196 }
197 else
198 {
199 entry_destroy(entry);
200 }
201 va_end(args);
202 return TRUE;
203 }
204 va_end(args);
205 entry->calling--;
206 return FALSE;
207 }
208
209 METHOD(bus_t, vlog, void,
210 private_bus_t *this, debug_t group, level_t level,
211 char* format, va_list args)
212 {
213 log_data_t data;
214
215 data.ike_sa = this->thread_sa->get(this->thread_sa);
216 data.thread = thread_current_id();
217 data.group = group;
218 data.level = level;
219 data.format = format;
220 va_copy(data.args, args);
221
222 this->mutex->lock(this->mutex);
223 /* We use the remove() method to invoke all listeners. This is cheap and
224 * does not require an allocation for this performance critical function. */
225 this->listeners->remove(this->listeners, &data, (void*)log_cb);
226 this->mutex->unlock(this->mutex);
227
228 va_end(data.args);
229 }
230
231 METHOD(bus_t, log_, void,
232 private_bus_t *this, debug_t group, level_t level, char* format, ...)
233 {
234 va_list args;
235
236 va_start(args, format);
237 vlog(this, group, level, format, args);
238 va_end(args);
239 }
240
241 /**
242 * unregister a listener
243 */
244 static void unregister_listener(private_bus_t *this, entry_t *entry,
245 enumerator_t *enumerator)
246 {
247 if (entry->blocker)
248 {
249 entry->blocker = FALSE;
250 entry->condvar->signal(entry->condvar);
251 }
252 else
253 {
254 entry_destroy(entry);
255 }
256 this->listeners->remove_at(this->listeners, enumerator);
257 }
258
259 METHOD(bus_t, alert, void,
260 private_bus_t *this, alert_t alert, ...)
261 {
262 enumerator_t *enumerator;
263 ike_sa_t *ike_sa;
264 entry_t *entry;
265 va_list args;
266 bool keep;
267
268 ike_sa = this->thread_sa->get(this->thread_sa);
269
270 this->mutex->lock(this->mutex);
271 enumerator = this->listeners->create_enumerator(this->listeners);
272 while (enumerator->enumerate(enumerator, &entry))
273 {
274 if (entry->calling || !entry->listener->alert)
275 {
276 continue;
277 }
278 entry->calling++;
279 va_start(args, alert);
280 keep = entry->listener->alert(entry->listener, ike_sa, alert, args);
281 va_end(args);
282 entry->calling--;
283 if (!keep)
284 {
285 unregister_listener(this, entry, enumerator);
286 }
287 }
288 enumerator->destroy(enumerator);
289 this->mutex->unlock(this->mutex);
290 }
291
292 METHOD(bus_t, ike_state_change, void,
293 private_bus_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
294 {
295 enumerator_t *enumerator;
296 entry_t *entry;
297 bool keep;
298
299 this->mutex->lock(this->mutex);
300 enumerator = this->listeners->create_enumerator(this->listeners);
301 while (enumerator->enumerate(enumerator, &entry))
302 {
303 if (entry->calling || !entry->listener->ike_state_change)
304 {
305 continue;
306 }
307 entry->calling++;
308 keep = entry->listener->ike_state_change(entry->listener, ike_sa, state);
309 entry->calling--;
310 if (!keep)
311 {
312 unregister_listener(this, entry, enumerator);
313 }
314 }
315 enumerator->destroy(enumerator);
316 this->mutex->unlock(this->mutex);
317 }
318
319 METHOD(bus_t, child_state_change, void,
320 private_bus_t *this, child_sa_t *child_sa, child_sa_state_t state)
321 {
322 enumerator_t *enumerator;
323 ike_sa_t *ike_sa;
324 entry_t *entry;
325 bool keep;
326
327 ike_sa = this->thread_sa->get(this->thread_sa);
328
329 this->mutex->lock(this->mutex);
330 enumerator = this->listeners->create_enumerator(this->listeners);
331 while (enumerator->enumerate(enumerator, &entry))
332 {
333 if (entry->calling || !entry->listener->child_state_change)
334 {
335 continue;
336 }
337 entry->calling++;
338 keep = entry->listener->child_state_change(entry->listener, ike_sa,
339 child_sa, state);
340 entry->calling--;
341 if (!keep)
342 {
343 unregister_listener(this, entry, enumerator);
344 }
345 }
346 enumerator->destroy(enumerator);
347 this->mutex->unlock(this->mutex);
348 }
349
350 METHOD(bus_t, message, void,
351 private_bus_t *this, message_t *message, bool incoming, bool plain)
352 {
353 enumerator_t *enumerator;
354 ike_sa_t *ike_sa;
355 entry_t *entry;
356 bool keep;
357
358 ike_sa = this->thread_sa->get(this->thread_sa);
359
360 this->mutex->lock(this->mutex);
361 enumerator = this->listeners->create_enumerator(this->listeners);
362 while (enumerator->enumerate(enumerator, &entry))
363 {
364 if (entry->calling || !entry->listener->message)
365 {
366 continue;
367 }
368 entry->calling++;
369 keep = entry->listener->message(entry->listener, ike_sa,
370 message, incoming, plain);
371 entry->calling--;
372 if (!keep)
373 {
374 unregister_listener(this, entry, enumerator);
375 }
376 }
377 enumerator->destroy(enumerator);
378 this->mutex->unlock(this->mutex);
379 }
380
381 METHOD(bus_t, ike_keys, void,
382 private_bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh,
383 chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r,
384 ike_sa_t *rekey, shared_key_t *shared)
385 {
386 enumerator_t *enumerator;
387 entry_t *entry;
388 bool keep;
389
390 this->mutex->lock(this->mutex);
391 enumerator = this->listeners->create_enumerator(this->listeners);
392 while (enumerator->enumerate(enumerator, &entry))
393 {
394 if (entry->calling || !entry->listener->ike_keys)
395 {
396 continue;
397 }
398 entry->calling++;
399 keep = entry->listener->ike_keys(entry->listener, ike_sa, dh, dh_other,
400 nonce_i, nonce_r, rekey, shared);
401 entry->calling--;
402 if (!keep)
403 {
404 unregister_listener(this, entry, enumerator);
405 }
406 }
407 enumerator->destroy(enumerator);
408 this->mutex->unlock(this->mutex);
409 }
410
411 METHOD(bus_t, child_keys, void,
412 private_bus_t *this, child_sa_t *child_sa, bool initiator,
413 diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r)
414 {
415 enumerator_t *enumerator;
416 ike_sa_t *ike_sa;
417 entry_t *entry;
418 bool keep;
419
420 ike_sa = this->thread_sa->get(this->thread_sa);
421
422 this->mutex->lock(this->mutex);
423 enumerator = this->listeners->create_enumerator(this->listeners);
424 while (enumerator->enumerate(enumerator, &entry))
425 {
426 if (entry->calling || !entry->listener->child_keys)
427 {
428 continue;
429 }
430 entry->calling++;
431 keep = entry->listener->child_keys(entry->listener, ike_sa, child_sa,
432 initiator, dh, nonce_i, nonce_r);
433 entry->calling--;
434 if (!keep)
435 {
436 unregister_listener(this, entry, enumerator);
437 }
438 }
439 enumerator->destroy(enumerator);
440 this->mutex->unlock(this->mutex);
441 }
442
443 METHOD(bus_t, child_updown, void,
444 private_bus_t *this, child_sa_t *child_sa, bool up)
445 {
446 enumerator_t *enumerator;
447 ike_sa_t *ike_sa;
448 entry_t *entry;
449 bool keep;
450
451 ike_sa = this->thread_sa->get(this->thread_sa);
452
453 this->mutex->lock(this->mutex);
454 enumerator = this->listeners->create_enumerator(this->listeners);
455 while (enumerator->enumerate(enumerator, &entry))
456 {
457 if (entry->calling || !entry->listener->child_updown)
458 {
459 continue;
460 }
461 entry->calling++;
462 keep = entry->listener->child_updown(entry->listener,
463 ike_sa, child_sa, up);
464 entry->calling--;
465 if (!keep)
466 {
467 unregister_listener(this, entry, enumerator);
468 }
469 }
470 enumerator->destroy(enumerator);
471 this->mutex->unlock(this->mutex);
472 }
473
474 METHOD(bus_t, child_rekey, void,
475 private_bus_t *this, child_sa_t *old, child_sa_t *new)
476 {
477 enumerator_t *enumerator;
478 ike_sa_t *ike_sa;
479 entry_t *entry;
480 bool keep;
481
482 ike_sa = this->thread_sa->get(this->thread_sa);
483
484 this->mutex->lock(this->mutex);
485 enumerator = this->listeners->create_enumerator(this->listeners);
486 while (enumerator->enumerate(enumerator, &entry))
487 {
488 if (entry->calling || !entry->listener->child_rekey)
489 {
490 continue;
491 }
492 entry->calling++;
493 keep = entry->listener->child_rekey(entry->listener, ike_sa, old, new);
494 entry->calling--;
495 if (!keep)
496 {
497 unregister_listener(this, entry, enumerator);
498 }
499 }
500 enumerator->destroy(enumerator);
501 this->mutex->unlock(this->mutex);
502 }
503
504 METHOD(bus_t, ike_updown, void,
505 private_bus_t *this, ike_sa_t *ike_sa, bool up)
506 {
507 enumerator_t *enumerator;
508 entry_t *entry;
509 bool keep;
510
511 this->mutex->lock(this->mutex);
512 enumerator = this->listeners->create_enumerator(this->listeners);
513 while (enumerator->enumerate(enumerator, &entry))
514 {
515 if (entry->calling || !entry->listener->ike_updown)
516 {
517 continue;
518 }
519 entry->calling++;
520 keep = entry->listener->ike_updown(entry->listener, ike_sa, up);
521 entry->calling--;
522 if (!keep)
523 {
524 unregister_listener(this, entry, enumerator);
525 }
526 }
527 enumerator->destroy(enumerator);
528 this->mutex->unlock(this->mutex);
529
530 /* a down event for IKE_SA implicitly downs all CHILD_SAs */
531 if (!up)
532 {
533 enumerator_t *enumerator;
534 child_sa_t *child_sa;
535
536 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
537 while (enumerator->enumerate(enumerator, (void**)&child_sa))
538 {
539 child_updown(this, child_sa, FALSE);
540 }
541 enumerator->destroy(enumerator);
542 }
543 }
544
545 METHOD(bus_t, ike_rekey, void,
546 private_bus_t *this, ike_sa_t *old, ike_sa_t *new)
547 {
548 enumerator_t *enumerator;
549 entry_t *entry;
550 bool keep;
551
552 this->mutex->lock(this->mutex);
553 enumerator = this->listeners->create_enumerator(this->listeners);
554 while (enumerator->enumerate(enumerator, &entry))
555 {
556 if (entry->calling || !entry->listener->ike_rekey)
557 {
558 continue;
559 }
560 entry->calling++;
561 keep = entry->listener->ike_rekey(entry->listener, old, new);
562 entry->calling--;
563 if (!keep)
564 {
565 unregister_listener(this, entry, enumerator);
566 }
567 }
568 enumerator->destroy(enumerator);
569 this->mutex->unlock(this->mutex);
570 }
571
572 METHOD(bus_t, authorize, bool,
573 private_bus_t *this, bool final)
574 {
575 enumerator_t *enumerator;
576 ike_sa_t *ike_sa;
577 entry_t *entry;
578 bool keep, success = TRUE;
579
580 ike_sa = this->thread_sa->get(this->thread_sa);
581
582 this->mutex->lock(this->mutex);
583 enumerator = this->listeners->create_enumerator(this->listeners);
584 while (enumerator->enumerate(enumerator, &entry))
585 {
586 if (entry->calling || !entry->listener->authorize)
587 {
588 continue;
589 }
590 entry->calling++;
591 keep = entry->listener->authorize(entry->listener, ike_sa,
592 final, &success);
593 entry->calling--;
594 if (!keep)
595 {
596 unregister_listener(this, entry, enumerator);
597 }
598 if (!success)
599 {
600 break;
601 }
602 }
603 enumerator->destroy(enumerator);
604 this->mutex->unlock(this->mutex);
605 return success;
606 }
607
608 METHOD(bus_t, narrow, void,
609 private_bus_t *this, child_sa_t *child_sa, narrow_hook_t type,
610 linked_list_t *local, linked_list_t *remote)
611 {
612 enumerator_t *enumerator;
613 ike_sa_t *ike_sa;
614 entry_t *entry;
615 bool keep;
616
617 ike_sa = this->thread_sa->get(this->thread_sa);
618
619 this->mutex->lock(this->mutex);
620 enumerator = this->listeners->create_enumerator(this->listeners);
621 while (enumerator->enumerate(enumerator, &entry))
622 {
623 if (entry->calling || !entry->listener->narrow)
624 {
625 continue;
626 }
627 entry->calling++;
628 keep = entry->listener->narrow(entry->listener, ike_sa, child_sa,
629 type, local, remote);
630 entry->calling--;
631 if (!keep)
632 {
633 unregister_listener(this, entry, enumerator);
634 }
635 }
636 enumerator->destroy(enumerator);
637 this->mutex->unlock(this->mutex);
638 }
639
640 METHOD(bus_t, destroy, void,
641 private_bus_t *this)
642 {
643 this->thread_sa->destroy(this->thread_sa);
644 this->mutex->destroy(this->mutex);
645 this->listeners->destroy_function(this->listeners, (void*)entry_destroy);
646 free(this);
647 }
648
649 /*
650 * Described in header.
651 */
652 bus_t *bus_create()
653 {
654 private_bus_t *this;
655
656 INIT(this,
657 .public = {
658 .add_listener = _add_listener,
659 .remove_listener = _remove_listener,
660 .set_sa = _set_sa,
661 .get_sa = _get_sa,
662 .log = _log_,
663 .vlog = _vlog,
664 .alert = _alert,
665 .ike_state_change = _ike_state_change,
666 .child_state_change = _child_state_change,
667 .message = _message,
668 .ike_keys = _ike_keys,
669 .child_keys = _child_keys,
670 .ike_updown = _ike_updown,
671 .ike_rekey = _ike_rekey,
672 .child_updown = _child_updown,
673 .child_rekey = _child_rekey,
674 .authorize = _authorize,
675 .narrow = _narrow,
676 .destroy = _destroy,
677 },
678 .listeners = linked_list_create(),
679 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
680 .thread_sa = thread_value_create(NULL),
681 );
682
683 return &this->public;
684 }
685