using dpd actions to enforce connection state
[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 * CHILD_SAs which get deleted
48 */
49 linked_list_t *child_sas;
50 };
51
52 /**
53 * build the delete payloads from the listed child_sas
54 */
55 static void build_payloads(private_child_delete_t *this, message_t *message)
56 {
57 iterator_t *iterator;
58 delete_payload_t *ah = NULL, *esp = NULL;
59 u_int32_t spi;
60 child_sa_t *child_sa;
61
62 iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
63 while (iterator->iterate(iterator, (void**)&child_sa))
64 {
65 spi = child_sa->get_spi(child_sa, TRUE);
66 switch (child_sa->get_protocol(child_sa))
67 {
68 case PROTO_ESP:
69 if (esp == NULL)
70 {
71 esp = delete_payload_create(PROTO_ESP);
72 message->add_payload(message, (payload_t*)esp);
73 }
74 esp->add_spi(esp, spi);
75 break;
76 case PROTO_AH:
77 if (ah == NULL)
78 {
79 ah = delete_payload_create(PROTO_AH);
80 message->add_payload(message, (payload_t*)ah);
81 }
82 ah->add_spi(ah, spi);
83 break;
84 default:
85 break;
86 }
87 child_sa->set_state(child_sa, CHILD_DELETING);
88 }
89 iterator->destroy(iterator);
90 }
91
92 /**
93 * read in payloads and find the children to delete
94 */
95 static void process_payloads(private_child_delete_t *this, message_t *message)
96 {
97 iterator_t *payloads, *spis;
98 payload_t *payload;
99 delete_payload_t *delete_payload;
100 u_int32_t *spi;
101 protocol_id_t protocol;
102 child_sa_t *child_sa;
103
104 payloads = message->get_payload_iterator(message);
105 while (payloads->iterate(payloads, (void**)&payload))
106 {
107 if (payload->get_type(payload) == DELETE)
108 {
109 delete_payload = (delete_payload_t*)payload;
110 protocol = delete_payload->get_protocol_id(delete_payload);
111 if (protocol != PROTO_ESP && protocol != PROTO_AH)
112 {
113 continue;
114 }
115 spis = delete_payload->create_spi_iterator(delete_payload);
116 while (spis->iterate(spis, (void**)&spi))
117 {
118 child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
119 *spi, FALSE);
120 if (child_sa == NULL)
121 {
122 DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, "
123 "but no such SA", protocol_id_names, protocol, ntohl(*spi));
124 continue;
125 }
126 DBG2(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x",
127 protocol_id_names, protocol, ntohl(*spi));
128
129 switch (child_sa->get_state(child_sa))
130 {
131 case CHILD_REKEYING:
132 /* we reply as usual, rekeying will fail */
133 break;
134 case CHILD_DELETING:
135 /* we don't send back a delete if we initiated ourself */
136 if (!this->initiator)
137 {
138 this->ike_sa->destroy_child_sa(this->ike_sa,
139 protocol, *spi);
140 continue;
141 }
142 default:
143 break;
144 }
145
146 this->child_sas->insert_last(this->child_sas, child_sa);
147 }
148 spis->destroy(spis);
149 }
150 }
151 payloads->destroy(payloads);
152 }
153
154 /**
155 * destroy the children listed in this->child_sas, reestablish by policy
156 */
157 static status_t destroy_and_reestablish(private_child_delete_t *this)
158 {
159 iterator_t *iterator;
160 child_sa_t *child_sa;
161 child_cfg_t *child_cfg;
162 protocol_id_t protocol;
163 u_int32_t spi;
164 status_t status = SUCCESS;
165
166 iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
167 while (iterator->iterate(iterator, (void**)&child_sa))
168 {
169 spi = child_sa->get_spi(child_sa, TRUE);
170 protocol = child_sa->get_protocol(child_sa);
171 child_cfg = child_sa->get_config(child_sa);
172 child_cfg->get_ref(child_cfg);
173 this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
174 if (!this->initiator)
175 { /* enforce child_cfg policy if deleted passively */
176 switch (child_cfg->get_action(child_cfg))
177 {
178 case ACTION_RESTART:
179 child_cfg->get_ref(child_cfg);
180 status = this->ike_sa->initiate(this->ike_sa, child_cfg);
181 break;
182 case ACTION_ROUTE:
183 status = this->ike_sa->route(this->ike_sa, child_cfg);
184 break;
185 default:
186 break;
187 }
188 }
189 child_cfg->destroy(child_cfg);
190 if (status != SUCCESS)
191 {
192 break;
193 }
194 }
195 iterator->destroy(iterator);
196 return status;
197 }
198
199 /**
200 * send closing signals for all CHILD_SAs over the bus
201 */
202 static void log_children(private_child_delete_t *this)
203 {
204 iterator_t *iterator;
205 child_sa_t *child_sa;
206
207 iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
208 while (iterator->iterate(iterator, (void**)&child_sa))
209 {
210 SIG(CHILD_DOWN_START, "closing CHILD_SA %#R=== %#R",
211 child_sa->get_traffic_selectors(child_sa, TRUE),
212 child_sa->get_traffic_selectors(child_sa, FALSE));
213 }
214 iterator->destroy(iterator);
215 }
216
217 /**
218 * Implementation of task_t.build for initiator
219 */
220 static status_t build_i(private_child_delete_t *this, message_t *message)
221 {
222 log_children(this);
223 build_payloads(this, message);
224 return NEED_MORE;
225 }
226
227 /**
228 * Implementation of task_t.process for initiator
229 */
230 static status_t process_i(private_child_delete_t *this, message_t *message)
231 {
232 /* flush the list before adding new SAs */
233 this->child_sas->destroy(this->child_sas);
234 this->child_sas = linked_list_create();
235
236 process_payloads(this, message);
237 SIG(CHILD_DOWN_SUCCESS, "CHILD_SA closed");
238 return destroy_and_reestablish(this);
239 }
240
241 /**
242 * Implementation of task_t.process for initiator
243 */
244 static status_t process_r(private_child_delete_t *this, message_t *message)
245 {
246 process_payloads(this, message);
247 log_children(this);
248 return NEED_MORE;
249 }
250
251 /**
252 * Implementation of task_t.build for responder
253 */
254 static status_t build_r(private_child_delete_t *this, message_t *message)
255 {
256 /* if we are rekeying, we send an empty informational */
257 if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING)
258 {
259 build_payloads(this, message);
260 }
261 SIG(CHILD_DOWN_SUCCESS, "CHILD_SA closed");
262 return destroy_and_reestablish(this);
263 }
264
265 /**
266 * Implementation of task_t.get_type
267 */
268 static task_type_t get_type(private_child_delete_t *this)
269 {
270 return CHILD_DELETE;
271 }
272
273 /**
274 * Implementation of child_delete_t.get_child
275 */
276 static child_sa_t* get_child(private_child_delete_t *this)
277 {
278 child_sa_t *child_sa = NULL;
279 this->child_sas->get_first(this->child_sas, (void**)&child_sa);
280 return child_sa;
281 }
282
283 /**
284 * Implementation of task_t.migrate
285 */
286 static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa)
287 {
288 this->ike_sa = ike_sa;
289
290 this->child_sas->destroy(this->child_sas);
291 this->child_sas = linked_list_create();
292 }
293
294 /**
295 * Implementation of task_t.destroy
296 */
297 static void destroy(private_child_delete_t *this)
298 {
299 this->child_sas->destroy(this->child_sas);
300 free(this);
301 }
302
303 /*
304 * Described in header.
305 */
306 child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
307 {
308 private_child_delete_t *this = malloc_thing(private_child_delete_t);
309
310 this->public.get_child = (child_sa_t*(*)(child_delete_t*))get_child;
311 this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
312 this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
313 this->public.task.destroy = (void(*)(task_t*))destroy;
314
315 this->ike_sa = ike_sa;
316 this->child_sas = linked_list_create();
317
318 if (child_sa != NULL)
319 {
320 this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
321 this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
322 this->initiator = TRUE;
323 this->child_sas->insert_last(this->child_sas, child_sa);
324 }
325 else
326 {
327 this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
328 this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
329 this->initiator = FALSE;
330 }
331 return &this->public;
332 }