child-sa-manager: Add a global manager storing CHILD_SA relations
[strongswan.git] / src / libcharon / sa / child_sa_manager.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 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 "child_sa_manager.h"
17
18 #include <daemon.h>
19 #include <threading/mutex.h>
20 #include <collections/hashtable.h>
21
22 typedef struct private_child_sa_manager_t private_child_sa_manager_t;
23
24 /**
25 * Private data of an child_sa_manager_t object.
26 */
27 struct private_child_sa_manager_t {
28
29 /**
30 * Public child_sa_manager_t interface.
31 */
32 child_sa_manager_t public;
33
34 /**
35 * CHILD_SAs by inbound SPI/dst, child_entry_t => child_entry_t
36 */
37 hashtable_t *in;
38
39 /**
40 * CHILD_SAs by outbound SPI/dst, child_entry_t => child_entry_t
41 */
42 hashtable_t *out;
43
44 /**
45 * CHILD_SAs by unique ID, child_entry_t => child_entry_t
46 */
47 hashtable_t *ids;
48
49 /**
50 * Mutex to access any hashtable
51 */
52 mutex_t *mutex;
53 };
54
55 /**
56 * Hashtable entry for a known CHILD_SA
57 */
58 typedef struct {
59 /** the associated IKE_SA */
60 ike_sa_id_t *ike_id;
61 /** unique CHILD_SA identifier */
62 u_int32_t unique_id;
63 /** inbound SPI */
64 u_int32_t spi_in;
65 /** outbound SPI */
66 u_int32_t spi_out;
67 /** inbound host address */
68 host_t *host_in;
69 /** outbound host address and port */
70 host_t *host_out;
71 /** IPsec protocol, AH|ESP */
72 protocol_id_t proto;
73 } child_entry_t;
74
75 /**
76 * Destroy a CHILD_SA entry
77 */
78 static void child_entry_destroy(child_entry_t *entry)
79 {
80 entry->ike_id->destroy(entry->ike_id);
81 entry->host_in->destroy(entry->host_in);
82 entry->host_out->destroy(entry->host_out);
83 free(entry);
84 }
85
86 /**
87 * Hashtable hash function for inbound SAs
88 */
89 static u_int hash_in(child_entry_t *entry)
90 {
91 return chunk_hash_inc(chunk_from_thing(entry->spi_in),
92 chunk_hash_inc(entry->host_in->get_address(entry->host_in),
93 chunk_hash(chunk_from_thing(entry->proto))));
94 }
95
96 /**
97 * Hashtable equals function for inbound SAs
98 */
99 static bool equals_in(child_entry_t *a, child_entry_t *b)
100 {
101 return a->spi_in == b->spi_in &&
102 a->proto == b->proto &&
103 a->host_in->ip_equals(a->host_in, b->host_in);
104 }
105
106 /**
107 * Hashtable hash function for outbound SAs
108 */
109 static u_int hash_out(child_entry_t *entry)
110 {
111 return chunk_hash_inc(chunk_from_thing(entry->spi_out),
112 chunk_hash_inc(entry->host_out->get_address(entry->host_out),
113 chunk_hash(chunk_from_thing(entry->proto))));
114 }
115
116 /**
117 * Hashtable equals function for outbound SAs
118 */
119 static bool equals_out(child_entry_t *a, child_entry_t *b)
120 {
121 return a->spi_out == b->spi_out &&
122 a->proto == b->proto &&
123 a->host_out->ip_equals(a->host_out, b->host_out);
124 }
125
126 /**
127 * Hashtable hash function for SAs by unique ID
128 */
129 static u_int hash_id(child_entry_t *entry)
130 {
131 return chunk_hash(chunk_from_thing(entry->unique_id));
132 }
133
134 /**
135 * Hashtable equals function for SAs by unique ID
136 */
137 static bool equals_id(child_entry_t *a, child_entry_t *b)
138 {
139 return a->unique_id == b->unique_id;
140 }
141
142 METHOD(child_sa_manager_t, add, void,
143 private_child_sa_manager_t *this, child_sa_t *child_sa, ike_sa_t *ike_sa)
144 {
145 child_entry_t *entry;
146 host_t *in, *out;
147 ike_sa_id_t *id;
148
149 id = ike_sa->get_id(ike_sa);
150 in = ike_sa->get_my_host(ike_sa);
151 out = ike_sa->get_other_host(ike_sa);
152
153 INIT(entry,
154 .ike_id = id->clone(id),
155 .unique_id = child_sa->get_unique_id(child_sa),
156 .proto = child_sa->get_protocol(child_sa),
157 .spi_in = child_sa->get_spi(child_sa, TRUE),
158 .spi_out = child_sa->get_spi(child_sa, FALSE),
159 .host_in = in->clone(in),
160 .host_out = out->clone(out),
161 );
162
163 this->mutex->lock(this->mutex);
164 if (!this->in->get(this->in, entry) &&
165 !this->out->get(this->out, entry))
166 {
167 this->in->put(this->in, entry, entry);
168 this->out->put(this->out, entry, entry);
169 entry = this->ids->put(this->ids, entry, entry);
170 }
171 this->mutex->unlock(this->mutex);
172
173 if (entry)
174 {
175 child_entry_destroy(entry);
176 }
177 }
178
179 METHOD(child_sa_manager_t, remove_, void,
180 private_child_sa_manager_t *this, child_sa_t *child_sa)
181 {
182 child_entry_t *entry, key = {
183 .unique_id = child_sa->get_unique_id(child_sa),
184 };
185
186 this->mutex->lock(this->mutex);
187 entry = this->ids->remove(this->ids, &key);
188 if (entry)
189 {
190 this->in->remove(this->in, entry);
191 this->out->remove(this->out, entry);
192 }
193 this->mutex->unlock(this->mutex);
194
195 if (entry)
196 {
197 child_entry_destroy(entry);
198 }
199 }
200
201 /**
202 * Check out an IKE_SA for a given CHILD_SA
203 */
204 static ike_sa_t *checkout_ikesa(private_child_sa_manager_t *this,
205 ike_sa_id_t *id, u_int32_t unique_id, child_sa_t **child_sa)
206 {
207 enumerator_t *enumerator;
208 child_sa_t *current;
209 ike_sa_t *ike_sa;
210 bool found = FALSE;
211
212 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
213 id->destroy(id);
214 if (ike_sa)
215 {
216 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
217 while (enumerator->enumerate(enumerator, &current))
218 {
219 found = current->get_unique_id(current) == unique_id;
220 if (found)
221 {
222 if (child_sa)
223 {
224 *child_sa = current;
225 }
226 break;
227 }
228 }
229 enumerator->destroy(enumerator);
230
231 if (found)
232 {
233 return ike_sa;
234 }
235 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
236 }
237 return NULL;
238 }
239
240 METHOD(child_sa_manager_t, checkout_by_id, ike_sa_t*,
241 private_child_sa_manager_t *this, u_int32_t unique_id,
242 child_sa_t **child_sa)
243 {
244 ike_sa_id_t *id;
245 child_entry_t *entry, key = {
246 .unique_id = unique_id,
247 };
248
249 this->mutex->lock(this->mutex);
250 entry = this->ids->get(this->ids, &key);
251 if (entry)
252 {
253 id = entry->ike_id->clone(entry->ike_id);
254 }
255 this->mutex->unlock(this->mutex);
256
257 if (entry)
258 {
259 return checkout_ikesa(this, id, unique_id, child_sa);
260 }
261 return NULL;
262 }
263
264 METHOD(child_sa_manager_t, checkout, ike_sa_t*,
265 private_child_sa_manager_t *this, protocol_id_t protocol, u_int32_t spi,
266 host_t *dst, child_sa_t **child_sa)
267 {
268 ike_sa_id_t *id;
269 u_int32_t unique_id;
270 child_entry_t *entry, key = {
271 .spi_in = spi,
272 .spi_out = spi,
273 .host_in = dst,
274 .host_out = dst,
275 .proto = protocol,
276 };
277
278 this->mutex->lock(this->mutex);
279 entry = this->in->get(this->in, &key);
280 if (!entry)
281 {
282 entry = this->out->get(this->out, &key);
283 }
284 if (entry)
285 {
286 unique_id = entry->unique_id;
287 id = entry->ike_id->clone(entry->ike_id);
288 }
289 this->mutex->unlock(this->mutex);
290
291 if (entry)
292 {
293 return checkout_ikesa(this, id, unique_id, child_sa);
294 }
295 return NULL;
296 }
297
298 METHOD(child_sa_manager_t, destroy, void,
299 private_child_sa_manager_t *this)
300 {
301 this->in->destroy(this->in);
302 this->out->destroy(this->out);
303 this->ids->destroy(this->ids);
304 this->mutex->destroy(this->mutex);
305 free(this);
306 }
307
308 /**
309 * See header
310 */
311 child_sa_manager_t *child_sa_manager_create()
312 {
313 private_child_sa_manager_t *this;
314
315 INIT(this,
316 .public = {
317 .add = _add,
318 .remove = _remove_,
319 .checkout = _checkout,
320 .checkout_by_id = _checkout_by_id,
321 .destroy = _destroy,
322 },
323 .in = hashtable_create((hashtable_hash_t)hash_in,
324 (hashtable_equals_t)equals_in, 8),
325 .out = hashtable_create((hashtable_hash_t)hash_out,
326 (hashtable_equals_t)equals_out, 8),
327 .ids = hashtable_create((hashtable_hash_t)hash_id,
328 (hashtable_equals_t)equals_id, 8),
329 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
330 );
331
332 return &this->public;
333 }