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