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