4 * @brief Implementation of the child_rekey task.
9 * Copyright (C) 2005-2007 Martin Willi
10 * Copyright (C) 2005 Jan Hutter
11 * Hochschule fuer Technik Rapperswil
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 #include "child_rekey.h"
27 #include <encoding/payloads/notify_payload.h>
28 #include <sa/tasks/child_create.h>
29 #include <sa/tasks/child_delete.h>
30 #include <processing/jobs/rekey_child_sa_job.h>
33 typedef struct private_child_rekey_t private_child_rekey_t
;
36 * Private members of a child_rekey_t task.
38 struct private_child_rekey_t
{
41 * Public methods and task_t interface.
51 * Are we the initiator?
56 * the CHILD_CREATE task which is reused to simplify rekeying
58 child_create_t
*child_create
;
61 * CHILD_SA which gets rekeyed
66 * colliding task, may be delete or rekey
72 * find a child using the REKEY_SA notify
74 static void find_child(private_child_rekey_t
*this, message_t
*message
)
79 iterator
= message
->get_payload_iterator(message
);
80 while (iterator
->iterate(iterator
, (void**)&payload
))
82 notify_payload_t
*notify
;
84 protocol_id_t protocol
;
86 if (payload
->get_type(payload
) != NOTIFY
)
91 notify
= (notify_payload_t
*)payload
;
92 protocol
= notify
->get_protocol_id(notify
);
93 spi
= notify
->get_spi(notify
);
95 if (protocol
!= PROTO_ESP
&& protocol
!= PROTO_AH
)
99 this->child_sa
= this->ike_sa
->get_child_sa(this->ike_sa
, protocol
,
104 iterator
->destroy(iterator
);
108 * Implementation of task_t.build for initiator
110 static status_t
build_i(private_child_rekey_t
*this, message_t
*message
)
112 notify_payload_t
*notify
;
113 protocol_id_t protocol
;
114 u_int32_t spi
, reqid
;
116 /* we just need the rekey notify ... */
117 protocol
= this->child_sa
->get_protocol(this->child_sa
);
118 spi
= this->child_sa
->get_spi(this->child_sa
, TRUE
);
119 notify
= notify_payload_create_from_protocol_and_type(protocol
, REKEY_SA
);
120 notify
->set_spi(notify
, spi
);
121 message
->add_payload(message
, (payload_t
*)notify
);
123 /* ... our CHILD_CREATE task does the hard work for us. */
124 reqid
= this->child_sa
->get_reqid(this->child_sa
);
125 this->child_create
->use_reqid(this->child_create
, reqid
);
126 this->child_create
->task
.build(&this->child_create
->task
, message
);
128 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
134 * Implementation of task_t.process for initiator
136 static status_t
process_r(private_child_rekey_t
*this, message_t
*message
)
138 /* let the CHILD_CREATE task process the message */
139 this->child_create
->task
.process(&this->child_create
->task
, message
);
141 find_child(this, message
);
147 * Implementation of task_t.build for responder
149 static status_t
build_r(private_child_rekey_t
*this, message_t
*message
)
153 if (this->child_sa
== NULL
||
154 this->child_sa
->get_state(this->child_sa
) == CHILD_DELETING
)
156 DBG1(DBG_IKE
, "unable to rekey, CHILD_SA not found");
157 message
->add_notify(message
, TRUE
, NO_PROPOSAL_CHOSEN
, chunk_empty
);
161 /* let the CHILD_CREATE task build the response */
162 reqid
= this->child_sa
->get_reqid(this->child_sa
);
163 this->child_create
->use_reqid(this->child_create
, reqid
);
164 this->child_create
->task
.build(&this->child_create
->task
, message
);
166 if (message
->get_payload(message
, SECURITY_ASSOCIATION
) == NULL
)
168 /* rekeying failed, reuse old child */
169 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
173 this->child_sa
->set_state(this->child_sa
, CHILD_REKEYING
);
178 * Implementation of task_t.process for initiator
180 static status_t
process_i(private_child_rekey_t
*this, message_t
*message
)
182 protocol_id_t protocol
;
184 child_sa_t
*to_delete
;
186 this->child_create
->task
.process(&this->child_create
->task
, message
);
187 if (message
->get_payload(message
, SECURITY_ASSOCIATION
) == NULL
)
189 /* establishing new child failed, reuse old. but not when we
190 * recieved a delete in the meantime */
191 if (!(this->collision
&&
192 this->collision
->get_type(this->collision
) == CHILD_DELETE
))
195 u_int32_t retry
= charon
->configuration
->get_retry_interval(
196 charon
->configuration
);
197 job
= (job_t
*)rekey_child_sa_job_create(
198 this->child_sa
->get_reqid(this->child_sa
),
199 this->child_sa
->get_protocol(this->child_sa
),
200 this->child_sa
->get_spi(this->child_sa
, TRUE
));
201 DBG1(DBG_IKE
, "CHILD_SA rekeying failed, "
202 "trying again in %d seconds", retry
);
203 this->child_sa
->set_state(this->child_sa
, CHILD_INSTALLED
);
204 charon
->event_queue
->add_relative(charon
->event_queue
, job
, retry
* 1000);
209 to_delete
= this->child_sa
;
211 /* check for rekey collisions */
212 if (this->collision
&&
213 this->collision
->get_type(this->collision
) == CHILD_REKEY
)
215 chunk_t this_nonce
, other_nonce
;
216 private_child_rekey_t
*other
= (private_child_rekey_t
*)this->collision
;
218 this_nonce
= this->child_create
->get_lower_nonce(this->child_create
);
219 other_nonce
= other
->child_create
->get_lower_nonce(other
->child_create
);
221 /* if we have the lower nonce, delete rekeyed SA. If not, delete
223 if (memcmp(this_nonce
.ptr
, other_nonce
.ptr
,
224 min(this_nonce
.len
, other_nonce
.len
)) < 0)
226 DBG1(DBG_IKE
, "CHILD_SA rekey collision won, deleting rekeyed child");
230 DBG1(DBG_IKE
, "CHILD_SA rekey collision lost, deleting redundant child");
231 to_delete
= this->child_create
->get_child(this->child_create
);
232 if (to_delete
== NULL
)
234 /* ooops, should not happen, fallback */
235 to_delete
= this->child_sa
;
240 spi
= to_delete
->get_spi(to_delete
, TRUE
);
241 protocol
= to_delete
->get_protocol(to_delete
);
242 if (this->ike_sa
->delete_child_sa(this->ike_sa
, protocol
, spi
) != SUCCESS
)
250 * Implementation of task_t.get_type
252 static task_type_t
get_type(private_child_rekey_t
*this)
258 * Implementation of child_rekey_t.collide
260 static void collide(private_child_rekey_t
*this, task_t
*other
)
262 /* the task manager only detects exchange collision, but not if
263 * the collision is for the same child. we check it here. */
264 if (other
->get_type(other
) == CHILD_REKEY
)
266 private_child_rekey_t
*rekey
= (private_child_rekey_t
*)other
;
267 if (rekey
== NULL
|| rekey
->child_sa
!= this->child_sa
)
269 /* not the same child => no collision */
273 else if (other
->get_type(other
) == CHILD_DELETE
)
275 child_delete_t
*del
= (child_delete_t
*)other
;
276 if (del
== NULL
|| del
->get_child(del
) != this->child_sa
)
278 /* not the same child => no collision */
284 /* any other task is not critical for collisisions, ignore */
287 DESTROY_IF(this->collision
);
288 this->collision
= other
;
292 * Implementation of task_t.migrate
294 static void migrate(private_child_rekey_t
*this, ike_sa_t
*ike_sa
)
296 this->child_create
->task
.migrate(&this->child_create
->task
, ike_sa
);
297 DESTROY_IF(this->collision
);
299 this->ike_sa
= ike_sa
;
300 this->collision
= NULL
;
304 * Implementation of task_t.destroy
306 static void destroy(private_child_rekey_t
*this)
308 this->child_create
->task
.destroy(&this->child_create
->task
);
309 DESTROY_IF(this->collision
);
314 * Described in header.
316 child_rekey_t
*child_rekey_create(ike_sa_t
*ike_sa
, child_sa_t
*child_sa
)
319 private_child_rekey_t
*this = malloc_thing(private_child_rekey_t
);
321 this->public.collide
= (void (*)(child_rekey_t
*,task_t
*))collide
;
322 this->public.task
.get_type
= (task_type_t(*)(task_t
*))get_type
;
323 this->public.task
.migrate
= (void(*)(task_t
*,ike_sa_t
*))migrate
;
324 this->public.task
.destroy
= (void(*)(task_t
*))destroy
;
325 if (child_sa
!= NULL
)
327 this->public.task
.build
= (status_t(*)(task_t
*,message_t
*))build_i
;
328 this->public.task
.process
= (status_t(*)(task_t
*,message_t
*))process_i
;
329 this->initiator
= TRUE
;
330 config
= child_sa
->get_config(child_sa
);
331 this->child_create
= child_create_create(ike_sa
, config
);
335 this->public.task
.build
= (status_t(*)(task_t
*,message_t
*))build_r
;
336 this->public.task
.process
= (status_t(*)(task_t
*,message_t
*))process_r
;
337 this->initiator
= FALSE
;
338 this->child_create
= child_create_create(ike_sa
, NULL
);
341 this->ike_sa
= ike_sa
;
342 this->child_sa
= child_sa
;
343 this->collision
= NULL
;
345 return &this->public;