]>
Commit | Line | Data |
---|---|---|
eb8ed130 MW |
1 | /* |
2 | * Copyright (C) 2009 Martin Willi | |
3 | * 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. | |
eb8ed130 MW |
14 | */ |
15 | ||
16 | #include "trap_manager.h" | |
17 | ||
18 | #include <daemon.h> | |
19 | #include <utils/mutex.h> | |
20 | #include <utils/linked_list.h> | |
21 | ||
22 | ||
23 | typedef struct private_trap_manager_t private_trap_manager_t; | |
9d737ecf MW |
24 | typedef struct trap_listener_t trap_listener_t; |
25 | ||
26 | /** | |
27 | * listener to track acquires | |
28 | */ | |
29 | struct trap_listener_t { | |
30 | ||
31 | /** | |
32 | * Implements listener interface | |
33 | */ | |
34 | listener_t listener; | |
35 | ||
36 | /** | |
37 | * points to trap_manager | |
38 | */ | |
39 | private_trap_manager_t *traps; | |
40 | }; | |
eb8ed130 MW |
41 | |
42 | /** | |
43 | * Private data of an trap_manager_t object. | |
44 | */ | |
45 | struct private_trap_manager_t { | |
46 | ||
47 | /** | |
48 | * Public trap_manager_t interface. | |
49 | */ | |
50 | trap_manager_t public; | |
51 | ||
52 | /** | |
53 | * Installed traps, as entry_t | |
54 | */ | |
55 | linked_list_t *traps; | |
56 | ||
57 | /** | |
58 | * mutex to lock traps list | |
59 | */ | |
60 | mutex_t *mutex; | |
9d737ecf MW |
61 | |
62 | /** | |
63 | * listener to track acquiring IKE_SAs | |
64 | */ | |
65 | trap_listener_t listener; | |
eb8ed130 MW |
66 | }; |
67 | ||
68 | /** | |
69 | * A installed trap entry | |
70 | */ | |
71 | typedef struct { | |
72 | /** ref to peer_cfg to initiate */ | |
73 | peer_cfg_t *peer_cfg; | |
74 | /** ref to instanciated CHILD_SA */ | |
75 | child_sa_t *child_sa; | |
9d737ecf MW |
76 | /** pending IKE_SA connecting upon acquire */ |
77 | ike_sa_t *pending; | |
eb8ed130 MW |
78 | } entry_t; |
79 | ||
80 | /** | |
81 | * actually uninstall and destroy an installed entry | |
82 | */ | |
83 | static void destroy_entry(entry_t *entry) | |
84 | { | |
85 | entry->child_sa->destroy(entry->child_sa); | |
86 | entry->peer_cfg->destroy(entry->peer_cfg); | |
87 | free(entry); | |
88 | } | |
89 | ||
90 | /** | |
91 | * Implementation of trap_manager_t.install | |
92 | */ | |
c3626c2c | 93 | static u_int32_t install(private_trap_manager_t *this, peer_cfg_t *peer, |
eb8ed130 MW |
94 | child_cfg_t *child) |
95 | { | |
96 | entry_t *entry; | |
97 | ike_cfg_t *ike_cfg; | |
98 | child_sa_t *child_sa; | |
99 | host_t *me, *other; | |
100 | linked_list_t *my_ts, *other_ts; | |
101 | enumerator_t *enumerator; | |
102 | bool found = FALSE; | |
103 | status_t status; | |
c3626c2c | 104 | u_int32_t reqid; |
eb8ed130 MW |
105 | |
106 | /* check if not already done */ | |
107 | this->mutex->lock(this->mutex); | |
108 | enumerator = this->traps->create_enumerator(this->traps); | |
109 | while (enumerator->enumerate(enumerator, &entry)) | |
110 | { | |
111 | if (streq(entry->child_sa->get_name(entry->child_sa), | |
112 | child->get_name(child))) | |
113 | { | |
114 | found = TRUE; | |
115 | break; | |
116 | } | |
117 | } | |
118 | enumerator->destroy(enumerator); | |
119 | this->mutex->unlock(this->mutex); | |
120 | if (found) | |
121 | { | |
122 | DBG1(DBG_CFG, "CHILD_SA named '%s' already routed", | |
123 | child->get_name(child)); | |
124 | return 0; | |
125 | } | |
126 | ||
127 | /* try to resolve addresses */ | |
128 | ike_cfg = peer->get_ike_cfg(peer); | |
129 | other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg), | |
130 | 0, IKEV2_UDP_PORT); | |
131 | if (!other) | |
132 | { | |
133 | DBG1(DBG_CFG, "installing trap failed, remote address unknown"); | |
134 | return 0; | |
135 | } | |
136 | me = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg), | |
137 | other->get_family(other), IKEV2_UDP_PORT); | |
138 | if (!me || me->is_anyaddr(me)) | |
139 | { | |
140 | DESTROY_IF(me); | |
141 | me = charon->kernel_interface->get_source_addr( | |
142 | charon->kernel_interface, other, NULL); | |
143 | if (!me) | |
144 | { | |
145 | DBG1(DBG_CFG, "installing trap failed, local address unknown"); | |
146 | other->destroy(other); | |
147 | return 0; | |
148 | } | |
149 | me->set_port(me, IKEV2_UDP_PORT); | |
150 | } | |
151 | ||
152 | /* create and route CHILD_SA */ | |
153 | child_sa = child_sa_create(me, other, child, 0, FALSE); | |
154 | my_ts = child->get_traffic_selectors(child, TRUE, NULL, me); | |
155 | other_ts = child->get_traffic_selectors(child, FALSE, NULL, other); | |
156 | me->destroy(me); | |
157 | other->destroy(other); | |
158 | ||
159 | child_sa->set_mode(child_sa, child->get_mode(child)); | |
160 | status = child_sa->add_policies(child_sa, my_ts, other_ts); | |
161 | my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); | |
162 | other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); | |
163 | if (status != SUCCESS) | |
164 | { | |
165 | child_sa->destroy(child_sa); | |
166 | DBG1(DBG_CFG, "installing trap failed"); | |
167 | return 0; | |
168 | } | |
169 | ||
170 | reqid = child_sa->get_reqid(child_sa); | |
171 | entry = malloc_thing(entry_t); | |
172 | entry->child_sa = child_sa; | |
173 | entry->peer_cfg = peer->get_ref(peer); | |
9d737ecf | 174 | entry->pending = NULL; |
eb8ed130 MW |
175 | |
176 | this->mutex->lock(this->mutex); | |
177 | this->traps->insert_last(this->traps, entry); | |
178 | this->mutex->unlock(this->mutex); | |
179 | ||
180 | return reqid; | |
181 | } | |
182 | ||
183 | /** | |
184 | * Implementation of trap_manager_t.uninstall | |
185 | */ | |
c3626c2c | 186 | static bool uninstall(private_trap_manager_t *this, u_int32_t reqid) |
eb8ed130 MW |
187 | { |
188 | enumerator_t *enumerator; | |
189 | entry_t *entry, *found = NULL; | |
190 | ||
191 | this->mutex->lock(this->mutex); | |
192 | enumerator = this->traps->create_enumerator(this->traps); | |
193 | while (enumerator->enumerate(enumerator, &entry)) | |
194 | { | |
195 | if (entry->child_sa->get_reqid(entry->child_sa) == reqid) | |
196 | { | |
197 | this->traps->remove_at(this->traps, enumerator); | |
198 | found = entry; | |
199 | break; | |
200 | } | |
201 | } | |
202 | enumerator->destroy(enumerator); | |
203 | this->mutex->unlock(this->mutex); | |
204 | ||
205 | if (!found) | |
206 | { | |
207 | DBG1(DBG_CFG, "trap %d not found to uninstall", reqid); | |
208 | return FALSE; | |
209 | } | |
210 | ||
211 | destroy_entry(found); | |
212 | return TRUE; | |
213 | } | |
214 | ||
215 | /** | |
216 | * convert enumerated entries to peer_cfg, child_sa | |
217 | */ | |
218 | static bool trap_filter(mutex_t *mutex, entry_t **entry, peer_cfg_t **peer_cfg, | |
219 | void *none, child_sa_t **child_sa) | |
220 | { | |
221 | if (peer_cfg) | |
222 | { | |
223 | *peer_cfg = (*entry)->peer_cfg; | |
224 | } | |
225 | if (child_sa) | |
226 | { | |
227 | *child_sa = (*entry)->child_sa; | |
228 | } | |
229 | return TRUE; | |
230 | } | |
231 | ||
232 | /** | |
233 | * Implementation of trap_manager_t.create_enumerator | |
234 | */ | |
235 | static enumerator_t* create_enumerator(private_trap_manager_t *this) | |
236 | { | |
237 | this->mutex->lock(this->mutex); | |
238 | return enumerator_create_filter(this->traps->create_enumerator(this->traps), | |
239 | (void*)trap_filter, this->mutex, | |
240 | (void*)this->mutex->unlock); | |
241 | } | |
242 | ||
243 | /** | |
244 | * Implementation of trap_manager_t.acquire | |
245 | */ | |
c3626c2c | 246 | static void acquire(private_trap_manager_t *this, u_int32_t reqid, |
eb8ed130 MW |
247 | traffic_selector_t *src, traffic_selector_t *dst) |
248 | { | |
249 | enumerator_t *enumerator; | |
250 | entry_t *entry, *found = NULL; | |
251 | peer_cfg_t *peer; | |
252 | child_cfg_t *child; | |
253 | ike_sa_t *ike_sa; | |
254 | ||
255 | this->mutex->lock(this->mutex); | |
256 | enumerator = this->traps->create_enumerator(this->traps); | |
257 | while (enumerator->enumerate(enumerator, &entry)) | |
258 | { | |
259 | if (entry->child_sa->get_reqid(entry->child_sa) == reqid) | |
260 | { | |
261 | found = entry; | |
262 | break; | |
263 | } | |
264 | } | |
265 | enumerator->destroy(enumerator); | |
266 | ||
267 | if (!found) | |
268 | { | |
269 | DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid); | |
eb8ed130 | 270 | } |
9d737ecf | 271 | else if (found->pending) |
eb8ed130 | 272 | { |
9d737ecf MW |
273 | DBG1(DBG_CFG, "ignoring acquire, connection attempt pending"); |
274 | } | |
275 | else | |
276 | { | |
277 | child = found->child_sa->get_config(found->child_sa); | |
278 | peer = found->peer_cfg; | |
279 | ike_sa = charon->ike_sa_manager->checkout_by_config( | |
280 | charon->ike_sa_manager, peer); | |
281 | if (ike_sa->get_peer_cfg(ike_sa) == NULL) | |
282 | { | |
283 | ike_sa->set_peer_cfg(ike_sa, peer); | |
284 | } | |
285 | child->get_ref(child); | |
286 | reqid = found->child_sa->get_reqid(found->child_sa); | |
a13c013b | 287 | if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME) |
9d737ecf MW |
288 | { |
289 | found->pending = ike_sa; | |
290 | charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); | |
291 | } | |
292 | else | |
293 | { | |
294 | charon->ike_sa_manager->checkin_and_destroy( | |
295 | charon->ike_sa_manager, ike_sa); | |
296 | } | |
eb8ed130 | 297 | } |
eb8ed130 | 298 | this->mutex->unlock(this->mutex); |
9d737ecf MW |
299 | } |
300 | ||
301 | /** | |
302 | * Implementation of listener_t.ike_state_change | |
303 | */ | |
304 | static bool ike_state_change(trap_listener_t *listener, ike_sa_t *ike_sa, | |
305 | ike_sa_state_t state) | |
306 | { | |
307 | private_trap_manager_t *this; | |
308 | enumerator_t *enumerator; | |
309 | entry_t *entry; | |
310 | ||
311 | switch (state) | |
eb8ed130 | 312 | { |
9d737ecf MW |
313 | case IKE_ESTABLISHED: |
314 | case IKE_DESTROYING: | |
315 | break; | |
316 | default: | |
317 | return TRUE; | |
eb8ed130 | 318 | } |
9d737ecf MW |
319 | |
320 | this = listener->traps; | |
321 | this->mutex->lock(this->mutex); | |
322 | enumerator = this->traps->create_enumerator(this->traps); | |
323 | while (enumerator->enumerate(enumerator, &entry)) | |
324 | { | |
325 | if (entry->pending == ike_sa) | |
326 | { | |
327 | entry->pending = NULL; | |
328 | } | |
329 | } | |
330 | enumerator->destroy(enumerator); | |
331 | this->mutex->unlock(this->mutex); | |
332 | return TRUE; | |
eb8ed130 MW |
333 | } |
334 | ||
335 | /** | |
336 | * Implementation of trap_manager_t.destroy. | |
337 | */ | |
338 | static void destroy(private_trap_manager_t *this) | |
339 | { | |
9d737ecf | 340 | charon->bus->remove_listener(charon->bus, &this->listener.listener); |
eb8ed130 MW |
341 | this->traps->invoke_function(this->traps, (void*)destroy_entry); |
342 | this->traps->destroy(this->traps); | |
343 | this->mutex->destroy(this->mutex); | |
344 | free(this); | |
345 | } | |
346 | ||
347 | /** | |
348 | * See header | |
349 | */ | |
350 | trap_manager_t *trap_manager_create() | |
351 | { | |
352 | private_trap_manager_t *this = malloc_thing(private_trap_manager_t); | |
353 | ||
9d737ecf | 354 | this->public.install = (u_int(*)(trap_manager_t*, peer_cfg_t *peer, child_cfg_t *child))install; |
c3626c2c | 355 | this->public.uninstall = (bool(*)(trap_manager_t*, u_int32_t id))uninstall; |
eb8ed130 | 356 | this->public.create_enumerator = (enumerator_t*(*)(trap_manager_t*))create_enumerator; |
c3626c2c | 357 | this->public.acquire = (void(*)(trap_manager_t*, u_int32_t reqid, traffic_selector_t *src, traffic_selector_t *dst))acquire; |
eb8ed130 MW |
358 | this->public.destroy = (void(*)(trap_manager_t*))destroy; |
359 | ||
360 | this->traps = linked_list_create(); | |
361 | this->mutex = mutex_create(MUTEX_DEFAULT); | |
362 | ||
9d737ecf MW |
363 | /* register listener for IKE state changes */ |
364 | this->listener.traps = this; | |
365 | memset(&this->listener.listener, 0, sizeof(listener_t)); | |
366 | this->listener.listener.ike_state_change = (void*)ike_state_change; | |
367 | charon->bus->add_listener(charon->bus, &this->listener.listener); | |
368 | ||
eb8ed130 MW |
369 | return &this->public; |
370 | } | |
371 |