5231994c830c02d89256eb6ac7b0401ed94d631b
[strongswan.git] / src / libcharon / sa / shunt_manager.c
1 /*
2 * Copyright (C) 2015 Tobias Brunner
3 * Copyright (C) 2011 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 <hydra.h>
20 #include <daemon.h>
21 #include <threading/rwlock.h>
22 #include <threading/rwlock_condvar.h>
23 #include <collections/linked_list.h>
24
25 #define INSTALL_DISABLED ((u_int)~0)
26
27 typedef struct private_shunt_manager_t private_shunt_manager_t;
28
29 /**
30 * Private data of an shunt_manager_t object.
31 */
32 struct private_shunt_manager_t {
33
34 /**
35 * Public shunt_manager_t interface.
36 */
37 shunt_manager_t public;
38
39 /**
40 * Installed shunts, as child_cfg_t
41 */
42 linked_list_t *shunts;
43
44 /**
45 * Lock to safely access the list of shunts
46 */
47 rwlock_t *lock;
48
49 /**
50 * Number of threads currently installing shunts, or INSTALL_DISABLED
51 */
52 u_int installing;
53
54 /**
55 * Condvar to signal shunt installation
56 */
57 rwlock_condvar_t *condvar;
58 };
59
60 /**
61 * Install in and out shunt policies in the kernel
62 */
63 static bool install_shunt_policy(child_cfg_t *child)
64 {
65 enumerator_t *e_my_ts, *e_other_ts;
66 linked_list_t *my_ts_list, *other_ts_list, *hosts;
67 traffic_selector_t *my_ts, *other_ts;
68 host_t *host_any, *host_any6;
69 policy_type_t policy_type;
70 policy_priority_t policy_prio;
71 status_t status = SUCCESS;
72 ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
73
74 switch (child->get_mode(child))
75 {
76 case MODE_PASS:
77 policy_type = POLICY_PASS;
78 policy_prio = POLICY_PRIORITY_PASS;
79 break;
80 case MODE_DROP:
81 policy_type = POLICY_DROP;
82 policy_prio = POLICY_PRIORITY_FALLBACK;
83 break;
84 default:
85 return FALSE;
86 }
87
88 host_any = host_create_any(AF_INET);
89 host_any6 = host_create_any(AF_INET6);
90
91 hosts = linked_list_create_with_items(host_any, host_any6, NULL);
92 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts);
93 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts);
94 hosts->destroy(hosts);
95
96 /* enumerate pairs of traffic selectors */
97 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
98 while (e_my_ts->enumerate(e_my_ts, &my_ts))
99 {
100 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
101 while (e_other_ts->enumerate(e_other_ts, &other_ts))
102 {
103 if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
104 {
105 continue;
106 }
107 if (my_ts->get_protocol(my_ts) &&
108 other_ts->get_protocol(other_ts) &&
109 my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
110 {
111 continue;
112 }
113 /* install out policy */
114 status |= hydra->kernel_interface->add_policy(
115 hydra->kernel_interface, host_any, host_any,
116 my_ts, other_ts, POLICY_OUT, policy_type,
117 &sa, child->get_mark(child, FALSE),
118 policy_prio);
119
120 /* install in policy */
121 status |= hydra->kernel_interface->add_policy(
122 hydra->kernel_interface, host_any, host_any,
123 other_ts, my_ts, POLICY_IN, policy_type,
124 &sa, child->get_mark(child, TRUE),
125 policy_prio);
126
127 /* install forward policy */
128 status |= hydra->kernel_interface->add_policy(
129 hydra->kernel_interface, host_any, host_any,
130 other_ts, my_ts, POLICY_FWD, policy_type,
131 &sa, child->get_mark(child, TRUE),
132 policy_prio);
133 }
134 e_other_ts->destroy(e_other_ts);
135 }
136 e_my_ts->destroy(e_my_ts);
137
138 my_ts_list->destroy_offset(my_ts_list,
139 offsetof(traffic_selector_t, destroy));
140 other_ts_list->destroy_offset(other_ts_list,
141 offsetof(traffic_selector_t, destroy));
142 host_any6->destroy(host_any6);
143 host_any->destroy(host_any);
144
145 return status == SUCCESS;
146 }
147
148 METHOD(shunt_manager_t, install, bool,
149 private_shunt_manager_t *this, child_cfg_t *child)
150 {
151 enumerator_t *enumerator;
152 child_cfg_t *child_cfg;
153 bool found = FALSE, success;
154
155 /* check if not already installed */
156 this->lock->write_lock(this->lock);
157 if (this->installing == INSTALL_DISABLED)
158 { /* flush() has been called */
159 this->lock->unlock(this->lock);
160 return FALSE;
161 }
162 enumerator = this->shunts->create_enumerator(this->shunts);
163 while (enumerator->enumerate(enumerator, &child_cfg))
164 {
165 if (streq(child_cfg->get_name(child_cfg), child->get_name(child)))
166 {
167 found = TRUE;
168 break;
169 }
170 }
171 enumerator->destroy(enumerator);
172 if (found)
173 {
174 DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
175 ipsec_mode_names, child->get_mode(child), child->get_name(child));
176 this->lock->unlock(this->lock);
177 return TRUE;
178 }
179 this->shunts->insert_last(this->shunts, child->get_ref(child));
180 this->installing++;
181 this->lock->unlock(this->lock);
182
183 success = install_shunt_policy(child);
184
185 this->lock->write_lock(this->lock);
186 if (!success)
187 {
188 this->shunts->remove(this->shunts, child, NULL);
189 child->destroy(child);
190 }
191 this->installing--;
192 this->condvar->signal(this->condvar);
193 this->lock->unlock(this->lock);
194 return success;
195 }
196
197 /**
198 * Uninstall in and out shunt policies in the kernel
199 */
200 static void uninstall_shunt_policy(child_cfg_t *child)
201 {
202 enumerator_t *e_my_ts, *e_other_ts;
203 linked_list_t *my_ts_list, *other_ts_list, *hosts;
204 traffic_selector_t *my_ts, *other_ts;
205 host_t *host_any, *host_any6;
206 policy_type_t policy_type;
207 policy_priority_t policy_prio;
208 status_t status = SUCCESS;
209 ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
210
211 switch (child->get_mode(child))
212 {
213 case MODE_PASS:
214 policy_type = POLICY_PASS;
215 policy_prio = POLICY_PRIORITY_PASS;
216 break;
217 case MODE_DROP:
218 policy_type = POLICY_DROP;
219 policy_prio = POLICY_PRIORITY_FALLBACK;
220 break;
221 default:
222 return;
223 }
224
225 host_any = host_create_any(AF_INET);
226 host_any6 = host_create_any(AF_INET6);
227
228 hosts = linked_list_create_with_items(host_any, host_any6, NULL);
229 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts);
230 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts);
231 hosts->destroy(hosts);
232
233 /* enumerate pairs of traffic selectors */
234 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
235 while (e_my_ts->enumerate(e_my_ts, &my_ts))
236 {
237 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
238 while (e_other_ts->enumerate(e_other_ts, &other_ts))
239 {
240 if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
241 {
242 continue;
243 }
244 if (my_ts->get_protocol(my_ts) &&
245 other_ts->get_protocol(other_ts) &&
246 my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
247 {
248 continue;
249 }
250 /* uninstall out policy */
251 status |= hydra->kernel_interface->del_policy(
252 hydra->kernel_interface, host_any, host_any,
253 my_ts, other_ts, POLICY_OUT, policy_type,
254 &sa, child->get_mark(child, FALSE),
255 policy_prio);
256
257 /* uninstall in policy */
258 status |= hydra->kernel_interface->del_policy(
259 hydra->kernel_interface, host_any, host_any,
260 other_ts, my_ts, POLICY_IN, policy_type,
261 &sa, child->get_mark(child, TRUE),
262 policy_prio);
263
264 /* uninstall forward policy */
265 status |= hydra->kernel_interface->del_policy(
266 hydra->kernel_interface, host_any, host_any,
267 other_ts, my_ts, POLICY_FWD, policy_type,
268 &sa, child->get_mark(child, TRUE),
269 policy_prio);
270 }
271 e_other_ts->destroy(e_other_ts);
272 }
273 e_my_ts->destroy(e_my_ts);
274
275 my_ts_list->destroy_offset(my_ts_list,
276 offsetof(traffic_selector_t, destroy));
277 other_ts_list->destroy_offset(other_ts_list,
278 offsetof(traffic_selector_t, destroy));
279 host_any6->destroy(host_any6);
280 host_any->destroy(host_any);
281
282 if (status != SUCCESS)
283 {
284 DBG1(DBG_CFG, "uninstalling shunt %N 'policy %s' failed",
285 ipsec_mode_names, child->get_mode(child), child->get_name(child));
286 }
287 }
288
289 METHOD(shunt_manager_t, uninstall, bool,
290 private_shunt_manager_t *this, char *name)
291 {
292 enumerator_t *enumerator;
293 child_cfg_t *child, *found = NULL;
294
295 this->lock->write_lock(this->lock);
296 enumerator = this->shunts->create_enumerator(this->shunts);
297 while (enumerator->enumerate(enumerator, &child))
298 {
299 if (streq(name, child->get_name(child)))
300 {
301 this->shunts->remove_at(this->shunts, enumerator);
302 found = child;
303 break;
304 }
305 }
306 enumerator->destroy(enumerator);
307 this->lock->unlock(this->lock);
308
309 if (!found)
310 {
311 return FALSE;
312 }
313 uninstall_shunt_policy(child);
314 child->destroy(child);
315 return TRUE;
316 }
317
318 METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
319 private_shunt_manager_t *this)
320 {
321 this->lock->read_lock(this->lock);
322 return enumerator_create_cleaner(
323 this->shunts->create_enumerator(this->shunts),
324 (void*)this->lock->unlock, this->lock);
325 }
326
327 METHOD(shunt_manager_t, flush, void,
328 private_shunt_manager_t *this)
329 {
330 child_cfg_t *child;
331
332 this->lock->write_lock(this->lock);
333 while (this->installing)
334 {
335 this->condvar->wait(this->condvar, this->lock);
336 }
337 while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS)
338 {
339 uninstall_shunt_policy(child);
340 child->destroy(child);
341 }
342 this->installing = INSTALL_DISABLED;
343 this->lock->unlock(this->lock);
344 }
345
346 METHOD(shunt_manager_t, destroy, void,
347 private_shunt_manager_t *this)
348 {
349 this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy));
350 this->lock->destroy(this->lock);
351 this->condvar->destroy(this->condvar);
352 free(this);
353 }
354
355 /**
356 * See header
357 */
358 shunt_manager_t *shunt_manager_create()
359 {
360 private_shunt_manager_t *this;
361
362 INIT(this,
363 .public = {
364 .install = _install,
365 .uninstall = _uninstall,
366 .create_enumerator = _create_enumerator,
367 .flush = _flush,
368 .destroy = _destroy,
369 },
370 .shunts = linked_list_create(),
371 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
372 .condvar = rwlock_condvar_create(),
373 );
374
375 return &this->public;
376 }