Merge branch 'android-fixes'
[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 FALSE);
122 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts,
123 FALSE);
124 hosts->destroy(hosts);
125
126 manual_prio = child->get_manual_prio(child);
127 interface = child->get_interface(child);
128 fwd_out = child->has_option(child, OPT_FWD_OUT_POLICIES);
129
130 /* enumerate pairs of traffic selectors */
131 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
132 while (e_my_ts->enumerate(e_my_ts, &my_ts))
133 {
134 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
135 while (e_other_ts->enumerate(e_other_ts, &other_ts))
136 {
137 if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
138 {
139 continue;
140 }
141 if (my_ts->get_protocol(my_ts) &&
142 other_ts->get_protocol(other_ts) &&
143 my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
144 {
145 continue;
146 }
147 /* install out policy */
148 kernel_ipsec_policy_id_t id = {
149 .dir = POLICY_OUT,
150 .src_ts = my_ts,
151 .dst_ts = other_ts,
152 .mark = child->get_mark(child, FALSE),
153 .interface = interface,
154 };
155 kernel_ipsec_manage_policy_t policy = {
156 .type = policy_type,
157 .prio = policy_prio,
158 .manual_prio = manual_prio,
159 .src = host_any,
160 .dst = host_any,
161 .sa = &sa,
162 };
163 status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
164 if (fwd_out)
165 { /* install "outbound" forward policy */
166 id.dir = POLICY_FWD;
167 status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
168 }
169 /* install in policy */
170 id = (kernel_ipsec_policy_id_t){
171 .dir = POLICY_IN,
172 .src_ts = other_ts,
173 .dst_ts = my_ts,
174 .mark = child->get_mark(child, TRUE),
175 .interface = interface,
176 };
177 status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
178 /* install "inbound" forward policy */
179 id.dir = POLICY_FWD;
180 status |= charon->kernel->add_policy(charon->kernel, &id, &policy);
181 }
182 e_other_ts->destroy(e_other_ts);
183 }
184 e_my_ts->destroy(e_my_ts);
185
186 my_ts_list->destroy_offset(my_ts_list,
187 offsetof(traffic_selector_t, destroy));
188 other_ts_list->destroy_offset(other_ts_list,
189 offsetof(traffic_selector_t, destroy));
190 host_any6->destroy(host_any6);
191 host_any->destroy(host_any);
192
193 return status == SUCCESS;
194 }
195
196 METHOD(shunt_manager_t, install, bool,
197 private_shunt_manager_t *this, char *ns, child_cfg_t *cfg)
198 {
199 enumerator_t *enumerator;
200 entry_t *entry;
201 bool found = FALSE, success;
202
203 if (!ns)
204 {
205 DBG1(DBG_CFG, "missing namespace for shunt policy '%s'",
206 cfg->get_name(cfg));
207 return FALSE;
208 }
209
210 /* check if not already installed */
211 this->lock->write_lock(this->lock);
212 if (this->installing == INSTALL_DISABLED)
213 { /* flush() has been called */
214 this->lock->unlock(this->lock);
215 return FALSE;
216 }
217 enumerator = this->shunts->create_enumerator(this->shunts);
218 while (enumerator->enumerate(enumerator, &entry))
219 {
220 if (streq(ns, entry->ns) &&
221 streq(cfg->get_name(cfg), entry->cfg->get_name(entry->cfg)))
222 {
223 found = TRUE;
224 break;
225 }
226 }
227 enumerator->destroy(enumerator);
228 if (found)
229 {
230 DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
231 ipsec_mode_names, cfg->get_mode(cfg), cfg->get_name(cfg));
232 this->lock->unlock(this->lock);
233 return TRUE;
234 }
235 INIT(entry,
236 .ns = strdup(ns),
237 .cfg = cfg->get_ref(cfg),
238 );
239 this->shunts->insert_last(this->shunts, entry);
240 this->installing++;
241 this->lock->unlock(this->lock);
242
243 success = install_shunt_policy(cfg);
244
245 this->lock->write_lock(this->lock);
246 if (!success)
247 {
248 this->shunts->remove(this->shunts, entry, NULL);
249 entry_destroy(entry);
250 }
251 this->installing--;
252 this->condvar->signal(this->condvar);
253 this->lock->unlock(this->lock);
254 return success;
255 }
256
257 /**
258 * Uninstall in and out shunt policies in the kernel
259 */
260 static void uninstall_shunt_policy(child_cfg_t *child)
261 {
262 enumerator_t *e_my_ts, *e_other_ts;
263 linked_list_t *my_ts_list, *other_ts_list, *hosts;
264 traffic_selector_t *my_ts, *other_ts;
265 host_t *host_any, *host_any6;
266 policy_type_t policy_type;
267 policy_priority_t policy_prio;
268 status_t status = SUCCESS;
269 uint32_t manual_prio;
270 char *interface;
271 bool fwd_out;
272 ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
273
274 switch (child->get_mode(child))
275 {
276 case MODE_PASS:
277 policy_type = POLICY_PASS;
278 policy_prio = POLICY_PRIORITY_PASS;
279 break;
280 case MODE_DROP:
281 policy_type = POLICY_DROP;
282 policy_prio = POLICY_PRIORITY_FALLBACK;
283 break;
284 default:
285 return;
286 }
287
288 host_any = host_create_any(AF_INET);
289 host_any6 = host_create_any(AF_INET6);
290
291 hosts = linked_list_create_with_items(host_any, host_any6, NULL);
292 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts,
293 FALSE);
294 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts,
295 FALSE);
296 hosts->destroy(hosts);
297
298 manual_prio = child->get_manual_prio(child);
299 interface = child->get_interface(child);
300 fwd_out = child->has_option(child, OPT_FWD_OUT_POLICIES);
301
302 /* enumerate pairs of traffic selectors */
303 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
304 while (e_my_ts->enumerate(e_my_ts, &my_ts))
305 {
306 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
307 while (e_other_ts->enumerate(e_other_ts, &other_ts))
308 {
309 if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
310 {
311 continue;
312 }
313 if (my_ts->get_protocol(my_ts) &&
314 other_ts->get_protocol(other_ts) &&
315 my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
316 {
317 continue;
318 }
319 /* uninstall out policy */
320 kernel_ipsec_policy_id_t id = {
321 .dir = POLICY_OUT,
322 .src_ts = my_ts,
323 .dst_ts = other_ts,
324 .mark = child->get_mark(child, FALSE),
325 .interface = interface,
326 };
327 kernel_ipsec_manage_policy_t policy = {
328 .type = policy_type,
329 .prio = policy_prio,
330 .manual_prio = manual_prio,
331 .src = host_any,
332 .dst = host_any,
333 .sa = &sa,
334 };
335 status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
336 if (fwd_out)
337 {
338 /* uninstall "outbound" forward policy */
339 id.dir = POLICY_FWD;
340 status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
341 }
342 /* uninstall in policy */
343 id = (kernel_ipsec_policy_id_t){
344 .dir = POLICY_IN,
345 .src_ts = other_ts,
346 .dst_ts = my_ts,
347 .mark = child->get_mark(child, TRUE),
348 .interface = interface,
349 };
350 status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
351 /* uninstall "inbound" forward policy */
352 id.dir = POLICY_FWD;
353 status |= charon->kernel->del_policy(charon->kernel, &id, &policy);
354 }
355 e_other_ts->destroy(e_other_ts);
356 }
357 e_my_ts->destroy(e_my_ts);
358
359 my_ts_list->destroy_offset(my_ts_list,
360 offsetof(traffic_selector_t, destroy));
361 other_ts_list->destroy_offset(other_ts_list,
362 offsetof(traffic_selector_t, destroy));
363 host_any6->destroy(host_any6);
364 host_any->destroy(host_any);
365
366 if (status != SUCCESS)
367 {
368 DBG1(DBG_CFG, "uninstalling shunt %N 'policy %s' failed",
369 ipsec_mode_names, child->get_mode(child), child->get_name(child));
370 }
371 }
372
373 METHOD(shunt_manager_t, uninstall, bool,
374 private_shunt_manager_t *this, char *ns, char *name)
375 {
376 enumerator_t *enumerator;
377 entry_t *entry, *found = NULL;
378
379 this->lock->write_lock(this->lock);
380 enumerator = this->shunts->create_enumerator(this->shunts);
381 while (enumerator->enumerate(enumerator, &entry))
382 {
383 if ((!ns || streq(ns, entry->ns)) &&
384 streq(name, entry->cfg->get_name(entry->cfg)))
385 {
386 this->shunts->remove_at(this->shunts, enumerator);
387 found = entry;
388 break;
389 }
390 }
391 enumerator->destroy(enumerator);
392 this->lock->unlock(this->lock);
393
394 if (!found)
395 {
396 return FALSE;
397 }
398 uninstall_shunt_policy(found->cfg);
399 entry_destroy(found);
400 return TRUE;
401 }
402
403 CALLBACK(filter_entries, bool,
404 void *unused, enumerator_t *orig, va_list args)
405 {
406 entry_t *entry;
407 child_cfg_t **cfg;
408 char **ns;
409
410 VA_ARGS_VGET(args, ns, cfg);
411
412 if (orig->enumerate(orig, &entry))
413 {
414 if (ns)
415 {
416 *ns = entry->ns;
417 }
418 *cfg = entry->cfg;
419 return TRUE;
420 }
421 return FALSE;
422 }
423
424 METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
425 private_shunt_manager_t *this)
426 {
427 this->lock->read_lock(this->lock);
428 return enumerator_create_filter(
429 this->shunts->create_enumerator(this->shunts),
430 filter_entries, this->lock,
431 (void*)this->lock->unlock);
432 }
433
434 METHOD(shunt_manager_t, flush, void,
435 private_shunt_manager_t *this)
436 {
437 entry_t *entry;
438
439 this->lock->write_lock(this->lock);
440 while (this->installing)
441 {
442 this->condvar->wait(this->condvar, this->lock);
443 }
444 while (this->shunts->remove_last(this->shunts, (void**)&entry) == SUCCESS)
445 {
446 uninstall_shunt_policy(entry->cfg);
447 entry_destroy(entry);
448 }
449 this->installing = INSTALL_DISABLED;
450 this->lock->unlock(this->lock);
451 }
452
453 METHOD(shunt_manager_t, destroy, void,
454 private_shunt_manager_t *this)
455 {
456 this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy));
457 this->lock->destroy(this->lock);
458 this->condvar->destroy(this->condvar);
459 free(this);
460 }
461
462 /**
463 * See header
464 */
465 shunt_manager_t *shunt_manager_create()
466 {
467 private_shunt_manager_t *this;
468
469 INIT(this,
470 .public = {
471 .install = _install,
472 .uninstall = _uninstall,
473 .create_enumerator = _create_enumerator,
474 .flush = _flush,
475 .destroy = _destroy,
476 },
477 .shunts = linked_list_create(),
478 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
479 .condvar = rwlock_condvar_create(),
480 );
481
482 return &this->public;
483 }