ike-sa-manager: Remove IKE_SA checkout by CHILD_SA reqid
[strongswan.git] / src / libcharon / plugins / unity / unity_handler.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2012 Martin Willi
6 * Copyright (C) 2012 revosec AG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * for more details.
17 */
18
19 #include "unity_handler.h"
20
21 #include <daemon.h>
22 #include <threading/mutex.h>
23 #include <collections/linked_list.h>
24 #include <processing/jobs/callback_job.h>
25
26 typedef struct private_unity_handler_t private_unity_handler_t;
27
28 /**
29 * Private data of an unity_handler_t object.
30 */
31 struct private_unity_handler_t {
32
33 /**
34 * Public unity_handler_t interface.
35 */
36 unity_handler_t public;
37
38 /**
39 * List of subnets to include, as entry_t
40 */
41 linked_list_t *include;
42
43 /**
44 * Mutex for concurrent access to lists
45 */
46 mutex_t *mutex;
47 };
48
49 /**
50 * Traffic selector entry for networks to include under a given IKE_SA
51 */
52 typedef struct {
53 /** associated IKE_SA, unique ID */
54 u_int32_t sa;
55 /** traffic selector to include/exclude */
56 traffic_selector_t *ts;
57 } entry_t;
58
59 /**
60 * Clean up an entry
61 */
62 static void entry_destroy(entry_t *this)
63 {
64 this->ts->destroy(this->ts);
65 free(this);
66 }
67
68 /**
69 * Create a traffic selector from a unity subnet definition
70 */
71 static traffic_selector_t *create_ts(chunk_t subnet)
72 {
73 chunk_t net, mask;
74 int i;
75
76 net = chunk_create(subnet.ptr, 4);
77 mask = chunk_clonea(chunk_create(subnet.ptr + 4, 4));
78 for (i = 0; i < net.len; i++)
79 {
80 mask.ptr[i] = (mask.ptr[i] ^ 0xFF) | net.ptr[i];
81 }
82 return traffic_selector_create_from_bytes(0, TS_IPV4_ADDR_RANGE,
83 net, 0, mask, 65535);
84 }
85
86 /**
87 * Parse a unity attribute and extract all subnets as traffic selectors
88 */
89 static linked_list_t *parse_subnets(chunk_t data)
90 {
91 linked_list_t *list = NULL;
92 traffic_selector_t *ts;
93
94 while (data.len >= 8)
95 { /* the padding is optional */
96 ts = create_ts(data);
97 if (ts)
98 {
99 if (!list)
100 {
101 list = linked_list_create();
102 }
103 list->insert_last(list, ts);
104 }
105 /* skip address, mask and 6 bytes of padding */
106 data = chunk_skip(data, 14);
107 }
108 return list;
109 }
110
111 /**
112 * Store a list of subnets to include in tunnels under this IKE_SA
113 */
114 static bool add_include(private_unity_handler_t *this, chunk_t data)
115 {
116 traffic_selector_t *ts;
117 linked_list_t *list;
118 ike_sa_t *ike_sa;
119 entry_t *entry;
120
121 ike_sa = charon->bus->get_sa(charon->bus);
122 if (!ike_sa)
123 {
124 return FALSE;
125 }
126 list = parse_subnets(data);
127 if (!list)
128 {
129 return FALSE;
130 }
131 while (list->remove_first(list, (void**)&ts) == SUCCESS)
132 {
133 INIT(entry,
134 .sa = ike_sa->get_unique_id(ike_sa),
135 .ts = ts,
136 );
137
138 this->mutex->lock(this->mutex);
139 this->include->insert_last(this->include, entry);
140 this->mutex->unlock(this->mutex);
141 }
142 list->destroy(list);
143 return TRUE;
144 }
145
146 /**
147 * Remove a list of subnets from the inclusion list for this IKE_SA
148 */
149 static bool remove_include(private_unity_handler_t *this, chunk_t data)
150 {
151 enumerator_t *enumerator;
152 traffic_selector_t *ts;
153 linked_list_t *list;
154 ike_sa_t *ike_sa;
155 entry_t *entry;
156
157 ike_sa = charon->bus->get_sa(charon->bus);
158 if (!ike_sa)
159 {
160 return FALSE;
161 }
162 list = parse_subnets(data);
163 if (!list)
164 {
165 return FALSE;
166 }
167
168 this->mutex->lock(this->mutex);
169 while (list->remove_first(list, (void**)&ts) == SUCCESS)
170 {
171 enumerator = this->include->create_enumerator(this->include);
172 while (enumerator->enumerate(enumerator, &entry))
173 {
174 if (entry->sa == ike_sa->get_unique_id(ike_sa) &&
175 ts->equals(ts, entry->ts))
176 {
177 this->include->remove_at(this->include, enumerator);
178 entry_destroy(entry);
179 break;
180 }
181 }
182 enumerator->destroy(enumerator);
183 ts->destroy(ts);
184 }
185 this->mutex->unlock(this->mutex);
186 list->destroy(list);
187 return TRUE;
188 }
189
190 /**
191 * Create a unique shunt name for a bypass policy
192 */
193 static void create_shunt_name(ike_sa_t *ike_sa, traffic_selector_t *ts,
194 char *buf, size_t len)
195 {
196 snprintf(buf, len, "Unity (%s[%u]: %R)", ike_sa->get_name(ike_sa),
197 ike_sa->get_unique_id(ike_sa), ts);
198 }
199
200 /**
201 * Install entry as a shunt policy
202 */
203 static job_requeue_t add_exclude_async(entry_t *entry)
204 {
205 enumerator_t *enumerator;
206 child_cfg_t *child_cfg;
207 lifetime_cfg_t lft = { .time = { .life = 0 } };
208 ike_sa_t *ike_sa;
209 char name[128];
210 host_t *host;
211
212 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
213 entry->sa);
214 if (ike_sa)
215 {
216 create_shunt_name(ike_sa, entry->ts, name, sizeof(name));
217
218 child_cfg = child_cfg_create(name, &lft, NULL, TRUE, MODE_PASS,
219 ACTION_NONE, ACTION_NONE, ACTION_NONE,
220 FALSE, 0, 0, NULL, NULL, FALSE);
221 child_cfg->add_traffic_selector(child_cfg, FALSE,
222 entry->ts->clone(entry->ts));
223 host = ike_sa->get_my_host(ike_sa);
224 child_cfg->add_traffic_selector(child_cfg, TRUE,
225 traffic_selector_create_from_subnet(host->clone(host),
226 32, 0, 0, 65535));
227 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
228 while (enumerator->enumerate(enumerator, &host))
229 {
230 child_cfg->add_traffic_selector(child_cfg, TRUE,
231 traffic_selector_create_from_subnet(host->clone(host),
232 32, 0, 0, 65535));
233 }
234 enumerator->destroy(enumerator);
235 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
236
237 charon->shunts->install(charon->shunts, child_cfg);
238 child_cfg->destroy(child_cfg);
239
240 DBG1(DBG_IKE, "installed %N bypass policy for %R",
241 configuration_attribute_type_names, UNITY_LOCAL_LAN, entry->ts);
242 }
243 return JOB_REQUEUE_NONE;
244 }
245
246 /**
247 * Add a bypass policy for a given subnet
248 */
249 static bool add_exclude(private_unity_handler_t *this, chunk_t data)
250 {
251 traffic_selector_t *ts;
252 linked_list_t *list;
253 ike_sa_t *ike_sa;
254 entry_t *entry;
255
256 ike_sa = charon->bus->get_sa(charon->bus);
257 if (!ike_sa)
258 {
259 return FALSE;
260 }
261 list = parse_subnets(data);
262 if (!list)
263 {
264 return FALSE;
265 }
266
267 while (list->remove_first(list, (void**)&ts) == SUCCESS)
268 {
269 INIT(entry,
270 .sa = ike_sa->get_unique_id(ike_sa),
271 .ts = ts,
272 );
273
274 /* we can't install the shunt policy yet, as we don't know the virtual IP.
275 * Defer installation using an async callback. */
276 lib->processor->queue_job(lib->processor, (job_t*)
277 callback_job_create((void*)add_exclude_async, entry,
278 (void*)entry_destroy, NULL));
279 }
280 list->destroy(list);
281 return TRUE;
282 }
283
284 /**
285 * Remove a bypass policy for a given subnet
286 */
287 static bool remove_exclude(private_unity_handler_t *this, chunk_t data)
288 {
289 traffic_selector_t *ts;
290 linked_list_t *list;
291 ike_sa_t *ike_sa;
292 char name[128];
293 bool success = TRUE;
294
295 ike_sa = charon->bus->get_sa(charon->bus);
296 if (!ike_sa)
297 {
298 return FALSE;
299 }
300 list = parse_subnets(data);
301 if (!list)
302 {
303 return FALSE;
304 }
305 while (list->remove_first(list, (void**)&ts) == SUCCESS)
306 {
307 create_shunt_name(ike_sa, ts, name, sizeof(name));
308 DBG1(DBG_IKE, "uninstalling %N bypass policy for %R",
309 configuration_attribute_type_names, UNITY_LOCAL_LAN, ts);
310 ts->destroy(ts);
311 success = charon->shunts->uninstall(charon->shunts, name) && success;
312 }
313 list->destroy(list);
314 return success;
315 }
316
317 METHOD(attribute_handler_t, handle, bool,
318 private_unity_handler_t *this, identification_t *id,
319 configuration_attribute_type_t type, chunk_t data)
320 {
321 switch (type)
322 {
323 case UNITY_SPLIT_INCLUDE:
324 return add_include(this, data);
325 case UNITY_LOCAL_LAN:
326 return add_exclude(this, data);
327 default:
328 return FALSE;
329 }
330 }
331
332 METHOD(attribute_handler_t, release, void,
333 private_unity_handler_t *this, identification_t *server,
334 configuration_attribute_type_t type, chunk_t data)
335 {
336 switch (type)
337 {
338 case UNITY_SPLIT_INCLUDE:
339 remove_include(this, data);
340 break;
341 case UNITY_LOCAL_LAN:
342 remove_exclude(this, data);
343 break;
344 default:
345 break;
346 }
347 }
348
349 /**
350 * Configuration attributes to request
351 */
352 static configuration_attribute_type_t attributes[] = {
353 UNITY_SPLIT_INCLUDE,
354 UNITY_LOCAL_LAN,
355 };
356
357 /**
358 * Attribute enumerator implementation
359 */
360 typedef struct {
361 /** implements enumerator_t */
362 enumerator_t public;
363 /** position in attributes[] */
364 int i;
365 } attribute_enumerator_t;
366
367 METHOD(enumerator_t, enumerate_attributes, bool,
368 attribute_enumerator_t *this, configuration_attribute_type_t *type,
369 chunk_t *data)
370 {
371 if (this->i < countof(attributes))
372 {
373 *type = attributes[this->i++];
374 *data = chunk_empty;
375 return TRUE;
376 }
377 return FALSE;
378 }
379
380 METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *,
381 unity_handler_t *this, identification_t *id, linked_list_t *vips)
382 {
383 attribute_enumerator_t *enumerator;
384 ike_sa_t *ike_sa;
385
386 ike_sa = charon->bus->get_sa(charon->bus);
387 if (!ike_sa || ike_sa->get_version(ike_sa) != IKEV1 ||
388 !ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
389 {
390 return enumerator_create_empty();
391 }
392 INIT(enumerator,
393 .public = {
394 .enumerate = (void*)_enumerate_attributes,
395 .destroy = (void*)free,
396 },
397 );
398 return &enumerator->public;
399 }
400
401 typedef struct {
402 /** mutex to unlock */
403 mutex_t *mutex;
404 /** IKE_SA ID to filter for */
405 u_int32_t id;
406 } include_filter_t;
407
408 /**
409 * Include enumerator filter function
410 */
411 static bool include_filter(include_filter_t *data,
412 entry_t **entry, traffic_selector_t **ts)
413 {
414 if ((*entry)->sa == data->id)
415 {
416 *ts = (*entry)->ts;
417 return TRUE;
418 }
419 return FALSE;
420 }
421
422 /**
423 * Destroy include filter data, unlock mutex
424 */
425 static void destroy_filter(include_filter_t *data)
426 {
427 data->mutex->unlock(data->mutex);
428 free(data);
429 }
430
431 METHOD(unity_handler_t, create_include_enumerator, enumerator_t*,
432 private_unity_handler_t *this, u_int32_t id)
433 {
434 include_filter_t *data;
435
436 INIT(data,
437 .mutex = this->mutex,
438 .id = id,
439 );
440 data->mutex->lock(data->mutex);
441 return enumerator_create_filter(
442 this->include->create_enumerator(this->include),
443 (void*)include_filter, data, (void*)destroy_filter);
444 }
445
446 METHOD(unity_handler_t, destroy, void,
447 private_unity_handler_t *this)
448 {
449 this->include->destroy(this->include);
450 this->mutex->destroy(this->mutex);
451 free(this);
452 }
453
454 /**
455 * See header
456 */
457 unity_handler_t *unity_handler_create()
458 {
459 private_unity_handler_t *this;
460
461 INIT(this,
462 .public = {
463 .handler = {
464 .handle = _handle,
465 .release = _release,
466 .create_attribute_enumerator = _create_attribute_enumerator,
467 },
468 .create_include_enumerator = _create_include_enumerator,
469 .destroy = _destroy,
470 },
471 .include = linked_list_create(),
472 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
473 );
474
475 return &this->public;
476 }