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