Added an advanced duplicate checking plugin with liveness check of old SA
[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 <utils/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 * Mutex to lock hashtables
38 */
39 mutex_t *mutex;
40
41 /**
42 * Hashtable of active IKE_SAs, identification_t => entry_t
43 */
44 hashtable_t *active;
45
46 /**
47 * Hashtable with active liveness checks, identification_t => entry_t
48 */
49 hashtable_t *checking;
50 };
51
52 /**
53 * Entry for hashtables
54 */
55 typedef struct {
56 /** peer identity */
57 identification_t *id;
58 /** IKE_SA identifier */
59 ike_sa_id_t *sa;
60 } entry_t;
61
62 /**
63 * Destroy a hashtable entry
64 */
65 static void entry_destroy(entry_t *this)
66 {
67 this->id->destroy(this->id);
68 this->sa->destroy(this->sa);
69 free(this);
70 }
71
72 /**
73 * Hashtable hash function
74 */
75 static u_int hash(identification_t *key)
76 {
77 return chunk_hash(key->get_encoding(key));
78 }
79
80 /**
81 * Hashtable equals function
82 */
83 static bool equals(identification_t *a, identification_t *b)
84 {
85 return a->equals(a, b);
86 }
87
88 METHOD(listener_t, ike_rekey, bool,
89 private_duplicheck_listener_t *this, ike_sa_t *new, ike_sa_t *old)
90 {
91 this->mutex->lock(this->mutex);
92 /* TODO update entires */
93 this->mutex->unlock(this->mutex);
94 return TRUE;
95 }
96
97 METHOD(listener_t, ike_updown, bool,
98 private_duplicheck_listener_t *this, ike_sa_t *ike_sa, bool up)
99 {
100 identification_t *id;
101 ike_sa_id_t *sa;
102 entry_t *entry;
103 job_t *job;
104
105 sa = ike_sa->get_id(ike_sa);
106 id = ike_sa->get_other_id(ike_sa);
107
108 if (up)
109 {
110 INIT(entry,
111 .id = id->clone(id),
112 .sa = sa->clone(sa),
113 );
114 this->mutex->lock(this->mutex);
115 entry = this->active->put(this->active, entry->id, entry);
116 this->mutex->unlock(this->mutex);
117 if (entry)
118 {
119 DBG1(DBG_CFG, "detected duplicate IKE_SA for '%Y', "
120 "triggering delete for old IKE_SA", id);
121 job = (job_t*)delete_ike_sa_job_create(entry->sa, TRUE);
122 this->mutex->lock(this->mutex);
123 entry = this->checking->put(this->checking, entry->id, entry);
124 this->mutex->unlock(this->mutex);
125 lib->processor->queue_job(lib->processor, job);
126 if (entry)
127 {
128 entry_destroy(entry);
129 }
130 }
131 }
132 else
133 {
134 this->mutex->lock(this->mutex);
135 entry = this->active->remove(this->active, id);
136 if (entry)
137 {
138 entry_destroy(entry);
139 }
140 entry = this->checking->remove(this->checking, id);
141 this->mutex->unlock(this->mutex);
142 if (entry)
143 {
144 DBG1(DBG_CFG, "delete for duplicate IKE_SA '%Y' timed out, "
145 "keeping new IKE_SA", id);
146 entry_destroy(entry);
147 }
148 }
149 return TRUE;
150 }
151
152 METHOD(listener_t, message_hook, bool,
153 private_duplicheck_listener_t *this, ike_sa_t *ike_sa,
154 message_t *message, bool incoming)
155 {
156 if (incoming && !message->get_request(message))
157 {
158 identification_t *id;
159 entry_t *entry;
160
161 id = ike_sa->get_other_id(ike_sa);
162 this->mutex->lock(this->mutex);
163 entry = this->checking->remove(this->checking, id);
164 this->mutex->unlock(this->mutex);
165 if (entry)
166 {
167 DBG1(DBG_CFG, "got a response on a duplicate IKE_SA for '%Y', "
168 "deleting new IKE_SA", id);
169 entry_destroy(entry);
170 this->mutex->lock(this->mutex);
171 entry = this->active->remove(this->active, id);
172 this->mutex->unlock(this->mutex);
173 if (entry)
174 {
175 lib->processor->queue_job(lib->processor,
176 (job_t*)delete_ike_sa_job_create(entry->sa, TRUE));
177 entry_destroy(entry);
178 }
179 }
180 }
181 return TRUE;
182 }
183
184 METHOD(duplicheck_listener_t, destroy, void,
185 private_duplicheck_listener_t *this)
186 {
187 enumerator_t *enumerator;
188 identification_t *key;
189 entry_t *value;
190
191 enumerator = this->active->create_enumerator(this->active);
192 while (enumerator->enumerate(enumerator, &key, &value))
193 {
194 entry_destroy(value);
195 }
196 enumerator->destroy(enumerator);
197
198 enumerator = this->checking->create_enumerator(this->checking);
199 while (enumerator->enumerate(enumerator, &key, &value))
200 {
201 entry_destroy(value);
202 }
203 enumerator->destroy(enumerator);
204
205 this->active->destroy(this->active);
206 this->checking->destroy(this->checking);
207 this->mutex->destroy(this->mutex);
208 free(this);
209 }
210
211 /**
212 * See header
213 */
214 duplicheck_listener_t *duplicheck_listener_create()
215 {
216 private_duplicheck_listener_t *this;
217
218 INIT(this,
219 .public = {
220 .listener = {
221 .ike_rekey = _ike_rekey,
222 .ike_updown = _ike_updown,
223 .message = _message_hook,
224 },
225 .destroy = _destroy,
226 },
227 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
228 .active = hashtable_create((hashtable_hash_t)hash,
229 (hashtable_equals_t)equals, 32),
230 .checking = hashtable_create((hashtable_hash_t)hash,
231 (hashtable_equals_t)equals, 2),
232 );
233
234 return &this->public;
235 }