2 * Copyright (C) 2006 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
20 #include <threading/thread.h>
21 #include <threading/thread_value.h>
22 #include <threading/condvar.h>
23 #include <threading/mutex.h>
25 typedef struct private_bus_t private_bus_t
;
28 * Private data of a bus_t object.
30 struct private_bus_t
{
32 * Public part of a bus_t object.
37 * List of registered listeners as entry_t's
39 linked_list_t
*listeners
;
42 * mutex to synchronize active listeners, recursively
47 * Thread local storage the threads IKE_SA
49 thread_value_t
*thread_sa
;
52 typedef struct entry_t entry_t
;
55 * a listener entry, either active or passive
60 * registered listener interface
65 * is this a active listen() call with a blocking thread
70 * are we currently calling this listener
75 * condvar where active listeners wait
81 * create a listener entry
83 static entry_t
*entry_create(listener_t
*listener
, bool blocker
)
85 entry_t
*this = malloc_thing(entry_t
);
87 this->listener
= listener
;
88 this->blocker
= blocker
;
90 this->condvar
= condvar_create(CONDVAR_TYPE_DEFAULT
);
98 static void entry_destroy(entry_t
*entry
)
100 entry
->condvar
->destroy(entry
->condvar
);
104 METHOD(bus_t
, add_listener
, void,
105 private_bus_t
*this, listener_t
*listener
)
107 this->mutex
->lock(this->mutex
);
108 this->listeners
->insert_last(this->listeners
, entry_create(listener
, FALSE
));
109 this->mutex
->unlock(this->mutex
);
112 METHOD(bus_t
, remove_listener
, void,
113 private_bus_t
*this, listener_t
*listener
)
115 enumerator_t
*enumerator
;
118 this->mutex
->lock(this->mutex
);
119 enumerator
= this->listeners
->create_enumerator(this->listeners
);
120 while (enumerator
->enumerate(enumerator
, &entry
))
122 if (entry
->listener
== listener
)
124 this->listeners
->remove_at(this->listeners
, enumerator
);
125 entry_destroy(entry
);
129 enumerator
->destroy(enumerator
);
130 this->mutex
->unlock(this->mutex
);
133 typedef struct cleanup_data_t cleanup_data_t
;
136 * data to remove a listener using thread_cleanup_t handler
138 struct cleanup_data_t
{
141 /** listener entry */
146 * thread_cleanup_t handler to remove a listener
148 static void listener_cleanup(cleanup_data_t
*data
)
150 data
->this->listeners
->remove(data
->this->listeners
, data
->entry
, NULL
);
151 entry_destroy(data
->entry
);
154 METHOD(bus_t
, listen_
, bool,
155 private_bus_t
*this, listener_t
*listener
, job_t
*job
, u_int timeout
)
157 bool old
, timed_out
= FALSE
;
163 add
.tv_sec
= timeout
/ 1000;
164 add
.tv_usec
= (timeout
- (add
.tv_sec
* 1000)) * 1000;
166 timeradd(&tv
, &add
, &tv
);
170 data
.entry
= entry_create(listener
, TRUE
);
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
)
182 if (data
.entry
->condvar
->timed_wait_abs(data
.entry
->condvar
,
191 data
.entry
->condvar
->wait(data
.entry
->condvar
, this->mutex
);
194 thread_cancelability(old
);
195 thread_cleanup_pop(FALSE
);
197 thread_cleanup_pop(TRUE
);
198 entry_destroy(data
.entry
);
202 METHOD(bus_t
, set_sa
, void,
203 private_bus_t
*this, ike_sa_t
*ike_sa
)
205 this->thread_sa
->set(this->thread_sa
, ike_sa
);
208 METHOD(bus_t
, get_sa
, ike_sa_t
*,
211 return this->thread_sa
->get(this->thread_sa
);
215 * data associated to a signal, passed to callback
218 /** associated IKE_SA */
220 /** invoking thread */
233 * listener->log() invocation as a list remove callback
235 static bool log_cb(entry_t
*entry
, log_data_t
*data
)
239 if (entry
->calling
|| !entry
->listener
->log
)
240 { /* avoid recursive calls */
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
))
250 entry
->blocker
= FALSE
;
251 entry
->condvar
->signal(entry
->condvar
);
256 entry_destroy(entry
);
266 METHOD(bus_t
, vlog
, void,
267 private_bus_t
*this, debug_t group
, level_t level
,
268 char* format
, va_list args
)
272 data
.ike_sa
= this->thread_sa
->get(this->thread_sa
);
273 data
.thread
= thread_current_id();
276 data
.format
= format
;
277 va_copy(data
.args
, args
);
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
);
288 METHOD(bus_t
, log_
, void,
289 private_bus_t
*this, debug_t group
, level_t level
, char* format
, ...)
293 va_start(args
, format
);
294 vlog(this, group
, level
, format
, args
);
299 * unregister a listener
301 static void unregister_listener(private_bus_t
*this, entry_t
*entry
,
302 enumerator_t
*enumerator
)
306 entry
->blocker
= FALSE
;
307 entry
->condvar
->signal(entry
->condvar
);
311 entry_destroy(entry
);
313 this->listeners
->remove_at(this->listeners
, enumerator
);
316 METHOD(bus_t
, alert
, void,
317 private_bus_t
*this, alert_t alert
, ...)
319 enumerator_t
*enumerator
;
325 ike_sa
= this->thread_sa
->get(this->thread_sa
);
327 this->mutex
->lock(this->mutex
);
328 enumerator
= this->listeners
->create_enumerator(this->listeners
);
329 while (enumerator
->enumerate(enumerator
, &entry
))
331 if (entry
->calling
|| !entry
->listener
->alert
)
336 va_start(args
, alert
);
337 keep
= entry
->listener
->alert(entry
->listener
, ike_sa
, alert
, args
);
342 unregister_listener(this, entry
, enumerator
);
345 enumerator
->destroy(enumerator
);
346 this->mutex
->unlock(this->mutex
);
349 METHOD(bus_t
, ike_state_change
, void,
350 private_bus_t
*this, ike_sa_t
*ike_sa
, ike_sa_state_t state
)
352 enumerator_t
*enumerator
;
356 this->mutex
->lock(this->mutex
);
357 enumerator
= this->listeners
->create_enumerator(this->listeners
);
358 while (enumerator
->enumerate(enumerator
, &entry
))
360 if (entry
->calling
|| !entry
->listener
->ike_state_change
)
365 keep
= entry
->listener
->ike_state_change(entry
->listener
, ike_sa
, state
);
369 unregister_listener(this, entry
, enumerator
);
372 enumerator
->destroy(enumerator
);
373 this->mutex
->unlock(this->mutex
);
376 METHOD(bus_t
, child_state_change
, void,
377 private_bus_t
*this, child_sa_t
*child_sa
, child_sa_state_t state
)
379 enumerator_t
*enumerator
;
384 ike_sa
= this->thread_sa
->get(this->thread_sa
);
386 this->mutex
->lock(this->mutex
);
387 enumerator
= this->listeners
->create_enumerator(this->listeners
);
388 while (enumerator
->enumerate(enumerator
, &entry
))
390 if (entry
->calling
|| !entry
->listener
->child_state_change
)
395 keep
= entry
->listener
->child_state_change(entry
->listener
, ike_sa
,
400 unregister_listener(this, entry
, enumerator
);
403 enumerator
->destroy(enumerator
);
404 this->mutex
->unlock(this->mutex
);
407 METHOD(bus_t
, message
, void,
408 private_bus_t
*this, message_t
*message
, bool incoming
)
410 enumerator_t
*enumerator
;
415 ike_sa
= this->thread_sa
->get(this->thread_sa
);
417 this->mutex
->lock(this->mutex
);
418 enumerator
= this->listeners
->create_enumerator(this->listeners
);
419 while (enumerator
->enumerate(enumerator
, &entry
))
421 if (entry
->calling
|| !entry
->listener
->message
)
426 keep
= entry
->listener
->message(entry
->listener
, ike_sa
,
431 unregister_listener(this, entry
, enumerator
);
434 enumerator
->destroy(enumerator
);
435 this->mutex
->unlock(this->mutex
);
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
)
442 enumerator_t
*enumerator
;
446 this->mutex
->lock(this->mutex
);
447 enumerator
= this->listeners
->create_enumerator(this->listeners
);
448 while (enumerator
->enumerate(enumerator
, &entry
))
450 if (entry
->calling
|| !entry
->listener
->ike_keys
)
455 keep
= entry
->listener
->ike_keys(entry
->listener
, ike_sa
, dh
,
456 nonce_i
, nonce_r
, rekey
);
460 unregister_listener(this, entry
, enumerator
);
463 enumerator
->destroy(enumerator
);
464 this->mutex
->unlock(this->mutex
);
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
)
471 enumerator_t
*enumerator
;
476 ike_sa
= this->thread_sa
->get(this->thread_sa
);
478 this->mutex
->lock(this->mutex
);
479 enumerator
= this->listeners
->create_enumerator(this->listeners
);
480 while (enumerator
->enumerate(enumerator
, &entry
))
482 if (entry
->calling
|| !entry
->listener
->child_keys
)
487 keep
= entry
->listener
->child_keys(entry
->listener
, ike_sa
, child_sa
,
488 initiator
, dh
, nonce_i
, nonce_r
);
492 unregister_listener(this, entry
, enumerator
);
495 enumerator
->destroy(enumerator
);
496 this->mutex
->unlock(this->mutex
);
499 METHOD(bus_t
, child_updown
, void,
500 private_bus_t
*this, child_sa_t
*child_sa
, bool up
)
502 enumerator_t
*enumerator
;
507 ike_sa
= this->thread_sa
->get(this->thread_sa
);
509 this->mutex
->lock(this->mutex
);
510 enumerator
= this->listeners
->create_enumerator(this->listeners
);
511 while (enumerator
->enumerate(enumerator
, &entry
))
513 if (entry
->calling
|| !entry
->listener
->child_updown
)
518 keep
= entry
->listener
->child_updown(entry
->listener
,
519 ike_sa
, child_sa
, up
);
523 unregister_listener(this, entry
, enumerator
);
526 enumerator
->destroy(enumerator
);
527 this->mutex
->unlock(this->mutex
);
530 METHOD(bus_t
, child_rekey
, void,
531 private_bus_t
*this, child_sa_t
*old
, child_sa_t
*new)
533 enumerator_t
*enumerator
;
538 ike_sa
= this->thread_sa
->get(this->thread_sa
);
540 this->mutex
->lock(this->mutex
);
541 enumerator
= this->listeners
->create_enumerator(this->listeners
);
542 while (enumerator
->enumerate(enumerator
, &entry
))
544 if (entry
->calling
|| !entry
->listener
->child_rekey
)
549 keep
= entry
->listener
->child_rekey(entry
->listener
, ike_sa
, old
, new);
553 unregister_listener(this, entry
, enumerator
);
556 enumerator
->destroy(enumerator
);
557 this->mutex
->unlock(this->mutex
);
560 METHOD(bus_t
, ike_updown
, void,
561 private_bus_t
*this, ike_sa_t
*ike_sa
, bool up
)
563 enumerator_t
*enumerator
;
567 this->mutex
->lock(this->mutex
);
568 enumerator
= this->listeners
->create_enumerator(this->listeners
);
569 while (enumerator
->enumerate(enumerator
, &entry
))
571 if (entry
->calling
|| !entry
->listener
->ike_updown
)
576 keep
= entry
->listener
->ike_updown(entry
->listener
, ike_sa
, up
);
580 unregister_listener(this, entry
, enumerator
);
583 enumerator
->destroy(enumerator
);
584 this->mutex
->unlock(this->mutex
);
586 /* a down event for IKE_SA implicitly downs all CHILD_SAs */
589 enumerator_t
*enumerator
;
590 child_sa_t
*child_sa
;
592 enumerator
= ike_sa
->create_child_sa_enumerator(ike_sa
);
593 while (enumerator
->enumerate(enumerator
, (void**)&child_sa
))
595 child_updown(this, child_sa
, FALSE
);
597 enumerator
->destroy(enumerator
);
601 METHOD(bus_t
, ike_rekey
, void,
602 private_bus_t
*this, ike_sa_t
*old
, ike_sa_t
*new)
604 enumerator_t
*enumerator
;
608 this->mutex
->lock(this->mutex
);
609 enumerator
= this->listeners
->create_enumerator(this->listeners
);
610 while (enumerator
->enumerate(enumerator
, &entry
))
612 if (entry
->calling
|| !entry
->listener
->ike_rekey
)
617 keep
= entry
->listener
->ike_rekey(entry
->listener
, old
, new);
621 unregister_listener(this, entry
, enumerator
);
624 enumerator
->destroy(enumerator
);
625 this->mutex
->unlock(this->mutex
);
628 METHOD(bus_t
, authorize
, bool,
629 private_bus_t
*this, bool final
)
631 enumerator_t
*enumerator
;
634 bool keep
, success
= TRUE
;
636 ike_sa
= this->thread_sa
->get(this->thread_sa
);
638 this->mutex
->lock(this->mutex
);
639 enumerator
= this->listeners
->create_enumerator(this->listeners
);
640 while (enumerator
->enumerate(enumerator
, &entry
))
642 if (entry
->calling
|| !entry
->listener
->authorize
)
647 keep
= entry
->listener
->authorize(entry
->listener
, ike_sa
,
652 unregister_listener(this, entry
, enumerator
);
659 enumerator
->destroy(enumerator
);
660 this->mutex
->unlock(this->mutex
);
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
)
668 enumerator_t
*enumerator
;
673 ike_sa
= this->thread_sa
->get(this->thread_sa
);
675 this->mutex
->lock(this->mutex
);
676 enumerator
= this->listeners
->create_enumerator(this->listeners
);
677 while (enumerator
->enumerate(enumerator
, &entry
))
679 if (entry
->calling
|| !entry
->listener
->narrow
)
684 keep
= entry
->listener
->narrow(entry
->listener
, ike_sa
, child_sa
,
685 type
, local
, remote
);
689 unregister_listener(this, entry
, enumerator
);
692 enumerator
->destroy(enumerator
);
693 this->mutex
->unlock(this->mutex
);
696 METHOD(bus_t
, destroy
, void,
699 this->thread_sa
->destroy(this->thread_sa
);
700 this->mutex
->destroy(this->mutex
);
701 this->listeners
->destroy_function(this->listeners
, (void*)entry_destroy
);
706 * Described in header.
714 .add_listener
= _add_listener
,
715 .remove_listener
= _remove_listener
,
722 .ike_state_change
= _ike_state_change
,
723 .child_state_change
= _child_state_change
,
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
,
735 .listeners
= linked_list_create(),
736 .mutex
= mutex_create(MUTEX_TYPE_RECURSIVE
),
737 .thread_sa
= thread_value_create(NULL
),
740 return &this->public;