Annotation of embedaddon/strongswan/src/libcharon/sa/shunt_manager.c, revision 1.1.1.1
1.1 misho 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>