libipsec: Pass the same data to del_policy() as to add_policy()
[strongswan.git] / src / libipsec / ipsec_policy_mgr.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "ipsec_policy_mgr.h"
19
20 #include <utils/debug.h>
21 #include <threading/rwlock.h>
22 #include <collections/linked_list.h>
23
24 /** Base priority for installed policies */
25 #define PRIO_BASE 384
26
27 typedef struct private_ipsec_policy_mgr_t private_ipsec_policy_mgr_t;
28
29 /**
30 * Private additions to ipsec_policy_mgr_t.
31 */
32 struct private_ipsec_policy_mgr_t {
33
34 /**
35 * Public members of ipsec_policy_mgr_t.
36 */
37 ipsec_policy_mgr_t public;
38
39 /**
40 * Installed policies (ipsec_policy_entry_t*)
41 */
42 linked_list_t *policies;
43
44 /**
45 * Lock to safely access the list of policies
46 */
47 rwlock_t *lock;
48
49 };
50
51 /**
52 * Helper struct to store policies in a list sorted by the same pseudo-priority
53 * used by the NETLINK kernel interface.
54 */
55 typedef struct {
56
57 /**
58 * Priority used to sort policies
59 */
60 u_int32_t priority;
61
62 /**
63 * The policy
64 */
65 ipsec_policy_t *policy;
66
67 } ipsec_policy_entry_t;
68
69 /**
70 * Calculate the pseudo-priority to sort policies. This is the same algorithm
71 * used by the NETLINK kernel interface (i.e. high priority -> low value).
72 */
73 static u_int32_t calculate_priority(policy_priority_t policy_priority,
74 traffic_selector_t *src,
75 traffic_selector_t *dst)
76 {
77 u_int32_t priority = PRIO_BASE;
78 u_int16_t port;
79 u_int8_t mask, proto;
80 host_t *net;
81
82 switch (policy_priority)
83 {
84 case POLICY_PRIORITY_FALLBACK:
85 priority <<= 1;
86 /* fall-through */
87 case POLICY_PRIORITY_ROUTED:
88 priority <<= 1;
89 /* fall-through */
90 case POLICY_PRIORITY_DEFAULT:
91 priority <<= 1;
92 /* fall-through */
93 case POLICY_PRIORITY_PASS:
94 break;
95 }
96 /* calculate priority based on selector size, small size = high prio */
97 src->to_subnet(src, &net, &mask);
98 priority -= mask;
99 proto = src->get_protocol(src);
100 port = net->get_port(net);
101 net->destroy(net);
102
103 dst->to_subnet(dst, &net, &mask);
104 priority -= mask;
105 proto = max(proto, dst->get_protocol(dst));
106 port = max(port, net->get_port(net));
107 net->destroy(net);
108
109 priority <<= 2; /* make some room for the two flags */
110 priority += port ? 0 : 2;
111 priority += proto ? 0 : 1;
112 return priority;
113 }
114
115 /**
116 * Create a policy entry
117 */
118 static ipsec_policy_entry_t *policy_entry_create(ipsec_policy_t *policy)
119 {
120 ipsec_policy_entry_t *this;
121
122 INIT(this,
123 .policy = policy,
124 .priority = calculate_priority(policy->get_priority(policy),
125 policy->get_source_ts(policy),
126 policy->get_destination_ts(policy)),
127 );
128 return this;
129 }
130
131 /**
132 * Destroy a policy entry
133 */
134 static void policy_entry_destroy(ipsec_policy_entry_t *this)
135 {
136 this->policy->destroy(this->policy);
137 free(this);
138 }
139
140 METHOD(ipsec_policy_mgr_t, add_policy, status_t,
141 private_ipsec_policy_mgr_t *this, host_t *src, host_t *dst,
142 traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
143 policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
144 policy_priority_t priority)
145 {
146 enumerator_t *enumerator;
147 ipsec_policy_entry_t *entry, *current;
148 ipsec_policy_t *policy;
149
150 if (type != POLICY_IPSEC || direction == POLICY_FWD)
151 { /* we ignore these policies as we currently have no use for them */
152 return SUCCESS;
153 }
154
155 DBG2(DBG_ESP, "adding policy %R === %R %N", src_ts, dst_ts,
156 policy_dir_names, direction);
157
158 policy = ipsec_policy_create(src, dst, src_ts, dst_ts, direction, type, sa,
159 mark, priority);
160 entry = policy_entry_create(policy);
161
162 this->lock->write_lock(this->lock);
163 enumerator = this->policies->create_enumerator(this->policies);
164 while (enumerator->enumerate(enumerator, (void**)&current))
165 {
166 if (current->priority >= entry->priority)
167 {
168 break;
169 }
170 }
171 this->policies->insert_before(this->policies, enumerator, entry);
172 enumerator->destroy(enumerator);
173 this->lock->unlock(this->lock);
174 return SUCCESS;
175 }
176
177 METHOD(ipsec_policy_mgr_t, del_policy, status_t,
178 private_ipsec_policy_mgr_t *this, host_t *src, host_t *dst,
179 traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
180 policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
181 policy_priority_t policy_priority)
182 {
183 enumerator_t *enumerator;
184 ipsec_policy_entry_t *current, *found = NULL;
185 u_int32_t priority;
186
187 if (type != POLICY_IPSEC || direction == POLICY_FWD)
188 { /* we ignore these policies as we currently have no use for them */
189 return SUCCESS;
190 }
191 DBG2(DBG_ESP, "deleting policy %R === %R %N", src_ts, dst_ts,
192 policy_dir_names, direction);
193
194 priority = calculate_priority(policy_priority, src_ts, dst_ts);
195
196 this->lock->write_lock(this->lock);
197 enumerator = this->policies->create_enumerator(this->policies);
198 while (enumerator->enumerate(enumerator, (void**)&current))
199 {
200 if (current->priority == priority &&
201 current->policy->match(current->policy, src_ts, dst_ts, direction,
202 sa->reqid, mark, policy_priority))
203 {
204 this->policies->remove_at(this->policies, enumerator);
205 found = current;
206 break;
207 }
208 }
209 enumerator->destroy(enumerator);
210 this->lock->unlock(this->lock);
211 if (found)
212 {
213 policy_entry_destroy(found);
214 return SUCCESS;
215 }
216 return FAILED;
217 }
218
219 METHOD(ipsec_policy_mgr_t, flush_policies, status_t,
220 private_ipsec_policy_mgr_t *this)
221 {
222 ipsec_policy_entry_t *entry;
223
224 DBG2(DBG_ESP, "flushing policies");
225
226 this->lock->write_lock(this->lock);
227 while (this->policies->remove_last(this->policies,
228 (void**)&entry) == SUCCESS)
229 {
230 policy_entry_destroy(entry);
231 }
232 this->lock->unlock(this->lock);
233 return SUCCESS;
234 }
235
236 METHOD(ipsec_policy_mgr_t, find_by_packet, ipsec_policy_t*,
237 private_ipsec_policy_mgr_t *this, ip_packet_t *packet, bool inbound,
238 u_int32_t reqid)
239 {
240 enumerator_t *enumerator;
241 ipsec_policy_entry_t *current;
242 ipsec_policy_t *found = NULL;
243
244 this->lock->read_lock(this->lock);
245 enumerator = this->policies->create_enumerator(this->policies);
246 while (enumerator->enumerate(enumerator, (void**)&current))
247 {
248 ipsec_policy_t *policy = current->policy;
249
250 if ((inbound == (policy->get_direction(policy) == POLICY_IN)) &&
251 policy->match_packet(policy, packet))
252 {
253 if (reqid == 0 || reqid == policy->get_reqid(policy))
254 {
255 found = policy->get_ref(policy);
256 break;
257 }
258 }
259 }
260 enumerator->destroy(enumerator);
261 this->lock->unlock(this->lock);
262 return found;
263 }
264
265 METHOD(ipsec_policy_mgr_t, destroy, void,
266 private_ipsec_policy_mgr_t *this)
267 {
268 flush_policies(this);
269 this->policies->destroy(this->policies);
270 this->lock->destroy(this->lock);
271 free(this);
272 }
273
274 /**
275 * Described in header.
276 */
277 ipsec_policy_mgr_t *ipsec_policy_mgr_create()
278 {
279 private_ipsec_policy_mgr_t *this;
280
281 INIT(this,
282 .public = {
283 .add_policy = _add_policy,
284 .del_policy = _del_policy,
285 .flush_policies = _flush_policies,
286 .find_by_packet = _find_by_packet,
287 .destroy = _destroy,
288 },
289 .policies = linked_list_create(),
290 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
291 );
292
293 return &this->public;
294 }