2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
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
16 #include "duplicheck_listener.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>
24 typedef struct private_duplicheck_listener_t private_duplicheck_listener_t
;
27 * Private data of an duplicheck_listener_t object.
29 struct private_duplicheck_listener_t
{
32 * Public duplicheck_listener_t interface.
34 duplicheck_listener_t
public;
37 * Socket to send notifications to
39 duplicheck_notify_t
*notify
;
42 * Mutex to lock hashtables
47 * Hashtable of active IKE_SAs, identification_t => entry_t
52 * Hashtable with active liveness checks, identification_t => entry_t
54 hashtable_t
*checking
;
58 * Entry for hashtables
63 /** list of IKE_SA identifiers, ike_sa_id_t */
68 * Destroy a hashtable entry
70 static void entry_destroy(entry_t
*this)
72 this->id
->destroy(this->id
);
73 this->sas
->destroy_offset(this->sas
, offsetof(ike_sa_id_t
, destroy
));
78 * Hashtable hash function
80 static u_int
hash(identification_t
*key
)
82 return chunk_hash(key
->get_encoding(key
));
86 * Hashtable equals function
88 static bool equals(identification_t
*a
, identification_t
*b
)
90 return a
->equals(a
, b
);
94 * Put an IKE_SA identifier to hashtable
96 static void put(hashtable_t
*table
, identification_t
*id
, ike_sa_id_t
*sa
)
100 entry
= table
->get(table
, id
);
105 .sas
= linked_list_create(),
107 table
->put(table
, entry
->id
, entry
);
109 entry
->sas
->insert_last(entry
->sas
, sa
->clone(sa
));
113 * Purge an entry from table if it has no IKE_SA identifiers
115 static void remove_if_empty(hashtable_t
*table
, entry_t
*entry
)
117 if (entry
->sas
->get_count(entry
->sas
) == 0)
119 entry
= table
->remove(table
, entry
->id
);
122 entry_destroy(entry
);
128 * Remove the first entry found in the table for the given id
130 static ike_sa_id_t
*remove_first(hashtable_t
*table
, identification_t
*id
)
132 ike_sa_id_t
*sa
= NULL
;
135 entry
= table
->get(table
, id
);
138 entry
->sas
->remove_first(entry
->sas
, (void**)&sa
);
139 remove_if_empty(table
, entry
);
145 * Remove a specific IKE_SA ID for the given identity
147 static bool remove_specific(hashtable_t
*table
, identification_t
*id
,
150 enumerator_t
*enumerator
;
153 ike_sa_id_t
*current
;
155 entry
= table
->get(table
, id
);
158 enumerator
= entry
->sas
->create_enumerator(entry
->sas
);
159 while (enumerator
->enumerate(enumerator
, ¤t
))
161 if (sa
->equals(sa
, current
))
163 entry
->sas
->remove_at(entry
->sas
, enumerator
);
164 current
->destroy(current
);
169 enumerator
->destroy(enumerator
);
172 remove_if_empty(table
, entry
);
178 METHOD(listener_t
, ike_rekey
, bool,
179 private_duplicheck_listener_t
*this, ike_sa_t
*old
, ike_sa_t
*new)
181 this->mutex
->lock(this->mutex
);
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));
186 this->mutex
->unlock(this->mutex
);
191 METHOD(listener_t
, ike_updown
, bool,
192 private_duplicheck_listener_t
*this, ike_sa_t
*ike_sa
, bool up
)
194 identification_t
*id
;
197 id
= ike_sa
->get_other_id(ike_sa
);
199 this->mutex
->lock(this->mutex
);
202 /* another IKE_SA for this identity active? */
203 sa
= remove_first(this->active
, id
);
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
));
213 /* register IKE_SA as the new active */
214 sa
= ike_sa
->get_id(ike_sa
);
215 put(this->active
, id
, sa
);
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
))
223 DBG1(DBG_CFG
, "delete for duplicate IKE_SA '%Y' timed out, "
224 "keeping new IKE_SA", id
);
226 /* check normal close of IKE_SA */
227 remove_specific(this->active
, id
, sa
);
229 this->mutex
->unlock(this->mutex
);
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
)
238 if (incoming
&& plain
&& !message
->get_request(message
))
240 identification_t
*id
;
243 id
= ike_sa
->get_other_id(ike_sa
);
244 sa
= ike_sa
->get_id(ike_sa
);
246 this->mutex
->lock(this->mutex
);
247 if (remove_specific(this->checking
, id
, sa
))
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
);
255 lib
->processor
->queue_job(lib
->processor
,
256 (job_t
*)delete_ike_sa_job_create(sa
, TRUE
));
259 this->mutex
->unlock(this->mutex
);
261 this->notify
->send(this->notify
, id
);
265 this->mutex
->unlock(this->mutex
);
271 METHOD(duplicheck_listener_t
, destroy
, void,
272 private_duplicheck_listener_t
*this)
274 enumerator_t
*enumerator
;
275 identification_t
*key
;
278 enumerator
= this->active
->create_enumerator(this->active
);
279 while (enumerator
->enumerate(enumerator
, &key
, &value
))
281 entry_destroy(value
);
283 enumerator
->destroy(enumerator
);
285 enumerator
= this->checking
->create_enumerator(this->checking
);
286 while (enumerator
->enumerate(enumerator
, &key
, &value
))
288 entry_destroy(value
);
290 enumerator
->destroy(enumerator
);
292 this->active
->destroy(this->active
);
293 this->checking
->destroy(this->checking
);
294 this->mutex
->destroy(this->mutex
);
301 duplicheck_listener_t
*duplicheck_listener_create(duplicheck_notify_t
*notify
)
303 private_duplicheck_listener_t
*this;
308 .ike_rekey
= _ike_rekey
,
309 .ike_updown
= _ike_updown
,
310 .message
= _message_hook
,
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),
322 return &this->public;