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