duplicheck: Include required headers for FreeBSD
[strongswan.git] / src / libcharon / plugins / duplicheck / duplicheck_listener.c
1 /*
2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
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 "duplicheck_listener.h"
17
18 #include <daemon.h>
19 #include <threading/mutex.h>
20 #include <collections/hashtable.h>
21 #include <encoding/payloads/delete_payload.h>
22 #include <processing/jobs/delete_ike_sa_job.h>
23
24 typedef struct private_duplicheck_listener_t private_duplicheck_listener_t;
25
26 /**
27 * Private data of an duplicheck_listener_t object.
28 */
29 struct private_duplicheck_listener_t {
30
31 /**
32 * Public duplicheck_listener_t interface.
33 */
34 duplicheck_listener_t public;
35
36 /**
37 * Socket to send notifications to
38 */
39 duplicheck_notify_t *notify;
40
41 /**
42 * Mutex to lock hashtables
43 */
44 mutex_t *mutex;
45
46 /**
47 * Hashtable of active IKE_SAs, identification_t => entry_t
48 */
49 hashtable_t *active;
50
51 /**
52 * Hashtable with active liveness checks, identification_t => entry_t
53 */
54 hashtable_t *checking;
55 };
56
57 /**
58 * Entry for hashtables
59 */
60 typedef struct {
61 /** peer identity */
62 identification_t *id;
63 /** list of IKE_SA identifiers, ike_sa_id_t */
64 linked_list_t *sas;
65 } entry_t;
66
67 /**
68 * Destroy a hashtable entry
69 */
70 static void entry_destroy(entry_t *this)
71 {
72 this->id->destroy(this->id);
73 this->sas->destroy_offset(this->sas, offsetof(ike_sa_id_t, destroy));
74 free(this);
75 }
76
77 /**
78 * Hashtable hash function
79 */
80 static u_int hash(identification_t *key)
81 {
82 return chunk_hash(key->get_encoding(key));
83 }
84
85 /**
86 * Hashtable equals function
87 */
88 static bool equals(identification_t *a, identification_t *b)
89 {
90 return a->equals(a, b);
91 }
92
93 /**
94 * Put an IKE_SA identifier to hashtable
95 */
96 static void put(hashtable_t *table, identification_t *id, ike_sa_id_t *sa)
97 {
98 entry_t *entry;
99
100 entry = table->get(table, id);
101 if (!entry)
102 {
103 INIT(entry,
104 .id = id->clone(id),
105 .sas = linked_list_create(),
106 );
107 table->put(table, entry->id, entry);
108 }
109 entry->sas->insert_last(entry->sas, sa->clone(sa));
110 }
111
112 /**
113 * Purge an entry from table if it has no IKE_SA identifiers
114 */
115 static void remove_if_empty(hashtable_t *table, entry_t *entry)
116 {
117 if (entry->sas->get_count(entry->sas) == 0)
118 {
119 entry = table->remove(table, entry->id);
120 if (entry)
121 {
122 entry_destroy(entry);
123 }
124 }
125 }
126
127 /**
128 * Remove the first entry found in the table for the given id
129 */
130 static ike_sa_id_t *remove_first(hashtable_t *table, identification_t *id)
131 {
132 ike_sa_id_t *sa = NULL;
133 entry_t *entry;
134
135 entry = table->get(table, id);
136 if (entry)
137 {
138 entry->sas->remove_first(entry->sas, (void**)&sa);
139 remove_if_empty(table, entry);
140 }
141 return sa;
142 }
143
144 /**
145 * Remove a specific IKE_SA ID for the given identity
146 */
147 static bool remove_specific(hashtable_t *table, identification_t *id,
148 ike_sa_id_t *sa)
149 {
150 enumerator_t *enumerator;
151 bool found = FALSE;
152 entry_t *entry;
153 ike_sa_id_t *current;
154
155 entry = table->get(table, id);
156 if (entry)
157 {
158 enumerator = entry->sas->create_enumerator(entry->sas);
159 while (enumerator->enumerate(enumerator, &current))
160 {
161 if (sa->equals(sa, current))
162 {
163 entry->sas->remove_at(entry->sas, enumerator);
164 current->destroy(current);
165 found = TRUE;
166 break;
167 }
168 }
169 enumerator->destroy(enumerator);
170 if (found)
171 {
172 remove_if_empty(table, entry);
173 }
174 }
175 return found;
176 }
177
178 METHOD(listener_t, ike_rekey, bool,
179 private_duplicheck_listener_t *this, ike_sa_t *old, ike_sa_t *new)
180 {
181 this->mutex->lock(this->mutex);
182
183 remove_specific(this->active, old->get_other_id(old), old->get_id(old));
184 put(this->active, new->get_other_id(new), new->get_id(new));
185
186 this->mutex->unlock(this->mutex);
187
188 return TRUE;
189 }
190
191 METHOD(listener_t, ike_updown, bool,
192 private_duplicheck_listener_t *this, ike_sa_t *ike_sa, bool up)
193 {
194 identification_t *id;
195 ike_sa_id_t *sa;
196
197 id = ike_sa->get_other_id(ike_sa);
198
199 this->mutex->lock(this->mutex);
200 if (up)
201 {
202 /* another IKE_SA for this identity active? */
203 sa = remove_first(this->active, id);
204 if (sa)
205 {
206 DBG1(DBG_CFG, "detected duplicate IKE_SA for '%Y', "
207 "triggering delete for old IKE_SA", id);
208 put(this->checking, id, sa);
209 lib->processor->queue_job(lib->processor,
210 (job_t*)delete_ike_sa_job_create(sa, TRUE));
211 sa->destroy(sa);
212 }
213 /* register IKE_SA as the new active */
214 sa = ike_sa->get_id(ike_sa);
215 put(this->active, id, sa);
216 }
217 else
218 {
219 sa = ike_sa->get_id(ike_sa);
220 /* check if closing an IKE_SA currently in checking state */
221 if (remove_specific(this->checking, id, sa))
222 {
223 DBG1(DBG_CFG, "delete for duplicate IKE_SA '%Y' timed out, "
224 "keeping new IKE_SA", id);
225 }
226 /* check normal close of IKE_SA */
227 remove_specific(this->active, id, sa);
228 }
229 this->mutex->unlock(this->mutex);
230
231 return TRUE;
232 }
233
234 METHOD(listener_t, message_hook, bool,
235 private_duplicheck_listener_t *this, ike_sa_t *ike_sa,
236 message_t *message, bool incoming, bool plain)
237 {
238 if (incoming && plain && !message->get_request(message))
239 {
240 identification_t *id;
241 ike_sa_id_t *sa;
242
243 id = ike_sa->get_other_id(ike_sa);
244 sa = ike_sa->get_id(ike_sa);
245
246 this->mutex->lock(this->mutex);
247 if (remove_specific(this->checking, id, sa))
248 {
249 DBG1(DBG_CFG, "got a response on a duplicate IKE_SA for '%Y', "
250 "deleting new IKE_SA", id);
251 charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP);
252 sa = remove_first(this->active, id);
253 if (sa)
254 {
255 lib->processor->queue_job(lib->processor,
256 (job_t*)delete_ike_sa_job_create(sa, TRUE));
257 sa->destroy(sa);
258 }
259 this->mutex->unlock(this->mutex);
260
261 this->notify->send(this->notify, id);
262 }
263 else
264 {
265 this->mutex->unlock(this->mutex);
266 }
267 }
268 return TRUE;
269 }
270
271 METHOD(duplicheck_listener_t, destroy, void,
272 private_duplicheck_listener_t *this)
273 {
274 enumerator_t *enumerator;
275 identification_t *key;
276 entry_t *value;
277
278 enumerator = this->active->create_enumerator(this->active);
279 while (enumerator->enumerate(enumerator, &key, &value))
280 {
281 entry_destroy(value);
282 }
283 enumerator->destroy(enumerator);
284
285 enumerator = this->checking->create_enumerator(this->checking);
286 while (enumerator->enumerate(enumerator, &key, &value))
287 {
288 entry_destroy(value);
289 }
290 enumerator->destroy(enumerator);
291
292 this->active->destroy(this->active);
293 this->checking->destroy(this->checking);
294 this->mutex->destroy(this->mutex);
295 free(this);
296 }
297
298 /**
299 * See header
300 */
301 duplicheck_listener_t *duplicheck_listener_create(duplicheck_notify_t *notify)
302 {
303 private_duplicheck_listener_t *this;
304
305 INIT(this,
306 .public = {
307 .listener = {
308 .ike_rekey = _ike_rekey,
309 .ike_updown = _ike_updown,
310 .message = _message_hook,
311 },
312 .destroy = _destroy,
313 },
314 .notify = notify,
315 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
316 .active = hashtable_create((hashtable_hash_t)hash,
317 (hashtable_equals_t)equals, 32),
318 .checking = hashtable_create((hashtable_hash_t)hash,
319 (hashtable_equals_t)equals, 2),
320 );
321
322 return &this->public;
323 }