shunt-manager: Don't install policies in case of an address family or IP protocol...
[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;
67 traffic_selector_t *my_ts, *other_ts;
68 host_t *host_any;
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 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, NULL);
89 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, NULL);
90 host_any = host_create_any(AF_INET);
91
92 /* enumerate pairs of traffic selectors */
93 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
94 while (e_my_ts->enumerate(e_my_ts, &my_ts))
95 {
96 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
97 while (e_other_ts->enumerate(e_other_ts, &other_ts))
98 {
99 if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
100 {
101 continue;
102 }
103 if (my_ts->get_protocol(my_ts) &&
104 other_ts->get_protocol(other_ts) &&
105 my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
106 {
107 continue;
108 }
109 /* install out policy */
110 status |= hydra->kernel_interface->add_policy(
111 hydra->kernel_interface, host_any, host_any,
112 my_ts, other_ts, POLICY_OUT, policy_type,
113 &sa, child->get_mark(child, FALSE),
114 policy_prio);
115
116 /* install in policy */
117 status |= hydra->kernel_interface->add_policy(
118 hydra->kernel_interface, host_any, host_any,
119 other_ts, my_ts, POLICY_IN, policy_type,
120 &sa, child->get_mark(child, TRUE),
121 policy_prio);
122
123 /* install forward policy */
124 status |= hydra->kernel_interface->add_policy(
125 hydra->kernel_interface, host_any, host_any,
126 other_ts, my_ts, POLICY_FWD, policy_type,
127 &sa, child->get_mark(child, TRUE),
128 policy_prio);
129 }
130 e_other_ts->destroy(e_other_ts);
131 }
132 e_my_ts->destroy(e_my_ts);
133
134 my_ts_list->destroy_offset(my_ts_list,
135 offsetof(traffic_selector_t, destroy));
136 other_ts_list->destroy_offset(other_ts_list,
137 offsetof(traffic_selector_t, destroy));
138 host_any->destroy(host_any);
139
140 return status == SUCCESS;
141 }
142
143 METHOD(shunt_manager_t, install, bool,
144 private_shunt_manager_t *this, child_cfg_t *child)
145 {
146 enumerator_t *enumerator;
147 child_cfg_t *child_cfg;
148 bool found = FALSE, success;
149
150 /* check if not already installed */
151 this->lock->write_lock(this->lock);
152 if (this->installing == INSTALL_DISABLED)
153 { /* flush() has been called */
154 this->lock->unlock(this->lock);
155 return FALSE;
156 }
157 enumerator = this->shunts->create_enumerator(this->shunts);
158 while (enumerator->enumerate(enumerator, &child_cfg))
159 {
160 if (streq(child_cfg->get_name(child_cfg), child->get_name(child)))
161 {
162 found = TRUE;
163 break;
164 }
165 }
166 enumerator->destroy(enumerator);
167 if (found)
168 {
169 DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
170 ipsec_mode_names, child->get_mode(child), child->get_name(child));
171 this->lock->unlock(this->lock);
172 return TRUE;
173 }
174 this->shunts->insert_last(this->shunts, child->get_ref(child));
175 this->installing++;
176 this->lock->unlock(this->lock);
177
178 success = install_shunt_policy(child);
179
180 this->lock->write_lock(this->lock);
181 if (!success)
182 {
183 this->shunts->remove(this->shunts, child, NULL);
184 child->destroy(child);
185 }
186 this->installing--;
187 this->condvar->signal(this->condvar);
188 this->lock->unlock(this->lock);
189 return success;
190 }
191
192 /**
193 * Uninstall in and out shunt policies in the kernel
194 */
195 static void uninstall_shunt_policy(child_cfg_t *child)
196 {
197 enumerator_t *e_my_ts, *e_other_ts;
198 linked_list_t *my_ts_list, *other_ts_list;
199 traffic_selector_t *my_ts, *other_ts;
200 policy_priority_t policy_prio;
201 status_t status = SUCCESS;
202
203 switch (child->get_mode(child))
204 {
205 case MODE_PASS:
206 policy_prio = POLICY_PRIORITY_PASS;
207 break;
208 case MODE_DROP:
209 policy_prio = POLICY_PRIORITY_FALLBACK;
210 break;
211 default:
212 return;
213 }
214
215 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, NULL);
216 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, NULL);
217
218 /* enumerate pairs of traffic selectors */
219 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
220 while (e_my_ts->enumerate(e_my_ts, &my_ts))
221 {
222 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
223 while (e_other_ts->enumerate(e_other_ts, &other_ts))
224 {
225 if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
226 {
227 continue;
228 }
229 if (my_ts->get_protocol(my_ts) &&
230 other_ts->get_protocol(other_ts) &&
231 my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
232 {
233 continue;
234 }
235 /* uninstall out policy */
236 status |= hydra->kernel_interface->del_policy(
237 hydra->kernel_interface, my_ts, other_ts,
238 POLICY_OUT, 0, child->get_mark(child, FALSE),
239 policy_prio);
240
241 /* uninstall in policy */
242 status |= hydra->kernel_interface->del_policy(
243 hydra->kernel_interface, other_ts, my_ts,
244 POLICY_IN, 0, child->get_mark(child, TRUE),
245 policy_prio);
246
247 /* uninstall forward policy */
248 status |= hydra->kernel_interface->del_policy(
249 hydra->kernel_interface, other_ts, my_ts,
250 POLICY_FWD, 0, child->get_mark(child, TRUE),
251 policy_prio);
252 }
253 e_other_ts->destroy(e_other_ts);
254 }
255 e_my_ts->destroy(e_my_ts);
256
257 my_ts_list->destroy_offset(my_ts_list,
258 offsetof(traffic_selector_t, destroy));
259 other_ts_list->destroy_offset(other_ts_list,
260 offsetof(traffic_selector_t, destroy));
261
262 if (status != SUCCESS)
263 {
264 DBG1(DBG_CFG, "uninstalling shunt %N 'policy %s' failed",
265 ipsec_mode_names, child->get_mode(child), child->get_name(child));
266 }
267 }
268
269 METHOD(shunt_manager_t, uninstall, bool,
270 private_shunt_manager_t *this, char *name)
271 {
272 enumerator_t *enumerator;
273 child_cfg_t *child, *found = NULL;
274
275 this->lock->write_lock(this->lock);
276 enumerator = this->shunts->create_enumerator(this->shunts);
277 while (enumerator->enumerate(enumerator, &child))
278 {
279 if (streq(name, child->get_name(child)))
280 {
281 this->shunts->remove_at(this->shunts, enumerator);
282 found = child;
283 break;
284 }
285 }
286 enumerator->destroy(enumerator);
287 this->lock->unlock(this->lock);
288
289 if (!found)
290 {
291 return FALSE;
292 }
293 uninstall_shunt_policy(child);
294 child->destroy(child);
295 return TRUE;
296 }
297
298 METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
299 private_shunt_manager_t *this)
300 {
301 this->lock->read_lock(this->lock);
302 return enumerator_create_cleaner(
303 this->shunts->create_enumerator(this->shunts),
304 (void*)this->lock->unlock, this->lock);
305 }
306
307 METHOD(shunt_manager_t, flush, void,
308 private_shunt_manager_t *this)
309 {
310 child_cfg_t *child;
311
312 this->lock->write_lock(this->lock);
313 while (this->installing)
314 {
315 this->condvar->wait(this->condvar, this->lock);
316 }
317 while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS)
318 {
319 uninstall_shunt_policy(child);
320 child->destroy(child);
321 }
322 this->installing = INSTALL_DISABLED;
323 this->lock->unlock(this->lock);
324 }
325
326 METHOD(shunt_manager_t, destroy, void,
327 private_shunt_manager_t *this)
328 {
329 this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy));
330 this->lock->destroy(this->lock);
331 this->condvar->destroy(this->condvar);
332 free(this);
333 }
334
335 /**
336 * See header
337 */
338 shunt_manager_t *shunt_manager_create()
339 {
340 private_shunt_manager_t *this;
341
342 INIT(this,
343 .public = {
344 .install = _install,
345 .uninstall = _uninstall,
346 .create_enumerator = _create_enumerator,
347 .flush = _flush,
348 .destroy = _destroy,
349 },
350 .shunts = linked_list_create(),
351 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
352 .condvar = rwlock_condvar_create(),
353 );
354
355 return &this->public;
356 }