Remove policies in kernel interfaces based on their priority.
[strongswan.git] / src / libcharon / sa / shunt_manager.c
1 /*
2 * Copyright (C) 2011 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "shunt_manager.h"
17
18 #include <hydra.h>
19 #include <daemon.h>
20 #include <threading/rwlock.h>
21 #include <utils/linked_list.h>
22
23
24 typedef struct private_shunt_manager_t private_shunt_manager_t;
25
26 /**
27 * Private data of an shunt_manager_t object.
28 */
29 struct private_shunt_manager_t {
30
31 /**
32 * Public shunt_manager_t interface.
33 */
34 shunt_manager_t public;
35
36 /**
37 * Installed shunts, as child_cfg_t
38 */
39 linked_list_t *shunts;
40 };
41
42 /**
43 * Install in and out shunt policies in the kernel
44 */
45 static bool install_shunt_policy(child_cfg_t *child)
46 {
47 enumerator_t *e_my_ts, *e_other_ts;
48 linked_list_t *my_ts_list, *other_ts_list;
49 traffic_selector_t *my_ts, *other_ts;
50 host_t *host_any;
51 policy_type_t policy_type;
52 status_t status = SUCCESS;
53 ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
54
55 policy_type = (child->get_mode(child) == MODE_PASS) ?
56 POLICY_PASS : POLICY_DROP;
57 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, NULL);
58 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, NULL);
59 host_any = host_create_any(AF_INET);
60
61 /* enumerate pairs of traffic selectors */
62 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
63 while (e_my_ts->enumerate(e_my_ts, &my_ts))
64 {
65 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
66 while (e_other_ts->enumerate(e_other_ts, &other_ts))
67 {
68 /* install out policy */
69 status |= hydra->kernel_interface->add_policy(
70 hydra->kernel_interface, host_any, host_any,
71 my_ts, other_ts, POLICY_OUT, policy_type,
72 &sa, child->get_mark(child, FALSE),
73 POLICY_PRIORITY_DEFAULT);
74
75 /* install in policy */
76 status |= hydra->kernel_interface->add_policy(
77 hydra->kernel_interface, host_any, host_any,
78 other_ts, my_ts, POLICY_IN, policy_type,
79 &sa, child->get_mark(child, TRUE),
80 POLICY_PRIORITY_DEFAULT);
81
82 /* install forward policy */
83 status |= hydra->kernel_interface->add_policy(
84 hydra->kernel_interface, host_any, host_any,
85 other_ts, my_ts, POLICY_FWD, policy_type,
86 &sa, child->get_mark(child, TRUE),
87 POLICY_PRIORITY_DEFAULT);
88 }
89 e_other_ts->destroy(e_other_ts);
90 }
91 e_my_ts->destroy(e_my_ts);
92
93 my_ts_list->destroy_offset(my_ts_list,
94 offsetof(traffic_selector_t, destroy));
95 other_ts_list->destroy_offset(other_ts_list,
96 offsetof(traffic_selector_t, destroy));
97 host_any->destroy(host_any);
98
99 return status == SUCCESS;
100 }
101
102 METHOD(shunt_manager_t, install, bool,
103 private_shunt_manager_t *this, child_cfg_t *child)
104 {
105 enumerator_t *enumerator;
106 child_cfg_t *child_cfg;
107 bool found = FALSE;
108
109 /* check if not already installed */
110 enumerator = this->shunts->create_enumerator(this->shunts);
111 while (enumerator->enumerate(enumerator, &child_cfg))
112 {
113 if (streq(child_cfg->get_name(child_cfg), child->get_name(child)))
114 {
115 found = TRUE;
116 break;
117 }
118 }
119 enumerator->destroy(enumerator);
120
121 if (found)
122 {
123 DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
124 ipsec_mode_names, child->get_mode(child), child->get_name(child));
125 return TRUE;
126 }
127 this->shunts->insert_last(this->shunts, child->get_ref(child));
128
129 return install_shunt_policy(child);
130 }
131
132 /**
133 * Uninstall in and out shunt policies in the kernel
134 */
135 static void uninstall_shunt_policy(child_cfg_t *child)
136 {
137 enumerator_t *e_my_ts, *e_other_ts;
138 linked_list_t *my_ts_list, *other_ts_list;
139 traffic_selector_t *my_ts, *other_ts;
140 status_t status = SUCCESS;
141
142 my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, NULL);
143 other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, NULL);
144
145 /* enumerate pairs of traffic selectors */
146 e_my_ts = my_ts_list->create_enumerator(my_ts_list);
147 while (e_my_ts->enumerate(e_my_ts, &my_ts))
148 {
149 e_other_ts = other_ts_list->create_enumerator(other_ts_list);
150 while (e_other_ts->enumerate(e_other_ts, &other_ts))
151 {
152 /* uninstall out policy */
153 status |= hydra->kernel_interface->del_policy(
154 hydra->kernel_interface, my_ts, other_ts,
155 POLICY_OUT, 0, child->get_mark(child, FALSE),
156 POLICY_PRIORITY_DEFAULT);
157
158 /* uninstall in policy */
159 status |= hydra->kernel_interface->del_policy(
160 hydra->kernel_interface, other_ts, my_ts,
161 POLICY_IN, 0, child->get_mark(child, TRUE),
162 POLICY_PRIORITY_DEFAULT);
163
164 /* uninstall forward policy */
165 status |= hydra->kernel_interface->del_policy(
166 hydra->kernel_interface, other_ts, my_ts,
167 POLICY_FWD, 0, child->get_mark(child, TRUE),
168 POLICY_PRIORITY_DEFAULT);
169 }
170 e_other_ts->destroy(e_other_ts);
171 }
172 e_my_ts->destroy(e_my_ts);
173
174 my_ts_list->destroy_offset(my_ts_list,
175 offsetof(traffic_selector_t, destroy));
176 other_ts_list->destroy_offset(other_ts_list,
177 offsetof(traffic_selector_t, destroy));
178
179 if (status != SUCCESS)
180 {
181 DBG1(DBG_CFG, "uninstalling shunt %N 'policy %s' failed",
182 ipsec_mode_names, child->get_mode(child), child->get_name(child));
183 }
184 }
185
186 METHOD(shunt_manager_t, uninstall, bool,
187 private_shunt_manager_t *this, char *name)
188 {
189 enumerator_t *enumerator;
190 child_cfg_t *child, *found = NULL;
191
192 enumerator = this->shunts->create_enumerator(this->shunts);
193 while (enumerator->enumerate(enumerator, &child))
194 {
195 if (streq(name, child->get_name(child)))
196 {
197 this->shunts->remove_at(this->shunts, enumerator);
198 found = child;
199 break;
200 }
201 }
202 enumerator->destroy(enumerator);
203
204 if (!found)
205 {
206 return FALSE;
207 }
208 uninstall_shunt_policy(child);
209 return TRUE;
210 }
211
212 METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
213 private_shunt_manager_t *this)
214 {
215 return this->shunts->create_enumerator(this->shunts);
216 }
217
218 METHOD(shunt_manager_t, destroy, void,
219 private_shunt_manager_t *this)
220 {
221 child_cfg_t *child;
222
223 while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS)
224 {
225 uninstall_shunt_policy(child);
226 child->destroy(child);
227 }
228 this->shunts->destroy(this->shunts);
229 free(this);
230 }
231
232 /**
233 * See header
234 */
235 shunt_manager_t *shunt_manager_create()
236 {
237 private_shunt_manager_t *this;
238
239 INIT(this,
240 .public = {
241 .install = _install,
242 .uninstall = _uninstall,
243 .create_enumerator = _create_enumerator,
244 .destroy = _destroy,
245 },
246 .shunts = linked_list_create(),
247 );
248
249 return &this->public;
250 }
251