ike-sa: Add option to force the destruction of an IKE_SA after initiating a delete
[strongswan.git] / src / libcharon / sa / shunt_manager.c
1 /*
2 * Copyright (C) 2015-2017 Tobias Brunner
3 * Copyright (C) 2011-2016 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
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 "shunt_manager.h"
18
19 #include <daemon.h>
20 #include <threading/rwlock.h>
21 #include <threading/rwlock_condvar.h>
22 #include <collections/linked_list.h>
23
24 #define INSTALL_DISABLED ((u_int)~0)
25
26 typedef struct private_shunt_manager_t private_shunt_manager_t;
27
28 /**
29 * Private data of an shunt_manager_t object.
30 */
31 struct private_shunt_manager_t {
32
33 /**
34 * Public shunt_manager_t interface.
35 */
36 shunt_manager_t public;
37
38 /**
39 * Installed shunts, as entry_t
40 */
41 linked_list_t *shunts;
42
43 /**
44 * Lock to safely access the list of shunts
45 */
46 rwlock_t *lock;
47
48 /**
49 * Number of threads currently installing shunts, or INSTALL_DISABLED
50 */
51 u_int installing;
52
53 /**
54 * Condvar to signal shunt installation
55 */
56 rwlock_condvar_t *condvar;
57 };
58
59 /**
60 * Config entry for a shunt
61 */
62 typedef struct {
63 /**
64 * Configured namespace
65 */
66 char *ns;
67
68 /**
69 * Child config
70 */
71 child_cfg_t *cfg;
72
73 } entry_t;
74
75 /**
76 * Destroy a config entry
77 */
78 static void entry_destroy(entry_t *this)
79 {
80 this->cfg->destroy(this->cfg);
81 free(this->ns);
82 free(this);
83 }
84
85 /**
86 * Install in and out shunt policies in the kernel
87 */
88 static bool install_shunt_policy(child_cfg_t *child)
89 {
90 enumerator_t *e_my_ts, *e_other_ts;
91 linked_list_t *my_ts_list, *other_ts_list, *hosts;
92 traffic_selector_t *my_ts, *other_ts;
93 host_t *host_any, *host_any6;
94 policy_type_t policy_type;
95 policy_priority_t policy_prio;
96 status_t status = SUCCESS;
97 uint32_t manual_prio;
98 char *interface;
99 bool fwd_out;
100 ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
101
102 switch (child->get_mode(child))
103 {
104 case MODE_PASS:
105 policy_type = POLICY_PASS;
106 policy_prio = POLICY_PRIORITY_PASS;
107 break;
108 case MODE_DROP:
109 policy_type = POLICY_DROP;
110 policy_prio = POLICY_PRIORITY_FALLBACK;
111 break;
112 default:
113 return FALSE;
114 }
115
116 host_any = host_create_any(AF_INET);
117 host_any6 = host_create_any(AF_INET6);
118
119 hosts = linked_list_create_with_items(host_any, host_any6, NULL);
120 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts);
121 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts);
122 hosts->destroy(hosts);
123
124 manual_prio = child->get_manual_prio(child);
125 interface = child->get_interface(child);
126 fwd_out = child->has_option(child, OPT_FWD_OUT_POLICIES);
127
128 /* enumerate pairs of traffic selectors */
129 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
130 while (e_my_ts->enumerate(e_my_ts, &my_ts))
131 {
132 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
133 while (e_other_ts->enumerate(e_other_ts, &other_ts))
134 {
135 if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
136 {
137 continue;
138 }
139 if (my_ts->get_protocol(my_ts) &&
140 other_ts->get_protocol(other_ts) &&
141 my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
142 {
143 continue;
144 }
145 /* install out policy */
146 kernel_ipsec_policy_id_t id = {
147 .dir = POLICY_OUT,
148 .src_ts = my_ts,
149 .dst_ts = other_ts,
150 .mark = child->get_mark(child, FALSE),
151 .interface = interface,
152 };
153 kernel_ipsec_manage_policy_t policy = {
154 .type = policy_type,
155 .prio = policy_prio,
156 .manual_prio = manual_prio,
157 .src = host_any,
158 .dst = host_any,
159 .sa = &sa,
160 };
161 status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
162 if (fwd_out)
163 { /* install "outbound" forward policy */
164 id.dir = POLICY_FWD;
165 status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
166 }
167 /* install in policy */
168 id = (kernel_ipsec_policy_id_t){
169 .dir = POLICY_IN,
170 .src_ts = other_ts,
171 .dst_ts = my_ts,
172 .mark = child->get_mark(child, TRUE),
173 .interface = interface,
174 };
175 status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
176 /* install "inbound" forward policy */
177 id.dir = POLICY_FWD;
178 status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
179 }
180 e_other_ts->destroy(e_other_ts);
181 }
182 e_my_ts->destroy(e_my_ts);
183
184 my_ts_list->destroy_offset(my_ts_list,
185 offsetof(traffic_selector_t, destroy));
186 other_ts_list->destroy_offset(other_ts_list,
187 offsetof(traffic_selector_t, destroy));
188 host_any6->destroy(host_any6);
189 host_any->destroy(host_any);
190
191 return status == SUCCESS;
192 }
193
194 METHOD(shunt_manager_t, install, bool,
195 private_shunt_manager_t *this, char *ns, child_cfg_t *cfg)
196 {
197 enumerator_t *enumerator;
198 entry_t *entry;
199 bool found = FALSE, success;
200
201 if (!ns)
202 {
203 DBG1(DBG_CFG, "missing namespace for shunt policy '%s'",
204 cfg->get_name(cfg));
205 return FALSE;
206 }
207
208 /* check if not already installed */
209 this->lock->write_lock(this->lock);
210 if (this->installing == INSTALL_DISABLED)
211 { /* flush() has been called */
212 this->lock->unlock(this->lock);
213 return FALSE;
214 }
215 enumerator = this->shunts->create_enumerator(this->shunts);
216 while (enumerator->enumerate(enumerator, &entry))
217 {
218 if (streq(ns, entry->ns) &&
219 streq(cfg->get_name(cfg), entry->cfg->get_name(entry->cfg)))
220 {
221 found = TRUE;
222 break;
223 }
224 }
225 enumerator->destroy(enumerator);
226 if (found)
227 {
228 DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
229 ipsec_mode_names, cfg->get_mode(cfg), cfg->get_name(cfg));
230 this->lock->unlock(this->lock);
231 return TRUE;
232 }
233 INIT(entry,
234 .ns = strdup(ns),
235 .cfg = cfg->get_ref(cfg),
236 );
237 this->shunts->insert_last(this->shunts, entry);
238 this->installing++;
239 this->lock->unlock(this->lock);
240
241 success = install_shunt_policy(cfg);
242
243 this->lock->write_lock(this->lock);
244 if (!success)
245 {
246 this->shunts->remove(this->shunts, entry, NULL);
247 entry_destroy(entry);
248 }
249 this->installing--;
250 this->condvar->signal(this->condvar);
251 this->lock->unlock(this->lock);
252 return success;
253 }
254
255 /**
256 * Uninstall in and out shunt policies in the kernel
257 */
258 static void uninstall_shunt_policy(child_cfg_t *child)
259 {
260 enumerator_t *e_my_ts, *e_other_ts;
261 linked_list_t *my_ts_list, *other_ts_list, *hosts;
262 traffic_selector_t *my_ts, *other_ts;
263 host_t *host_any, *host_any6;
264 policy_type_t policy_type;
265 policy_priority_t policy_prio;
266 status_t status = SUCCESS;
267 uint32_t manual_prio;
268 char *interface;
269 bool fwd_out;
270 ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
271
272 switch (child->get_mode(child))
273 {
274 case MODE_PASS:
275 policy_type = POLICY_PASS;
276 policy_prio = POLICY_PRIORITY_PASS;
277 break;
278 case MODE_DROP:
279 policy_type = POLICY_DROP;
280 policy_prio = POLICY_PRIORITY_FALLBACK;
281 break;
282 default:
283 return;
284 }
285
286 host_any = host_create_any(AF_INET);
287 host_any6 = host_create_any(AF_INET6);
288
289 hosts = linked_list_create_with_items(host_any, host_any6, NULL);
290 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts);
291 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts);
292 hosts->destroy(hosts);
293
294 manual_prio = child->get_manual_prio(child);
295 interface = child->get_interface(child);
296 fwd_out = child->has_option(child, OPT_FWD_OUT_POLICIES);
297
298 /* enumerate pairs of traffic selectors */
299 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
300 while (e_my_ts->enumerate(e_my_ts, &my_ts))
301 {
302 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
303 while (e_other_ts->enumerate(e_other_ts, &other_ts))
304 {
305 if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
306 {
307 continue;
308 }
309 if (my_ts->get_protocol(my_ts) &&
310 other_ts->get_protocol(other_ts) &&
311 my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
312 {
313 continue;
314 }
315 /* uninstall out policy */
316 kernel_ipsec_policy_id_t id = {
317 .dir = POLICY_OUT,
318 .src_ts = my_ts,
319 .dst_ts = other_ts,
320 .mark = child->get_mark(child, FALSE),
321 .interface = interface,
322 };
323 kernel_ipsec_manage_policy_t policy = {
324 .type = policy_type,
325 .prio = policy_prio,
326 .manual_prio = manual_prio,
327 .src = host_any,
328 .dst = host_any,
329 .sa = &sa,
330 };
331 status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
332 if (fwd_out)
333 {
334 /* uninstall "outbound" forward policy */
335 id.dir = POLICY_FWD;
336 status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
337 }
338 /* uninstall in policy */
339 id = (kernel_ipsec_policy_id_t){
340 .dir = POLICY_IN,
341 .src_ts = other_ts,
342 .dst_ts = my_ts,
343 .mark = child->get_mark(child, TRUE),
344 .interface = interface,
345 };
346 status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
347 /* uninstall "inbound" forward policy */
348 id.dir = POLICY_FWD;
349 status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
350 }
351 e_other_ts->destroy(e_other_ts);
352 }
353 e_my_ts->destroy(e_my_ts);
354
355 my_ts_list->destroy_offset(my_ts_list,
356 offsetof(traffic_selector_t, destroy));
357 other_ts_list->destroy_offset(other_ts_list,
358 offsetof(traffic_selector_t, destroy));
359 host_any6->destroy(host_any6);
360 host_any->destroy(host_any);
361
362 if (status != SUCCESS)
363 {
364 DBG1(DBG_CFG, "uninstalling shunt %N 'policy %s' failed",
365 ipsec_mode_names, child->get_mode(child), child->get_name(child));
366 }
367 }
368
369 METHOD(shunt_manager_t, uninstall, bool,
370 private_shunt_manager_t *this, char *ns, char *name)
371 {
372 enumerator_t *enumerator;
373 entry_t *entry, *found = NULL;
374
375 this->lock->write_lock(this->lock);
376 enumerator = this->shunts->create_enumerator(this->shunts);
377 while (enumerator->enumerate(enumerator, &entry))
378 {
379 if ((!ns || streq(ns, entry->ns)) &&
380 streq(name, entry->cfg->get_name(entry->cfg)))
381 {
382 this->shunts->remove_at(this->shunts, enumerator);
383 found = entry;
384 break;
385 }
386 }
387 enumerator->destroy(enumerator);
388 this->lock->unlock(this->lock);
389
390 if (!found)
391 {
392 return FALSE;
393 }
394 uninstall_shunt_policy(found->cfg);
395 entry_destroy(found);
396 return TRUE;
397 }
398
399 CALLBACK(filter_entries, bool,
400 void *unused, enumerator_t *orig, va_list args)
401 {
402 entry_t *entry;
403 child_cfg_t **cfg;
404 char **ns;
405
406 VA_ARGS_VGET(args, ns, cfg);
407
408 if (orig->enumerate(orig, &entry))
409 {
410 if (ns)
411 {
412 *ns = entry->ns;
413 }
414 *cfg = entry->cfg;
415 return TRUE;
416 }
417 return FALSE;
418 }
419
420 METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
421 private_shunt_manager_t *this)
422 {
423 this->lock->read_lock(this->lock);
424 return enumerator_create_filter(
425 this->shunts->create_enumerator(this->shunts),
426 filter_entries, this->lock,
427 (void*)this->lock->unlock);
428 }
429
430 METHOD(shunt_manager_t, flush, void,
431 private_shunt_manager_t *this)
432 {
433 entry_t *entry;
434
435 this->lock->write_lock(this->lock);
436 while (this->installing)
437 {
438 this->condvar->wait(this->condvar, this->lock);
439 }
440 while (this->shunts->remove_last(this->shunts, (void**)&entry) == SUCCESS)
441 {
442 uninstall_shunt_policy(entry->cfg);
443 entry_destroy(entry);
444 }
445 this->installing = INSTALL_DISABLED;
446 this->lock->unlock(this->lock);
447 }
448
449 METHOD(shunt_manager_t, destroy, void,
450 private_shunt_manager_t *this)
451 {
452 this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy));
453 this->lock->destroy(this->lock);
454 this->condvar->destroy(this->condvar);
455 free(this);
456 }
457
458 /**
459 * See header
460 */
461 shunt_manager_t *shunt_manager_create()
462 {
463 private_shunt_manager_t *this;
464
465 INIT(this,
466 .public = {
467 .install = _install,
468 .uninstall = _uninstall,
469 .create_enumerator = _create_enumerator,
470 .flush = _flush,
471 .destroy = _destroy,
472 },
473 .shunts = linked_list_create(),
474 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
475 .condvar = rwlock_condvar_create(),
476 );
477
478 return &this->public;
479 }