charon-nm: Set DPD/close action to restart and enable indefinite keying tries
[strongswan.git] / src / libcharon / plugins / unity / unity_handler.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * HSR 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 COOKIEs */
54 ike_sa_id_t *id;
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->id->destroy(this->id);
65 this->ts->destroy(this->ts);
66 free(this);
67 }
68
69 /**
70 * Create a traffic selector from a unity subnet definition
71 */
72 static traffic_selector_t *create_ts(chunk_t subnet)
73 {
74 chunk_t net, mask;
75 int i;
76
77 net = chunk_create(subnet.ptr, 4);
78 mask = chunk_clonea(chunk_create(subnet.ptr + 4, 4));
79 for (i = 0; i < net.len; i++)
80 {
81 mask.ptr[i] = (mask.ptr[i] ^ 0xFF) | net.ptr[i];
82 }
83 return traffic_selector_create_from_bytes(0, TS_IPV4_ADDR_RANGE,
84 net, 0, mask, 65535);
85 }
86
87 /**
88 * Parse a unity attribute and extract all subnets as traffic selectors
89 */
90 static linked_list_t *parse_subnets(chunk_t data)
91 {
92 linked_list_t *list = NULL;
93 traffic_selector_t *ts;
94
95 while (data.len >= 8)
96 { /* the padding is optional */
97 ts = create_ts(data);
98 if (ts)
99 {
100 if (!list)
101 {
102 list = linked_list_create();
103 }
104 list->insert_last(list, ts);
105 }
106 /* skip address, mask and 6 bytes of padding */
107 data = chunk_skip(data, 14);
108 }
109 return list;
110 }
111
112 /**
113 * Store a list of subnets to include in tunnels under this IKE_SA
114 */
115 static bool add_include(private_unity_handler_t *this, chunk_t data)
116 {
117 traffic_selector_t *ts;
118 linked_list_t *list;
119 ike_sa_t *ike_sa;
120 entry_t *entry;
121
122 ike_sa = charon->bus->get_sa(charon->bus);
123 if (!ike_sa)
124 {
125 return FALSE;
126 }
127 list = parse_subnets(data);
128 if (!list)
129 {
130 return FALSE;
131 }
132 while (list->remove_first(list, (void**)&ts) == SUCCESS)
133 {
134 INIT(entry,
135 .id = ike_sa->get_id(ike_sa),
136 .ts = ts,
137 );
138 entry->id = entry->id->clone(entry->id);
139
140 this->mutex->lock(this->mutex);
141 this->include->insert_last(this->include, entry);
142 this->mutex->unlock(this->mutex);
143 }
144 list->destroy(list);
145 return TRUE;
146 }
147
148 /**
149 * Remove a list of subnets from the inclusion list for this IKE_SA
150 */
151 static bool remove_include(private_unity_handler_t *this, chunk_t data)
152 {
153 enumerator_t *enumerator;
154 traffic_selector_t *ts;
155 linked_list_t *list;
156 ike_sa_t *ike_sa;
157 entry_t *entry;
158
159 ike_sa = charon->bus->get_sa(charon->bus);
160 if (!ike_sa)
161 {
162 return FALSE;
163 }
164 list = parse_subnets(data);
165 if (!list)
166 {
167 return FALSE;
168 }
169
170 this->mutex->lock(this->mutex);
171 while (list->remove_first(list, (void**)&ts) == SUCCESS)
172 {
173 enumerator = this->include->create_enumerator(this->include);
174 while (enumerator->enumerate(enumerator, &entry))
175 {
176 if (entry->id->equals(entry->id, ike_sa->get_id(ike_sa)) &&
177 ts->equals(ts, entry->ts))
178 {
179 this->include->remove_at(this->include, enumerator);
180 entry_destroy(entry);
181 break;
182 }
183 }
184 enumerator->destroy(enumerator);
185 ts->destroy(ts);
186 }
187 this->mutex->unlock(this->mutex);
188 list->destroy(list);
189 return TRUE;
190 }
191
192 /**
193 * Create a unique shunt name for a bypass policy
194 */
195 static void create_shunt_name(ike_sa_t *ike_sa, traffic_selector_t *ts,
196 char *buf, size_t len)
197 {
198 snprintf(buf, len, "Unity (%s[%u]: %R)", ike_sa->get_name(ike_sa),
199 ike_sa->get_unique_id(ike_sa), ts);
200 }
201
202 /**
203 * Install entry as a shunt policy
204 */
205 static job_requeue_t add_exclude_async(entry_t *entry)
206 {
207 enumerator_t *enumerator;
208 child_cfg_t *child_cfg;
209 child_cfg_create_t child = {
210 .mode = MODE_PASS,
211 };
212 ike_sa_t *ike_sa;
213 char name[128];
214 host_t *host;
215
216 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, entry->id);
217 if (ike_sa)
218 {
219 create_shunt_name(ike_sa, entry->ts, name, sizeof(name));
220
221 child_cfg = child_cfg_create(name, &child);
222 child_cfg->add_traffic_selector(child_cfg, FALSE,
223 entry->ts->clone(entry->ts));
224 host = ike_sa->get_my_host(ike_sa);
225 child_cfg->add_traffic_selector(child_cfg, TRUE,
226 traffic_selector_create_from_subnet(host->clone(host),
227 32, 0, 0, 65535));
228 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
229 while (enumerator->enumerate(enumerator, &host))
230 {
231 child_cfg->add_traffic_selector(child_cfg, TRUE,
232 traffic_selector_create_from_subnet(host->clone(host),
233 32, 0, 0, 65535));
234 }
235 enumerator->destroy(enumerator);
236 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
237
238 charon->shunts->install(charon->shunts, "unity", child_cfg);
239 child_cfg->destroy(child_cfg);
240
241 DBG1(DBG_IKE, "installed %N bypass policy for %R",
242 configuration_attribute_type_names, UNITY_LOCAL_LAN, entry->ts);
243 }
244 return JOB_REQUEUE_NONE;
245 }
246
247 /**
248 * Add a bypass policy for a given subnet
249 */
250 static bool add_exclude(private_unity_handler_t *this, chunk_t data)
251 {
252 traffic_selector_t *ts;
253 linked_list_t *list;
254 ike_sa_t *ike_sa;
255 entry_t *entry;
256
257 ike_sa = charon->bus->get_sa(charon->bus);
258 if (!ike_sa)
259 {
260 return FALSE;
261 }
262 list = parse_subnets(data);
263 if (!list)
264 {
265 return FALSE;
266 }
267
268 while (list->remove_first(list, (void**)&ts) == SUCCESS)
269 {
270 INIT(entry,
271 .id = ike_sa->get_id(ike_sa),
272 .ts = ts,
273 );
274 entry->id = entry->id->clone(entry->id);
275
276 /* we can't install the shunt policy yet, as we don't know the virtual IP.
277 * Defer installation using an async callback. */
278 lib->processor->queue_job(lib->processor, (job_t*)
279 callback_job_create((void*)add_exclude_async, entry,
280 (void*)entry_destroy, NULL));
281 }
282 list->destroy(list);
283 return TRUE;
284 }
285
286 /**
287 * Remove a bypass policy for a given subnet
288 */
289 static bool remove_exclude(private_unity_handler_t *this, chunk_t data)
290 {
291 traffic_selector_t *ts;
292 linked_list_t *list;
293 ike_sa_t *ike_sa;
294 char name[128];
295 bool success = TRUE;
296
297 ike_sa = charon->bus->get_sa(charon->bus);
298 if (!ike_sa)
299 {
300 return FALSE;
301 }
302 list = parse_subnets(data);
303 if (!list)
304 {
305 return FALSE;
306 }
307 while (list->remove_first(list, (void**)&ts) == SUCCESS)
308 {
309 create_shunt_name(ike_sa, ts, name, sizeof(name));
310 DBG1(DBG_IKE, "uninstalling %N bypass policy for %R",
311 configuration_attribute_type_names, UNITY_LOCAL_LAN, ts);
312 ts->destroy(ts);
313 success = charon->shunts->uninstall(charon->shunts, "unity",
314 name) && success;
315 }
316 list->destroy(list);
317 return success;
318 }
319
320 METHOD(attribute_handler_t, handle, bool,
321 private_unity_handler_t *this, ike_sa_t *ike_sa,
322 configuration_attribute_type_t type, chunk_t data)
323 {
324 switch (type)
325 {
326 case UNITY_SPLIT_INCLUDE:
327 return add_include(this, data);
328 case UNITY_LOCAL_LAN:
329 return add_exclude(this, data);
330 default:
331 return FALSE;
332 }
333 }
334
335 METHOD(attribute_handler_t, release, void,
336 private_unity_handler_t *this, ike_sa_t *ike_sa,
337 configuration_attribute_type_t type, chunk_t data)
338 {
339 switch (type)
340 {
341 case UNITY_SPLIT_INCLUDE:
342 remove_include(this, data);
343 break;
344 case UNITY_LOCAL_LAN:
345 remove_exclude(this, data);
346 break;
347 default:
348 break;
349 }
350 }
351
352 /**
353 * Configuration attributes to request
354 */
355 static configuration_attribute_type_t attributes[] = {
356 UNITY_SPLIT_INCLUDE,
357 UNITY_LOCAL_LAN,
358 };
359
360 /**
361 * Attribute enumerator implementation
362 */
363 typedef struct {
364 /** implements enumerator_t */
365 enumerator_t public;
366 /** position in attributes[] */
367 int i;
368 } attribute_enumerator_t;
369
370 METHOD(enumerator_t, enumerate_attributes, bool,
371 attribute_enumerator_t *this, va_list args)
372 {
373 configuration_attribute_type_t *type;
374 chunk_t *data;
375
376 VA_ARGS_VGET(args, type, data);
377 if (this->i < countof(attributes))
378 {
379 *type = attributes[this->i++];
380 *data = chunk_empty;
381 return TRUE;
382 }
383 return FALSE;
384 }
385
386 METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *,
387 unity_handler_t *this, ike_sa_t *ike_sa, linked_list_t *vips)
388 {
389 attribute_enumerator_t *enumerator;
390
391 ike_sa = charon->bus->get_sa(charon->bus);
392 if (!ike_sa || ike_sa->get_version(ike_sa) != IKEV1 ||
393 !ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
394 {
395 return enumerator_create_empty();
396 }
397 INIT(enumerator,
398 .public = {
399 .enumerate = enumerator_enumerate_default,
400 .venumerate = _enumerate_attributes,
401 .destroy = (void*)free,
402 },
403 );
404 return &enumerator->public;
405 }
406
407 typedef struct {
408 /** mutex to unlock */
409 mutex_t *mutex;
410 /** IKE_SA ID to filter for */
411 ike_sa_id_t *id;
412 } include_filter_t;
413
414 CALLBACK(include_filter, bool,
415 include_filter_t *data, enumerator_t *orig, va_list args)
416 {
417 entry_t *entry;
418 traffic_selector_t **ts;
419
420 VA_ARGS_VGET(args, ts);
421
422 while (orig->enumerate(orig, &entry))
423 {
424 if (data->id->equals(data->id, entry->id))
425 {
426 *ts = entry->ts;
427 return TRUE;
428 }
429 }
430 return FALSE;
431 }
432
433 CALLBACK(destroy_filter, void,
434 include_filter_t *data)
435 {
436 data->mutex->unlock(data->mutex);
437 free(data);
438 }
439
440 METHOD(unity_handler_t, create_include_enumerator, enumerator_t*,
441 private_unity_handler_t *this, ike_sa_id_t *id)
442 {
443 include_filter_t *data;
444
445 INIT(data,
446 .mutex = this->mutex,
447 .id = id,
448 );
449 data->mutex->lock(data->mutex);
450 return enumerator_create_filter(
451 this->include->create_enumerator(this->include),
452 include_filter, data, destroy_filter);
453 }
454
455 METHOD(unity_handler_t, destroy, void,
456 private_unity_handler_t *this)
457 {
458 this->include->destroy(this->include);
459 this->mutex->destroy(this->mutex);
460 free(this);
461 }
462
463 /**
464 * See header
465 */
466 unity_handler_t *unity_handler_create()
467 {
468 private_unity_handler_t *this;
469
470 INIT(this,
471 .public = {
472 .handler = {
473 .handle = _handle,
474 .release = _release,
475 .create_attribute_enumerator = _create_attribute_enumerator,
476 },
477 .create_include_enumerator = _create_include_enumerator,
478 .destroy = _destroy,
479 },
480 .include = linked_list_create(),
481 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
482 );
483
484 return &this->public;
485 }