merged the modularization branch (credentials) back to trunk
[strongswan.git] / src / charon / sa / mediation_manager.c
1 /*
2 * Copyright (C) 2007 Tobias Brunner
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 * $Id$
16 */
17
18 #include "mediation_manager.h"
19
20 #include <pthread.h>
21 #include <daemon.h>
22 #include <utils/linked_list.h>
23 #include <processing/jobs/mediation_job.h>
24
25
26 typedef struct peer_t peer_t;
27
28 /**
29 * An entry in the linked list.
30 */
31 struct peer_t {
32 /** id of the peer */
33 identification_t *id;
34
35 /** sa id of the peer, NULL if offline */
36 ike_sa_id_t *ike_sa_id;
37
38 /** list of peer ids that reuested this peer */
39 linked_list_t *requested_by;
40 };
41
42 /**
43 * Implementation of peer_t.destroy.
44 */
45 static void peer_destroy(peer_t *this)
46 {
47 DESTROY_IF(this->id);
48 DESTROY_IF(this->ike_sa_id);
49 this->requested_by->destroy_offset(this->requested_by, offsetof(identification_t, destroy));
50 free(this);
51 }
52
53 /**
54 * Creates a new entry for the list.
55 */
56 static peer_t *peer_create(identification_t *id, ike_sa_id_t* ike_sa_id)
57 {
58 peer_t *this = malloc_thing(peer_t);
59
60 /* clone everything */
61 this->id = id->clone(id);
62 this->ike_sa_id = ike_sa_id ? ike_sa_id->clone(ike_sa_id) : NULL;
63 this->requested_by = linked_list_create();
64
65 return this;
66 }
67
68
69 typedef struct private_mediation_manager_t private_mediation_manager_t;
70
71 /**
72 * Additional private members of mediation_manager_t.
73 */
74 struct private_mediation_manager_t {
75 /**
76 * Public interface of mediation_manager_t.
77 */
78 mediation_manager_t public;
79
80 /**
81 * Lock for exclusivly accessing the manager.
82 */
83 pthread_mutex_t mutex;
84
85 /**
86 * Linked list with state entries.
87 */
88 linked_list_t *peers;
89 };
90
91 /**
92 * Registers a peer's ID at another peer, if it is not yet registered
93 */
94 static void register_peer(peer_t *peer, identification_t *peer_id)
95 {
96 iterator_t *iterator;
97 identification_t *current;
98
99 iterator = peer->requested_by->create_iterator(peer->requested_by, TRUE);
100 while (iterator->iterate(iterator, (void**)&current))
101 {
102 if (peer_id->equals(peer_id, current))
103 {
104 iterator->destroy(iterator);
105 return;
106 }
107 }
108 iterator->destroy(iterator);
109
110 peer->requested_by->insert_last(peer->requested_by, peer_id->clone(peer_id));
111 }
112
113 /**
114 * Get a peer_t object by a peer's id
115 */
116 static status_t get_peer_by_id(private_mediation_manager_t *this,
117 identification_t *id, peer_t **peer)
118 {
119 iterator_t *iterator;
120 peer_t *current;
121 status_t status = NOT_FOUND;
122
123 iterator = this->peers->create_iterator(this->peers, TRUE);
124 while (iterator->iterate(iterator, (void**)&current))
125 {
126 if (id->equals(id, current->id))
127 {
128 if (peer)
129 {
130 *peer = current;
131 }
132 status = SUCCESS;
133 break;
134 }
135 }
136 iterator->destroy(iterator);
137
138 return status;
139 }
140
141 /**
142 * Check if a given peer is registered at other peers. If so, remove it there
143 * and then remove peers completely that are not online and have no registered
144 * peers.
145 */
146 static void unregister_peer(private_mediation_manager_t *this, identification_t *peer_id)
147 {
148 iterator_t *iterator, *iterator_r;
149 peer_t *peer;
150 identification_t *registered;
151
152 iterator = this->peers->create_iterator(this->peers, TRUE);
153 while (iterator->iterate(iterator, (void**)&peer))
154 {
155 iterator_r = peer->requested_by->create_iterator(peer->requested_by, TRUE);
156 while (iterator_r->iterate(iterator_r, (void**)&registered))
157 {
158 if (peer_id->equals(peer_id, registered))
159 {
160 iterator_r->remove(iterator_r);
161 registered->destroy(registered);
162 break;
163 }
164 }
165 iterator_r->destroy(iterator_r);
166
167 if (!peer->ike_sa_id && !peer->requested_by->get_count(peer->requested_by))
168 {
169 iterator->remove(iterator);
170 peer_destroy(peer);
171 break;
172 }
173 }
174 iterator->destroy(iterator);
175 }
176
177 /**
178 * Implementation of mediation_manager_t.remove
179 */
180 static void remove_sa(private_mediation_manager_t *this, ike_sa_id_t *ike_sa_id)
181 {
182 iterator_t *iterator;
183 peer_t *peer;
184
185 pthread_mutex_lock(&(this->mutex));
186
187 iterator = this->peers->create_iterator(this->peers, TRUE);
188 while (iterator->iterate(iterator, (void**)&peer))
189 {
190 if (ike_sa_id->equals(ike_sa_id, peer->ike_sa_id))
191 {
192 iterator->remove(iterator);
193
194 unregister_peer(this, peer->id);
195
196 peer_destroy(peer);
197 break;
198 }
199 }
200 iterator->destroy(iterator);
201
202 pthread_mutex_unlock(&(this->mutex));
203 }
204
205 /**
206 * Implementation of mediation_manager_t.update_sa_id
207 */
208 static void update_sa_id(private_mediation_manager_t *this, identification_t *peer_id, ike_sa_id_t *ike_sa_id)
209 {
210 iterator_t *iterator;
211 peer_t *peer;
212 bool found = FALSE;
213
214 pthread_mutex_lock(&(this->mutex));
215
216 iterator = this->peers->create_iterator(this->peers, TRUE);
217 while (iterator->iterate(iterator, (void**)&peer))
218 {
219 if (peer_id->equals(peer_id, peer->id))
220 {
221 DESTROY_IF(peer->ike_sa_id);
222 found = TRUE;
223 break;
224 }
225 }
226 iterator->destroy(iterator);
227
228 if (!found)
229 {
230 DBG2(DBG_IKE, "adding peer '%D'", peer_id);
231 peer = peer_create(peer_id, NULL);
232 this->peers->insert_last(this->peers, peer);
233 }
234
235 DBG2(DBG_IKE, "changing registered IKE_SA ID of peer '%D'", peer_id);
236 peer->ike_sa_id = ike_sa_id ? ike_sa_id->clone(ike_sa_id) : NULL;
237
238 /* send callbacks to registered peers */
239 identification_t *requester;
240 while(peer->requested_by->remove_last(peer->requested_by, (void**)&requester) == SUCCESS)
241 {
242 job_t *job = (job_t*)mediation_callback_job_create(requester, peer_id);
243 charon->processor->queue_job(charon->processor, job);
244 }
245
246 pthread_mutex_unlock(&(this->mutex));
247 }
248
249 /**
250 * Implementation of mediation_manager_t.check.
251 */
252 static ike_sa_id_t *check(private_mediation_manager_t *this,
253 identification_t *peer_id)
254 {
255 peer_t *peer;
256 ike_sa_id_t *ike_sa_id;
257
258 pthread_mutex_lock(&(this->mutex));
259
260 if (get_peer_by_id(this, peer_id, &peer) != SUCCESS)
261 {
262 pthread_mutex_unlock(&(this->mutex));
263 return NULL;
264 }
265
266 ike_sa_id = peer->ike_sa_id;
267
268 pthread_mutex_unlock(&(this->mutex));
269
270 return ike_sa_id;
271 }
272
273 /**
274 * Implementation of mediation_manager_t.check_and_register.
275 */
276 static ike_sa_id_t *check_and_register(private_mediation_manager_t *this,
277 identification_t *peer_id, identification_t *requester)
278 {
279 peer_t *peer;
280 ike_sa_id_t *ike_sa_id;
281
282 pthread_mutex_lock(&(this->mutex));
283
284 if (get_peer_by_id(this, peer_id, &peer) != SUCCESS)
285 {
286 DBG2(DBG_IKE, "adding peer %D", peer_id);
287 peer = peer_create(peer_id, NULL);
288 this->peers->insert_last(this->peers, peer);
289 }
290
291 if (!peer->ike_sa_id)
292 {
293 /* the peer is not online */
294 DBG2(DBG_IKE, "requested peer '%D' is offline, registering peer '%D'", peer_id, requester);
295 register_peer(peer, requester);
296 pthread_mutex_unlock(&(this->mutex));
297 return NULL;
298 }
299
300 ike_sa_id = peer->ike_sa_id;
301
302 pthread_mutex_unlock(&(this->mutex));
303
304 return ike_sa_id;
305 }
306
307 /**
308 * Implementation of mediation_manager_t.destroy.
309 */
310 static void destroy(private_mediation_manager_t *this)
311 {
312 pthread_mutex_lock(&(this->mutex));
313
314 this->peers->destroy_function(this->peers, (void*)peer_destroy);
315
316 pthread_mutex_unlock(&(this->mutex));
317 pthread_mutex_destroy(&(this->mutex));
318 free(this);
319 }
320
321 /*
322 * Described in header.
323 */
324 mediation_manager_t *mediation_manager_create()
325 {
326 private_mediation_manager_t *this = malloc_thing(private_mediation_manager_t);
327
328 this->public.destroy = (void(*)(mediation_manager_t*))destroy;
329 this->public.remove = (void(*)(mediation_manager_t*,ike_sa_id_t*))remove_sa;
330 this->public.update_sa_id = (void(*)(mediation_manager_t*,identification_t*,ike_sa_id_t*))update_sa_id;
331 this->public.check = (ike_sa_id_t*(*)(mediation_manager_t*,identification_t*))check;
332 this->public.check_and_register = (ike_sa_id_t*(*)(mediation_manager_t*,identification_t*,identification_t*))check_and_register;
333
334 this->peers = linked_list_create();
335 pthread_mutex_init(&(this->mutex), NULL);
336
337 return (mediation_manager_t*)this;
338 }