Notify duplicate detections over a UNIX sockets to listening applications
[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 * 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 /** IKE_SA identifier */
64 ike_sa_id_t *sa;
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->sa->destroy(this->sa);
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 METHOD(listener_t, ike_rekey, bool,
94 private_duplicheck_listener_t *this, ike_sa_t *new, ike_sa_t *old)
95 {
96 this->mutex->lock(this->mutex);
97 /* TODO update entires */
98 this->mutex->unlock(this->mutex);
99 return TRUE;
100 }
101
102 METHOD(listener_t, ike_updown, bool,
103 private_duplicheck_listener_t *this, ike_sa_t *ike_sa, bool up)
104 {
105 identification_t *id;
106 ike_sa_id_t *sa;
107 entry_t *entry;
108 job_t *job;
109
110 sa = ike_sa->get_id(ike_sa);
111 id = ike_sa->get_other_id(ike_sa);
112
113 if (up)
114 {
115 INIT(entry,
116 .id = id->clone(id),
117 .sa = sa->clone(sa),
118 );
119 this->mutex->lock(this->mutex);
120 entry = this->active->put(this->active, entry->id, entry);
121 this->mutex->unlock(this->mutex);
122 if (entry)
123 {
124 DBG1(DBG_CFG, "detected duplicate IKE_SA for '%Y', "
125 "triggering delete for old IKE_SA", id);
126 job = (job_t*)delete_ike_sa_job_create(entry->sa, TRUE);
127 this->mutex->lock(this->mutex);
128 entry = this->checking->put(this->checking, entry->id, entry);
129 this->mutex->unlock(this->mutex);
130 lib->processor->queue_job(lib->processor, job);
131 if (entry)
132 {
133 entry_destroy(entry);
134 }
135 }
136 }
137 else
138 {
139 this->mutex->lock(this->mutex);
140 entry = this->active->remove(this->active, id);
141 if (entry)
142 {
143 entry_destroy(entry);
144 }
145 entry = this->checking->remove(this->checking, id);
146 this->mutex->unlock(this->mutex);
147 if (entry)
148 {
149 DBG1(DBG_CFG, "delete for duplicate IKE_SA '%Y' timed out, "
150 "keeping new IKE_SA", id);
151 entry_destroy(entry);
152 }
153 }
154 return TRUE;
155 }
156
157 METHOD(listener_t, message_hook, bool,
158 private_duplicheck_listener_t *this, ike_sa_t *ike_sa,
159 message_t *message, bool incoming)
160 {
161 if (incoming && !message->get_request(message))
162 {
163 identification_t *id;
164 entry_t *entry;
165
166 id = ike_sa->get_other_id(ike_sa);
167 this->mutex->lock(this->mutex);
168 entry = this->checking->remove(this->checking, id);
169 this->mutex->unlock(this->mutex);
170 if (entry)
171 {
172 DBG1(DBG_CFG, "got a response on a duplicate IKE_SA for '%Y', "
173 "deleting new IKE_SA", id);
174 entry_destroy(entry);
175 this->mutex->lock(this->mutex);
176 entry = this->active->remove(this->active, id);
177 this->mutex->unlock(this->mutex);
178 if (entry)
179 {
180 lib->processor->queue_job(lib->processor,
181 (job_t*)delete_ike_sa_job_create(entry->sa, TRUE));
182 entry_destroy(entry);
183 }
184 this->notify->send(this->notify, id);
185 }
186 }
187 return TRUE;
188 }
189
190 METHOD(duplicheck_listener_t, destroy, void,
191 private_duplicheck_listener_t *this)
192 {
193 enumerator_t *enumerator;
194 identification_t *key;
195 entry_t *value;
196
197 enumerator = this->active->create_enumerator(this->active);
198 while (enumerator->enumerate(enumerator, &key, &value))
199 {
200 entry_destroy(value);
201 }
202 enumerator->destroy(enumerator);
203
204 enumerator = this->checking->create_enumerator(this->checking);
205 while (enumerator->enumerate(enumerator, &key, &value))
206 {
207 entry_destroy(value);
208 }
209 enumerator->destroy(enumerator);
210
211 this->active->destroy(this->active);
212 this->checking->destroy(this->checking);
213 this->mutex->destroy(this->mutex);
214 free(this);
215 }
216
217 /**
218 * See header
219 */
220 duplicheck_listener_t *duplicheck_listener_create(duplicheck_notify_t *notify)
221 {
222 private_duplicheck_listener_t *this;
223
224 INIT(this,
225 .public = {
226 .listener = {
227 .ike_rekey = _ike_rekey,
228 .ike_updown = _ike_updown,
229 .message = _message_hook,
230 },
231 .destroy = _destroy,
232 },
233 .notify = notify,
234 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
235 .active = hashtable_create((hashtable_hash_t)hash,
236 (hashtable_equals_t)equals, 32),
237 .checking = hashtable_create((hashtable_hash_t)hash,
238 (hashtable_equals_t)equals, 2),
239 );
240
241 return &this->public;
242 }