some task queueing improvements:
[strongswan.git] / src / charon / sa / tasks / child_delete.c
1 /*
2 * Copyright (C) 2006-2007 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.
14 *
15 * $Id$
16 */
17
18 #include "child_delete.h"
19
20 #include <daemon.h>
21 #include <encoding/payloads/delete_payload.h>
22
23
24 typedef struct private_child_delete_t private_child_delete_t;
25
26 /**
27 * Private members of a child_delete_t task.
28 */
29 struct private_child_delete_t {
30
31 /**
32 * Public methods and task_t interface.
33 */
34 child_delete_t public;
35
36 /**
37 * Assigned IKE_SA.
38 */
39 ike_sa_t *ike_sa;
40
41 /**
42 * Are we the initiator?
43 */
44 bool initiator;
45
46 /**
47 * Protocol of CHILD_SA to delete
48 */
49 protocol_id_t protocol;
50
51 /**
52 * Inbound SPI of CHILD_SA to delete
53 */
54 u_int32_t spi;
55
56 /**
57 * wheter to enforce delete action policy
58 */
59 bool check_delete_action;
60
61 /**
62 * CHILD_SAs which get deleted
63 */
64 linked_list_t *child_sas;
65 };
66
67 /**
68 * build the delete payloads from the listed child_sas
69 */
70 static void build_payloads(private_child_delete_t *this, message_t *message)
71 {
72 delete_payload_t *ah = NULL, *esp = NULL;
73 iterator_t *iterator;
74 child_sa_t *child_sa;
75
76 iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
77 while (iterator->iterate(iterator, (void**)&child_sa))
78 {
79 protocol_id_t protocol = child_sa->get_protocol(child_sa);
80 u_int32_t spi = child_sa->get_spi(child_sa, TRUE);
81
82 switch (protocol)
83 {
84 case PROTO_ESP:
85 if (esp == NULL)
86 {
87 esp = delete_payload_create(PROTO_ESP);
88 message->add_payload(message, (payload_t*)esp);
89 }
90 esp->add_spi(esp, spi);
91 DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
92 protocol_id_names, protocol, ntohl(spi));
93 break;
94 case PROTO_AH:
95 if (ah == NULL)
96 {
97 ah = delete_payload_create(PROTO_AH);
98 message->add_payload(message, (payload_t*)ah);
99 }
100 ah->add_spi(ah, spi);
101 DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
102 protocol_id_names, protocol, ntohl(spi));
103 break;
104 default:
105 break;
106 }
107 child_sa->set_state(child_sa, CHILD_DELETING);
108 }
109 iterator->destroy(iterator);
110 }
111
112 /**
113 * read in payloads and find the children to delete
114 */
115 static void process_payloads(private_child_delete_t *this, message_t *message)
116 {
117 iterator_t *payloads, *spis;
118 payload_t *payload;
119 delete_payload_t *delete_payload;
120 u_int32_t *spi;
121 protocol_id_t protocol;
122 child_sa_t *child_sa;
123
124 payloads = message->get_payload_iterator(message);
125 while (payloads->iterate(payloads, (void**)&payload))
126 {
127 if (payload->get_type(payload) == DELETE)
128 {
129 delete_payload = (delete_payload_t*)payload;
130 protocol = delete_payload->get_protocol_id(delete_payload);
131 if (protocol != PROTO_ESP && protocol != PROTO_AH)
132 {
133 continue;
134 }
135 spis = delete_payload->create_spi_iterator(delete_payload);
136 while (spis->iterate(spis, (void**)&spi))
137 {
138 child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
139 *spi, FALSE);
140 if (child_sa == NULL)
141 {
142 DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x, "
143 "but no such SA", protocol_id_names, protocol, ntohl(*spi));
144 continue;
145 }
146 DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x",
147 protocol_id_names, protocol, ntohl(*spi));
148
149 switch (child_sa->get_state(child_sa))
150 {
151 case CHILD_REKEYING:
152 /* we reply as usual, rekeying will fail */
153 break;
154 case CHILD_DELETING:
155 /* we don't send back a delete if we initiated ourself */
156 if (!this->initiator)
157 {
158 this->ike_sa->destroy_child_sa(this->ike_sa,
159 protocol, *spi);
160 continue;
161 }
162 case CHILD_INSTALLED:
163 if (!this->initiator)
164 { /* reestablish installed children if required */
165 this->check_delete_action = TRUE;
166 }
167 default:
168 break;
169 }
170
171 this->child_sas->insert_last(this->child_sas, child_sa);
172 }
173 spis->destroy(spis);
174 }
175 }
176 payloads->destroy(payloads);
177 }
178
179 /**
180 * destroy the children listed in this->child_sas, reestablish by policy
181 */
182 static status_t destroy_and_reestablish(private_child_delete_t *this)
183 {
184 iterator_t *iterator;
185 child_sa_t *child_sa;
186 child_cfg_t *child_cfg;
187 protocol_id_t protocol;
188 u_int32_t spi;
189 status_t status = SUCCESS;
190
191 iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
192 while (iterator->iterate(iterator, (void**)&child_sa))
193 {
194 spi = child_sa->get_spi(child_sa, TRUE);
195 protocol = child_sa->get_protocol(child_sa);
196 child_cfg = child_sa->get_config(child_sa);
197 child_cfg->get_ref(child_cfg);
198 this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
199 if (this->check_delete_action)
200 { /* enforce child_cfg policy if deleted passively */
201 switch (child_cfg->get_close_action(child_cfg))
202 {
203 case ACTION_RESTART:
204 child_cfg->get_ref(child_cfg);
205 status = this->ike_sa->initiate(this->ike_sa, child_cfg);
206 break;
207 case ACTION_ROUTE:
208 status = this->ike_sa->route(this->ike_sa, child_cfg);
209 break;
210 default:
211 break;
212 }
213 }
214 child_cfg->destroy(child_cfg);
215 if (status != SUCCESS)
216 {
217 break;
218 }
219 }
220 iterator->destroy(iterator);
221 return status;
222 }
223
224 /**
225 * send closing signals for all CHILD_SAs over the bus
226 */
227 static void log_children(private_child_delete_t *this)
228 {
229 iterator_t *iterator;
230 child_sa_t *child_sa;
231
232 iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
233 while (iterator->iterate(iterator, (void**)&child_sa))
234 {
235 DBG0(DBG_IKE, "closing CHILD_SA %s{%d} "
236 "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
237 child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
238 ntohl(child_sa->get_spi(child_sa, TRUE)),
239 ntohl(child_sa->get_spi(child_sa, FALSE)),
240 child_sa->get_traffic_selectors(child_sa, TRUE),
241 child_sa->get_traffic_selectors(child_sa, FALSE));
242 }
243 iterator->destroy(iterator);
244 }
245
246 /**
247 * Implementation of task_t.build for initiator
248 */
249 static status_t build_i(private_child_delete_t *this, message_t *message)
250 {
251 child_sa_t *child_sa;
252
253 child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
254 this->spi, TRUE);
255 if (!child_sa)
256 { /* child does not exist anymore */
257 return SUCCESS;
258 }
259 this->child_sas->insert_last(this->child_sas, child_sa);
260
261 log_children(this);
262 build_payloads(this, message);
263 return NEED_MORE;
264 }
265
266 /**
267 * Implementation of task_t.process for initiator
268 */
269 static status_t process_i(private_child_delete_t *this, message_t *message)
270 {
271 /* flush the list before adding new SAs */
272 this->child_sas->destroy(this->child_sas);
273 this->child_sas = linked_list_create();
274
275 process_payloads(this, message);
276 DBG1(DBG_IKE, "CHILD_SA closed");
277 return destroy_and_reestablish(this);
278 }
279
280 /**
281 * Implementation of task_t.process for initiator
282 */
283 static status_t process_r(private_child_delete_t *this, message_t *message)
284 {
285 process_payloads(this, message);
286 log_children(this);
287 return NEED_MORE;
288 }
289
290 /**
291 * Implementation of task_t.build for responder
292 */
293 static status_t build_r(private_child_delete_t *this, message_t *message)
294 {
295 /* if we are rekeying, we send an empty informational */
296 if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING)
297 {
298 build_payloads(this, message);
299 }
300 DBG1(DBG_IKE, "CHILD_SA closed");
301 return destroy_and_reestablish(this);
302 }
303
304 /**
305 * Implementation of task_t.get_type
306 */
307 static task_type_t get_type(private_child_delete_t *this)
308 {
309 return CHILD_DELETE;
310 }
311
312 /**
313 * Implementation of child_delete_t.get_child
314 */
315 static child_sa_t* get_child(private_child_delete_t *this)
316 {
317 child_sa_t *child_sa = NULL;
318 this->child_sas->get_first(this->child_sas, (void**)&child_sa);
319 return child_sa;
320 }
321
322 /**
323 * Implementation of task_t.migrate
324 */
325 static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa)
326 {
327 this->check_delete_action = FALSE;
328 this->ike_sa = ike_sa;
329
330 this->child_sas->destroy(this->child_sas);
331 this->child_sas = linked_list_create();
332 }
333
334 /**
335 * Implementation of task_t.destroy
336 */
337 static void destroy(private_child_delete_t *this)
338 {
339 this->child_sas->destroy(this->child_sas);
340 free(this);
341 }
342
343 /*
344 * Described in header.
345 */
346 child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
347 u_int32_t spi)
348 {
349 private_child_delete_t *this = malloc_thing(private_child_delete_t);
350
351 this->public.get_child = (child_sa_t*(*)(child_delete_t*))get_child;
352 this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
353 this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
354 this->public.task.destroy = (void(*)(task_t*))destroy;
355
356 this->ike_sa = ike_sa;
357 this->check_delete_action = FALSE;
358 this->child_sas = linked_list_create();
359 this->protocol = protocol;
360 this->spi = spi;
361
362 if (protocol != PROTO_NONE)
363 {
364 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
365 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
366 this->initiator = TRUE;
367 }
368 else
369 {
370 this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
371 this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
372 this->initiator = FALSE;
373 }
374 return &this->public;
375 }