]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libcharon/sa/task_manager_v1.c
Print message ID as unsigned integer
[thirdparty/strongswan.git] / src / libcharon / sa / task_manager_v1.c
CommitLineData
4a09d9ee
MW
1/*
2 * Copyright (C) 2007 Tobias Brunner
3 * Copyright (C) 2007-2010 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17#include "task_manager_v1.h"
18
4a09d9ee 19#include <daemon.h>
c73c832c 20#include <sa/tasks/main_mode.h>
4a09d9ee
MW
21
22typedef struct exchange_t exchange_t;
23
24/**
25 * An exchange in the air, used do detect and handle retransmission
26 */
27struct exchange_t {
28
29 /**
30 * Message ID used for this transaction
31 */
32 u_int32_t mid;
33
34 /**
35 * generated packet for retransmission
36 */
37 packet_t *packet;
38};
39
40typedef struct private_task_manager_t private_task_manager_t;
41
42/**
43 * private data of the task manager
44 */
45struct private_task_manager_t {
46
47 /**
48 * public functions
49 */
50 task_manager_v1_t public;
51
52 /**
53 * associated IKE_SA we are serving
54 */
55 ike_sa_t *ike_sa;
56
73aaf76b
MW
57 /**
58 * RNG to create message IDs
59 */
60 rng_t *rng;
61
4a09d9ee
MW
62 /**
63 * Exchange we are currently handling as responder
64 */
65 struct {
66 /**
67 * Message ID of the exchange
68 */
69 u_int32_t mid;
70
71 /**
72 * packet for retransmission
73 */
74 packet_t *packet;
75
76 } responding;
77
78 /**
79 * Exchange we are currently handling as initiator
80 */
81 struct {
82 /**
83 * Message ID of the exchange
84 */
85 u_int32_t mid;
86
87 /**
88 * how many times we have retransmitted so far
89 */
90 u_int retransmitted;
91
92 /**
93 * packet for retransmission
94 */
95 packet_t *packet;
96
97 /**
98 * type of the initated exchange
99 */
100 exchange_type_t type;
101
102 } initiating;
103
104 /**
105 * List of queued tasks not yet in action
106 */
107 linked_list_t *queued_tasks;
108
109 /**
110 * List of active tasks, initiated by ourselve
111 */
112 linked_list_t *active_tasks;
113
114 /**
115 * List of tasks initiated by peer
116 */
117 linked_list_t *passive_tasks;
118
4a09d9ee
MW
119 /**
120 * Number of times we retransmit messages before giving up
121 */
122 u_int retransmit_tries;
123
124 /**
125 * Retransmission timeout
126 */
127 double retransmit_timeout;
128
129 /**
130 * Base to calculate retransmission timeout
131 */
132 double retransmit_base;
133};
134
135/**
136 * flush all tasks in the task manager
137 */
138static void flush(private_task_manager_t *this)
139{
140 this->queued_tasks->destroy_offset(this->queued_tasks,
141 offsetof(task_t, destroy));
142 this->queued_tasks = linked_list_create();
143 this->passive_tasks->destroy_offset(this->passive_tasks,
144 offsetof(task_t, destroy));
145 this->passive_tasks = linked_list_create();
146 this->active_tasks->destroy_offset(this->active_tasks,
147 offsetof(task_t, destroy));
148 this->active_tasks = linked_list_create();
149}
150
151METHOD(task_manager_t, retransmit, status_t,
152 private_task_manager_t *this, u_int32_t message_id)
153{
154 return FAILED;
155}
156
26b55dc6
MW
157/**
158 * move a task of a specific type from the queue to the active list
159 */
160static bool activate_task(private_task_manager_t *this, task_type_t type)
161{
162 enumerator_t *enumerator;
163 task_t *task;
164 bool found = FALSE;
165
166 enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
167 while (enumerator->enumerate(enumerator, (void**)&task))
168 {
169 if (task->get_type(task) == type)
170 {
171 DBG2(DBG_IKE, " activating %N task", task_type_names, type);
172 this->queued_tasks->remove_at(this->queued_tasks, enumerator);
173 this->active_tasks->insert_last(this->active_tasks, task);
174 found = TRUE;
175 break;
176 }
177 }
178 enumerator->destroy(enumerator);
179 return found;
180}
181
4a09d9ee
MW
182METHOD(task_manager_t, initiate, status_t,
183 private_task_manager_t *this)
184{
26b55dc6
MW
185 enumerator_t *enumerator;
186 task_t *task;
187 message_t *message;
188 host_t *me, *other;
189 status_t status;
73aaf76b
MW
190 exchange_type_t exchange = EXCHANGE_TYPE_UNDEFINED;
191
192 if (!this->rng)
193 {
194 DBG1(DBG_IKE, "no RNG supported");
195 return FAILED;
196 }
26b55dc6
MW
197
198 if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED)
199 {
200 DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress",
201 exchange_type_names, this->initiating.type);
202 /* do not initiate if we already have a message in the air */
203 return SUCCESS;
204 }
205
206 if (this->active_tasks->get_count(this->active_tasks) == 0)
207 {
208 DBG2(DBG_IKE, "activating new tasks");
209 switch (this->ike_sa->get_state(this->ike_sa))
210 {
211 case IKE_CREATED:
212 if (activate_task(this, MAIN_MODE))
213 {
214 exchange = ID_PROT;
215 }
216 break;
217 default:
218 break;
219 }
220 }
221 else
222 {
223 DBG2(DBG_IKE, "reinitiating already active tasks");
224 enumerator = this->active_tasks->create_enumerator(this->active_tasks);
225 while (enumerator->enumerate(enumerator, (void**)&task))
226 {
227 DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task));
228 switch (task->get_type(task))
229 {
230 case MAIN_MODE:
231 exchange = ID_PROT;
232 break;
233 default:
234 continue;
235 }
236 break;
237 }
238 enumerator->destroy(enumerator);
239 }
240
73aaf76b 241 if (exchange == EXCHANGE_TYPE_UNDEFINED)
26b55dc6
MW
242 {
243 DBG2(DBG_IKE, "nothing to initiate");
244 /* nothing to do yet... */
245 return SUCCESS;
246 }
247
248 me = this->ike_sa->get_my_host(this->ike_sa);
249 other = this->ike_sa->get_other_host(this->ike_sa);
250
251 message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION);
252 if (exchange != ID_PROT)
253 {
73aaf76b
MW
254 this->rng->get_bytes(this->rng, sizeof(this->initiating.mid),
255 (void*)&this->initiating.mid);
256 message->set_message_id(message, this->initiating.mid);
26b55dc6
MW
257 }
258 message->set_source(message, me->clone(me));
259 message->set_destination(message, other->clone(other));
260 message->set_exchange_type(message, exchange);
261 this->initiating.type = exchange;
262 this->initiating.retransmitted = 0;
263
264 enumerator = this->active_tasks->create_enumerator(this->active_tasks);
265 while (enumerator->enumerate(enumerator, (void*)&task))
266 {
267 switch (task->build(task, message))
268 {
269 case SUCCESS:
270 /* task completed, remove it */
271 this->active_tasks->remove_at(this->active_tasks, enumerator);
272 task->destroy(task);
273 break;
274 case NEED_MORE:
275 /* processed, but task needs another exchange */
276 break;
277 case FAILED:
278 default:
279 if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
280 {
281 charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
282 }
283 /* FALL */
284 case DESTROY_ME:
285 /* critical failure, destroy IKE_SA */
286 enumerator->destroy(enumerator);
287 message->destroy(message);
288 flush(this);
289 return DESTROY_ME;
290 }
291 }
292 enumerator->destroy(enumerator);
293
294 /* update exchange type if a task changed it */
295 this->initiating.type = message->get_exchange_type(message);
296
297 status = this->ike_sa->generate_message(this->ike_sa, message,
298 &this->initiating.packet);
299 if (status != SUCCESS)
300 {
301 /* message generation failed. There is nothing more to do than to
302 * close the SA */
303 message->destroy(message);
304 flush(this);
305 charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
306 return DESTROY_ME;
307 }
308 message->destroy(message);
309
310 charon->sender->send(charon->sender,
311 this->initiating.packet->clone(this->initiating.packet));
312
313 return SUCCESS;
4a09d9ee
MW
314}
315
316/**
317 * handle exchange collisions
318 */
319static bool handle_collisions(private_task_manager_t *this, task_t *task)
320{
321 return FALSE;
322}
323
324/**
325 * build a response depending on the "passive" task list
326 */
327static status_t build_response(private_task_manager_t *this, message_t *request)
328{
329 enumerator_t *enumerator;
330 task_t *task;
331 message_t *message;
332 host_t *me, *other;
333 bool delete = FALSE;
334 status_t status;
335
336 me = request->get_destination(request);
337 other = request->get_source(request);
338
339 message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION);
340 message->set_exchange_type(message, request->get_exchange_type(request));
341 /* send response along the path the request came in */
342 message->set_source(message, me->clone(me));
343 message->set_destination(message, other->clone(other));
73aaf76b 344 message->set_message_id(message, request->get_message_id(request));
4a09d9ee
MW
345 message->set_request(message, FALSE);
346
347 enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
348 while (enumerator->enumerate(enumerator, (void*)&task))
349 {
350 switch (task->build(task, message))
351 {
352 case SUCCESS:
353 /* task completed, remove it */
354 this->passive_tasks->remove_at(this->passive_tasks, enumerator);
355 if (!handle_collisions(this, task))
356 {
357 task->destroy(task);
358 }
359 break;
360 case NEED_MORE:
361 /* processed, but task needs another exchange */
362 if (handle_collisions(this, task))
363 {
364 this->passive_tasks->remove_at(this->passive_tasks,
365 enumerator);
366 }
367 break;
368 case FAILED:
369 default:
370 charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
371 /* FALL */
372 case DESTROY_ME:
373 /* destroy IKE_SA, but SEND response first */
374 delete = TRUE;
375 break;
376 }
377 if (delete)
378 {
379 break;
380 }
381 }
382 enumerator->destroy(enumerator);
383
384 /* message complete, send it */
385 DESTROY_IF(this->responding.packet);
386 this->responding.packet = NULL;
387 status = this->ike_sa->generate_message(this->ike_sa, message,
388 &this->responding.packet);
389 message->destroy(message);
390 if (status != SUCCESS)
391 {
392 charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
393 return DESTROY_ME;
394 }
395
396 charon->sender->send(charon->sender,
397 this->responding.packet->clone(this->responding.packet));
398 if (delete)
399 {
400 return DESTROY_ME;
401 }
402 return SUCCESS;
403}
404
405/**
406 * handle an incoming request message
407 */
408static status_t process_request(private_task_manager_t *this,
409 message_t *message)
410{
411 enumerator_t *enumerator;
412 task_t *task = NULL;
413
414 if (this->passive_tasks->get_count(this->passive_tasks) == 0)
415 { /* create tasks depending on request type, if not already some queued */
416 switch (message->get_exchange_type(message))
417 {
418 case ID_PROT:
c73c832c
MW
419 task = (task_t *)main_mode_create(this->ike_sa, FALSE);
420 this->passive_tasks->insert_last(this->passive_tasks, task);
4a09d9ee
MW
421 break;
422 case AGGRESSIVE:
423 /* TODO-IKEv1: agressive mode */
424 return FAILED;
425 case QUICK_MODE:
426 /* TODO-IKEv1: quick mode */
427 return FAILED;
428 case INFORMATIONAL_V1:
429 /* TODO-IKEv1: informational */
430 return FAILED;
431 default:
432 return FAILED;
433 }
434 }
435 /* let the tasks process the message */
436 enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
437 while (enumerator->enumerate(enumerator, (void*)&task))
438 {
439 switch (task->process(task, message))
440 {
441 case SUCCESS:
442 /* task completed, remove it */
443 this->passive_tasks->remove_at(this->passive_tasks, enumerator);
444 task->destroy(task);
445 break;
446 case NEED_MORE:
447 /* processed, but task needs at least another call to build() */
448 break;
449 case FAILED:
450 default:
451 charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
452 /* FALL */
453 case DESTROY_ME:
454 /* critical failure, destroy IKE_SA */
455 this->passive_tasks->remove_at(this->passive_tasks, enumerator);
456 enumerator->destroy(enumerator);
457 task->destroy(task);
458 return DESTROY_ME;
459 }
460 }
461 enumerator->destroy(enumerator);
462
463 return build_response(this, message);
464}
465
26b55dc6
MW
466/**
467 * handle an incoming response message
468 */
469static status_t process_response(private_task_manager_t *this,
470 message_t *message)
471{
472 enumerator_t *enumerator;
473 task_t *task;
474
475 if (message->get_exchange_type(message) != this->initiating.type)
476 {
477 DBG1(DBG_IKE, "received %N response, but expected %N",
478 exchange_type_names, message->get_exchange_type(message),
479 exchange_type_names, this->initiating.type);
480 charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
481 return DESTROY_ME;
482 }
483
484 enumerator = this->active_tasks->create_enumerator(this->active_tasks);
485 while (enumerator->enumerate(enumerator, (void*)&task))
486 {
487 switch (task->process(task, message))
488 {
489 case SUCCESS:
490 /* task completed, remove it */
491 this->active_tasks->remove_at(this->active_tasks, enumerator);
492 task->destroy(task);
493 break;
494 case NEED_MORE:
495 /* processed, but task needs another exchange */
496 break;
497 case FAILED:
498 default:
499 charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
500 /* FALL */
501 case DESTROY_ME:
502 /* critical failure, destroy IKE_SA */
503 this->active_tasks->remove_at(this->active_tasks, enumerator);
504 enumerator->destroy(enumerator);
505 task->destroy(task);
506 return DESTROY_ME;
507 }
508 }
509 enumerator->destroy(enumerator);
510
511 this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
512 this->initiating.packet->destroy(this->initiating.packet);
513 this->initiating.packet = NULL;
514
515 return initiate(this);
516}
517
4a09d9ee
MW
518METHOD(task_manager_t, process_message, status_t,
519 private_task_manager_t *this, message_t *msg)
520{
73aaf76b
MW
521 u_int32_t hash, mid;
522
523 mid = msg->get_message_id(msg);
524 hash = chunk_hash(msg->get_packet_data(msg));
525
526 if ((mid && mid == this->initiating.mid) ||
527 (this->initiating.mid == 0 &&
528 this->active_tasks->get_count(this->active_tasks)))
4a09d9ee 529 {
73aaf76b
MW
530 charon->bus->message(charon->bus, msg, FALSE);
531 if (process_response(this, msg) != SUCCESS)
4a09d9ee
MW
532 {
533 flush(this);
534 return DESTROY_ME;
535 }
536 }
537 else
538 {
73aaf76b
MW
539 if ((mid && mid == this->responding.mid) ||
540 hash == this->responding.mid)
541 {
542 DBG1(DBG_IKE, "received retransmit of request with ID %d, "
543 "retransmitting response", mid);
544 charon->sender->send(charon->sender,
545 this->responding.packet->clone(this->responding.packet));
546 return SUCCESS;
547 }
548
549 charon->bus->message(charon->bus, msg, TRUE);
550 if (process_request(this, msg) != SUCCESS)
26b55dc6
MW
551 {
552 flush(this);
553 return DESTROY_ME;
554 }
73aaf76b
MW
555
556 if (!mid)
557 {
558 mid = hash;
559 }
560 this->responding.mid = mid;
4a09d9ee
MW
561 }
562 return SUCCESS;
563}
564
565METHOD(task_manager_t, queue_task, void,
566 private_task_manager_t *this, task_t *task)
567{
568 DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
569 this->queued_tasks->insert_last(this->queued_tasks, task);
570}
571
572METHOD(task_manager_t, adopt_tasks, void,
573 private_task_manager_t *this, task_manager_t *other_public)
574{
575 private_task_manager_t *other = (private_task_manager_t*)other_public;
576 task_t *task;
577
578 /* move queued tasks from other to this */
579 while (other->queued_tasks->remove_last(other->queued_tasks,
580 (void**)&task) == SUCCESS)
581 {
582 DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
583 task->migrate(task, this->ike_sa);
584 this->queued_tasks->insert_first(this->queued_tasks, task);
585 }
586}
587
588METHOD(task_manager_t, busy, bool,
589 private_task_manager_t *this)
590{
591 return (this->active_tasks->get_count(this->active_tasks) > 0);
592}
593
594METHOD(task_manager_t, incr_mid, void,
595 private_task_manager_t *this, bool initiate)
596{
4a09d9ee
MW
597}
598
599METHOD(task_manager_t, reset, void,
600 private_task_manager_t *this, u_int32_t initiate, u_int32_t respond)
601{
4a09d9ee
MW
602}
603
604METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
605 private_task_manager_t *this, task_queue_t queue)
606{
607 switch (queue)
608 {
609 case TASK_QUEUE_ACTIVE:
610 return this->active_tasks->create_enumerator(this->active_tasks);
611 case TASK_QUEUE_PASSIVE:
612 return this->passive_tasks->create_enumerator(this->passive_tasks);
613 case TASK_QUEUE_QUEUED:
614 return this->queued_tasks->create_enumerator(this->queued_tasks);
615 default:
616 return enumerator_create_empty();
617 }
618}
619
620METHOD(task_manager_t, destroy, void,
621 private_task_manager_t *this)
622{
623 flush(this);
624
625 this->active_tasks->destroy(this->active_tasks);
626 this->queued_tasks->destroy(this->queued_tasks);
627 this->passive_tasks->destroy(this->passive_tasks);
628
629 DESTROY_IF(this->responding.packet);
630 DESTROY_IF(this->initiating.packet);
73aaf76b 631 DESTROY_IF(this->rng);
4a09d9ee
MW
632 free(this);
633}
634
635/*
636 * see header file
637 */
638task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
639{
640 private_task_manager_t *this;
641
642 INIT(this,
643 .public = {
644 .task_manager = {
645 .process_message = _process_message,
646 .queue_task = _queue_task,
647 .initiate = _initiate,
648 .retransmit = _retransmit,
649 .incr_mid = _incr_mid,
650 .reset = _reset,
651 .adopt_tasks = _adopt_tasks,
652 .busy = _busy,
653 .create_task_enumerator = _create_task_enumerator,
654 .destroy = _destroy,
655 },
656 },
657 .ike_sa = ike_sa,
658 .initiating.type = EXCHANGE_TYPE_UNDEFINED,
73aaf76b 659 .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
4a09d9ee
MW
660 .queued_tasks = linked_list_create(),
661 .active_tasks = linked_list_create(),
662 .passive_tasks = linked_list_create(),
663 .retransmit_tries = lib->settings->get_int(lib->settings,
664 "charon.retransmit_tries", RETRANSMIT_TRIES),
665 .retransmit_timeout = lib->settings->get_double(lib->settings,
666 "charon.retransmit_timeout", RETRANSMIT_TIMEOUT),
667 .retransmit_base = lib->settings->get_double(lib->settings,
668 "charon.retransmit_base", RETRANSMIT_BASE),
669 );
670
671 return &this->public;
672}